Compare commits
1 Commits
fix/variab
...
1c418dded0
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c418dded0 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -69,5 +69,3 @@ dkms.conf
|
|||||||
|
|
||||||
# debug information files
|
# debug information files
|
||||||
*.dwo
|
*.dwo
|
||||||
|
|
||||||
minishell-codex/
|
|
||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
|||||||
[submodule "tests/42_minishell_tester"]
|
|
||||||
path = tests/42_minishell_tester
|
|
||||||
url = https://github.com/zstenger93/42_minishell_tester.git
|
|
||||||
37
AGENTS.md
37
AGENTS.md
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
## 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`.
|
|
||||||
- `src/executor/` includes pipeline/process orchestration plus file redirections in `redirections.c`.
|
|
||||||
- `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`).
|
||||||
@@ -21,46 +19,11 @@
|
|||||||
- 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.
|
|
||||||
|
|
||||||
## Parser & Lexer Functionality (Current `src/parser`)
|
|
||||||
- Runtime entrypoint is `parse(line, minishell)` from `src/minishell.c` (`readline -> parse -> execute`).
|
|
||||||
- `parse` calls `lex(line)` and then converts token lists to `t_command` nodes with `parse_tokens`.
|
|
||||||
- `command_new` builds one command from tokens up to `TOKEN_PIPE`.
|
|
||||||
- `words_add` stores consecutive `TOKEN_WORD` tokens in `command->argv` and increments `argc`.
|
|
||||||
- `expand_envs` is currently a TODO (no `$VAR` expansion is applied in parser stage).
|
|
||||||
- Redirection tokens are converted into `t_redirection` and stored in `t_command.redirections`; heredocs are stored in `t_command.heredocs`.
|
|
||||||
- Path resolution is handled in executor (`executor_resolve_command_path`) before `execve`.
|
|
||||||
- `src/parser/lexer.c` provides a separate lexer (`lex`) that tokenizes into `TOKEN_WORD`, `TOKEN_PIPE`, `TOKEN_REDIRECT_IN`, `TOKEN_REDIRECT_OUT`, `TOKEN_APPEND`, and `TOKEN_HEREDOC`.
|
|
||||||
- The lexer tracks single/double quote context so metacharacters inside quotes remain part of words.
|
|
||||||
- Meta runs are read as contiguous chunks in `read_token` (for example, repeated `|`/`<`/`>` are captured as one token value).
|
|
||||||
- Current parser flow consumes lexer output directly.
|
|
||||||
|
|
||||||
## Executor Redirections (Current `src/executor`)
|
|
||||||
- File redirections are applied in `src/executor/redirections.c` via `open` + `dup2` before command execution.
|
|
||||||
- Supported file redirections: input `<`, output truncate `>`, output append `>>`.
|
|
||||||
- Redirections are applied for both forked commands (child path) and single builtins executed in parent.
|
|
||||||
- Parent-builtin redirections save/restore `STDIN_FILENO` and `STDOUT_FILENO` after builtin execution.
|
|
||||||
|
|
||||||
## Parser & Lexer Known Gaps
|
|
||||||
- Heredoc tokens are parsed and stored, but runtime heredoc execution/input feeding is still pending in executor.
|
|
||||||
- No explicit unmatched-quote syntax error handling is implemented in parser/lexer path.
|
|
||||||
|
|
||||||
## 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: marcnava <marcnava@student.42madrid.com +#+ +:+ +#+ #
|
# By: sede-san <sede-san@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/09 22:44:34 by marcnava ### ########.fr #
|
# Updated: 2026/02/05 21:06:52 by sede-san ### ########.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
177
allowed.txt
@@ -1,177 +0,0 @@
|
|||||||
[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
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
# 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.
|
|
||||||
Binary file not shown.
@@ -1,56 +0,0 @@
|
|||||||
# Errores detectados: parser, expansión y ejecutor
|
|
||||||
|
|
||||||
## Críticos
|
|
||||||
|
|
||||||
1. Segmentation fault al mezclar palabras y redirecciones en un mismo comando.
|
|
||||||
- Síntoma: `echo a > /tmp/x b` puede crashear.
|
|
||||||
- Referencias: `src/parser/parser.c:365`, `src/parser/parser.c:371`, `src/parser/parser.c:187`.
|
|
||||||
|
|
||||||
2. Bucle infinito al recibir EOF en modo no interactivo.
|
|
||||||
- Síntoma: con `printf 'echo ok\n' | ./minishell` entra en loop imprimiendo prompt.
|
|
||||||
- Referencias: `src/minishell.c:40`, `src/minishell.c:42`.
|
|
||||||
|
|
||||||
3. Parseo de pipes inválidos permite ejecución parcial o comandos inválidos.
|
|
||||||
- Síntoma: casos como `| echo x` o `echo hi |` no se bloquean correctamente.
|
|
||||||
- Referencias: `src/parser/parser.c:83`, `src/parser/parser.c:101`, `src/parser/parser.c:221`.
|
|
||||||
|
|
||||||
## Altos
|
|
||||||
|
|
||||||
4. Expansión de variables incompleta e incorrecta en casos especiales.
|
|
||||||
- Síntoma: `$FOO_BAR` y `$_X` fallan; `$?` y `$$` no se expanden correctamente.
|
|
||||||
- Referencias: `src/parser/parser.c:122`, `src/parser/parser.c:156`.
|
|
||||||
|
|
||||||
5. No se eliminan comillas tras parseo/expansión.
|
|
||||||
- Síntoma: `echo "$HOME"` imprime comillas literales.
|
|
||||||
- Referencias: `src/parser/lexer.c:161`, `src/parser/parser.c:142`.
|
|
||||||
|
|
||||||
6. No hay expansión en targets de redirección.
|
|
||||||
- Síntoma: `echo hi > $HOME_test` crea archivo literal `$HOME_test`.
|
|
||||||
- Referencias: `src/parser/parser.c:187`, `src/parser/parser.c:263`.
|
|
||||||
|
|
||||||
7. Heredoc parseado pero no ejecutado en runtime.
|
|
||||||
- Síntoma: se almacena en `heredocs` pero no se aplica en ejecución.
|
|
||||||
- Referencias: `src/parser/parser.c:397`, `src/executor/redirections.c:68`, `src/executor/process_helpers.c:110`.
|
|
||||||
|
|
||||||
8. Lexer acepta operadores inválidos (`||`, `>>>`, `><`) sin validación formal.
|
|
||||||
- Síntoma: tokenización por “runs” de metacaracteres produce entradas inválidas no rechazadas de forma robusta.
|
|
||||||
- Referencias: `src/parser/lexer.c:140`, `src/parser/lexer.c:77`.
|
|
||||||
|
|
||||||
9. Errores de sintaxis no siempre fijan `exit_status` correcto (debería ser `2`).
|
|
||||||
- Síntoma: algunos casos acaban con salida `0`.
|
|
||||||
- Referencias: `src/executor/executor.c:20`, `src/parser/parser.c:101`.
|
|
||||||
|
|
||||||
## Medios
|
|
||||||
|
|
||||||
10. Resolución de rutas explícitas reporta mal algunos errores de permisos/ejecución.
|
|
||||||
- Síntoma: ruta existente no ejecutable puede terminar como `command not found`.
|
|
||||||
- Referencias: `src/executor/path_resolver.c:57`, `src/executor/path_resolver.c:59`, `src/executor/command_exec.c:81`.
|
|
||||||
|
|
||||||
11. Check de puntero nulo defectuoso en el loop principal.
|
|
||||||
- Síntoma: `if (minishell == NULL)` desreferencia igualmente el puntero.
|
|
||||||
- Referencia: `src/minishell.c:35`.
|
|
||||||
|
|
||||||
12. Falta manejo explícito de comillas no cerradas.
|
|
||||||
- Síntoma: entrada con comillas abiertas no genera error sintáctico dedicado.
|
|
||||||
- Referencia: `src/parser/lexer.c:159`.
|
|
||||||
|
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,26 +0,0 @@
|
|||||||
Tests eliminados del reporte por ser extras (\ y/o ~):
|
|
||||||
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:52 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:54 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:56 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:58 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:60 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:62 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:64 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:7 | motivo: ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:59 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:61 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:63 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:65 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:67 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:69 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:71 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:314 | motivo: ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:328 | motivo: ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:337 | motivo: ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:606 | motivo: \, ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_redirs.sh:208 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/2_path_check.sh:30 | motivo: ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/8_syntax_errors.sh:19 | motivo: ~
|
|
||||||
|
|
||||||
Total casos eliminados (extras): 22
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
Tests eliminados del reporte por estar fuera del obligatorio:
|
|
||||||
- \ (barra invertida no especificada)
|
|
||||||
- ~ (tilde expansion no obligatoria)
|
|
||||||
- $"..." (extension bash, no requerida)
|
|
||||||
- ; (separador no obligatorio segun subject)
|
|
||||||
- && / || (bonus)
|
|
||||||
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:26 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:28 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:32 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:34 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:52 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:54 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:56 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:58 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:60 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:62 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/0_compare_parsing.sh:64 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/10_parsing_hell.sh:301 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:7 | motivo: ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:31 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:33 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:37 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:39 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:59 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:61 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:63 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:65 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:67 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:69 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:71 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:314 | motivo: ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:328 | motivo: ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:337 | motivo: ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:536 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:538 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:552 | motivo: ; (no obligatorio)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_builtins.sh:606 | motivo: \, ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_redirs.sh:208 | motivo: \
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_variables.sh:40 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_variables.sh:42 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_variables.sh:44 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_variables.sh:48 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_variables.sh:60 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/1_variables.sh:62 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/2_correction.sh:129 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/2_correction.sh:139 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/2_correction.sh:141 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/2_correction.sh:143 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/2_correction.sh:147 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/2_correction.sh:159 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/2_correction.sh:161 | motivo: $"..." (bash extension)
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/2_path_check.sh:30 | motivo: ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/8_syntax_errors.sh:19 | motivo: ~
|
|
||||||
- /home/marcos/programming/minishell_sergio/tests/42_minishell_tester/cmds/mand/8_syntax_errors.sh:68 | motivo: &&/|| (bonus)
|
|
||||||
|
|
||||||
Total casos evaluados en reporte original: 195
|
|
||||||
Total casos eliminados (extras): 48
|
|
||||||
Total casos restantes (mandatory): 147
|
|
||||||
341
executor_new_previous.c
Normal file
341
executor_new_previous.c
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* executor_new.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/10/28 13:03:44 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2026/02/08 19:05:37 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "executor.h"
|
||||||
|
#include "builtins.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
static void handle_execve_error();
|
||||||
|
static void handle_parent_process(
|
||||||
|
int *prev_read_fd,
|
||||||
|
t_list *current_command,
|
||||||
|
int pipefd[2]
|
||||||
|
);
|
||||||
|
static int handle_pipeline(
|
||||||
|
t_list *current_command,
|
||||||
|
int pipefd[2],
|
||||||
|
int *exit_status
|
||||||
|
);
|
||||||
|
static inline uint8_t execute_builtin(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
);
|
||||||
|
static void execute_external_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
);
|
||||||
|
static pid_t handle_fork(
|
||||||
|
t_list *current_command,
|
||||||
|
t_minishell *minishell
|
||||||
|
);
|
||||||
|
static uint8_t execute_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
);
|
||||||
|
|
||||||
|
static inline bool can_execute_command(t_command *command, t_minishell *minishell);
|
||||||
|
static inline bool command_exists(t_command *command, t_minishell *minishell);
|
||||||
|
static void cmdfree(t_command *command);
|
||||||
|
static void cmdfree_argv(char **argv);
|
||||||
|
|
||||||
|
# define WRITE_PIPE 1
|
||||||
|
# define READ_PIPE 0
|
||||||
|
# define CHILD_PID 0
|
||||||
|
|
||||||
|
u_int8_t execute(
|
||||||
|
t_list *command_list,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
t_list *current_command;
|
||||||
|
int prev_read_fd;
|
||||||
|
int exit_status;
|
||||||
|
int pipefd[2];
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
|
||||||
|
prev_read_fd = -1;
|
||||||
|
exit_status = EXIT_SUCCESS;
|
||||||
|
current_command = command_list;
|
||||||
|
while (current_command != NULL)
|
||||||
|
{
|
||||||
|
if (handle_pipeline(current_command, pipefd, &exit_status) == EXIT_FAILURE)
|
||||||
|
break ;
|
||||||
|
pid = handle_fork(current_command, minishell);
|
||||||
|
if (pid == -1)
|
||||||
|
{
|
||||||
|
perror("fork");
|
||||||
|
exit_status = EXIT_FAILURE;
|
||||||
|
break ;
|
||||||
|
}
|
||||||
|
if (pid == CHILD_PID)
|
||||||
|
handle_child_process(prev_read_fd, current_command, pipefd, minishell);
|
||||||
|
handle_parent_process(&prev_read_fd, current_command, pipefd);
|
||||||
|
current_command = current_command->next;
|
||||||
|
}
|
||||||
|
ft_lstclear(&command_list, (void (*)(void *))cmdfree);
|
||||||
|
while (wait(&exit_status) > 0)
|
||||||
|
{
|
||||||
|
if (WIFEXITED(exit_status))
|
||||||
|
exit_status = WEXITSTATUS(exit_status);
|
||||||
|
}
|
||||||
|
return (exit_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_pipeline(
|
||||||
|
t_list *current_command,
|
||||||
|
int pipefd[2],
|
||||||
|
int *exit_status
|
||||||
|
) {
|
||||||
|
if (current_command->next) // create pipe if needed
|
||||||
|
{
|
||||||
|
if (pipe(pipefd) == -1)
|
||||||
|
{
|
||||||
|
perror("pipe");
|
||||||
|
*exit_status = EXIT_FAILURE;
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_parent_process(
|
||||||
|
int *prev_read_fd,
|
||||||
|
t_list *current_command,
|
||||||
|
int pipefd[2]
|
||||||
|
) {
|
||||||
|
if (*prev_read_fd != -1)
|
||||||
|
close(*prev_read_fd);
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
close(pipefd[WRITE_PIPE]); // parent does not write
|
||||||
|
*prev_read_fd = pipefd[READ_PIPE]; // pass read end forward
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*prev_read_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pid_t handle_fork(
|
||||||
|
t_list *current_command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
pid_t pid;
|
||||||
|
const t_command *command = (t_command *)current_command->content;
|
||||||
|
|
||||||
|
pid = 0;
|
||||||
|
if (current_command->next != NULL
|
||||||
|
|| !is_builtin(command->path, minishell))
|
||||||
|
pid = fork();
|
||||||
|
return (pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_child_process(int prev_read_fd, t_list *current_command, int pipefd[2], t_minishell *minishell)
|
||||||
|
{
|
||||||
|
redirect_pipes(prev_read_fd, current_command, pipefd);
|
||||||
|
execute_command((t_command *)current_command->content, minishell);
|
||||||
|
}
|
||||||
|
|
||||||
|
void redirect_pipes(int prev_read_fd, t_list *current_command, int pipefd[2])
|
||||||
|
{
|
||||||
|
redirect_input_pipe(prev_read_fd);
|
||||||
|
redirect_output_pipe(current_command, pipefd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void redirect_output_pipe(t_list * current_command, int pipefd[2])
|
||||||
|
{
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
dup2(pipefd[WRITE_PIPE], STDOUT_FILENO);
|
||||||
|
close(pipefd[READ_PIPE]);
|
||||||
|
close(pipefd[WRITE_PIPE]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void redirect_input_pipe(int prev_read_fd)
|
||||||
|
{
|
||||||
|
if (prev_read_fd != -1)
|
||||||
|
{
|
||||||
|
dup2(prev_read_fd, STDIN_FILENO);
|
||||||
|
close(prev_read_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool can_execute_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
if (!is_builtin(command->path, minishell)
|
||||||
|
&& access(command->path, X_OK) != EXIT_SUCCESS)
|
||||||
|
return (false);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool command_exists(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
if (!is_builtin(command->path, minishell)
|
||||||
|
&& access(command->path, F_OK) != EXIT_SUCCESS)
|
||||||
|
return (false);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t execute_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
if (is_builtin(command->path, minishell))
|
||||||
|
return (execute_builtin(command, minishell));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
execute_external_command(command, minishell);
|
||||||
|
return (EXIT_FAILURE); //! should never reach here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t execute_builtin(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
const t_builtin_func builtin
|
||||||
|
= ft_hashmap_get(minishell->builtins, command->path);
|
||||||
|
|
||||||
|
return (builtin(*command, minishell));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void execute_external_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
char **envp;
|
||||||
|
|
||||||
|
envp = get_envp(minishell);
|
||||||
|
if (envp == NULL)
|
||||||
|
{
|
||||||
|
perror("get_envp");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
execve(command->path, command->argv, envp);
|
||||||
|
handle_execve_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_execve_error() {
|
||||||
|
perror("execve");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static inline bool is_piped(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// return (command->piped_to != NULL || command->piped_from != NULL);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static u_int8_t handle_child_process(
|
||||||
|
// t_command *command,
|
||||||
|
// t_minishell *minishell
|
||||||
|
// ) {
|
||||||
|
// if (!redirect_pipes(command))
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// execute_command(command, minishell);
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static int redirect_pipe(
|
||||||
|
// int from,
|
||||||
|
// int to
|
||||||
|
// ) {
|
||||||
|
// if (dup2(from, to) == -1)
|
||||||
|
// return (-1);
|
||||||
|
// close(from);
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static int redirect_pipes(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// if (command->piped_from &&
|
||||||
|
// redirect_pipe(command->piped_from->outfile, STDIN_FILENO) == -1)
|
||||||
|
// {
|
||||||
|
// perror("dup2");
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
// if (command->piped_to &&
|
||||||
|
// redirect_pipe(command->outfile, STDOUT_FILENO) == -1)
|
||||||
|
// {
|
||||||
|
// perror("dup2");
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
// return (1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static void show_error(
|
||||||
|
// t_command *command,
|
||||||
|
// t_minishell *minishell
|
||||||
|
// ) {
|
||||||
|
// if (!command_exists(command, minishell))
|
||||||
|
// {
|
||||||
|
// ft_eprintf("minishell: %s: command not found\n", command->argv[0]);
|
||||||
|
// minishell->exit_status = 127;
|
||||||
|
// }
|
||||||
|
// else if (!can_execute_command(command, minishell))
|
||||||
|
// {
|
||||||
|
// ft_eprintf("minishell: %s: %s\n", command->path, strerror(errno));
|
||||||
|
// minishell->exit_status = errno;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
static void cmdfree(
|
||||||
|
t_command *command
|
||||||
|
) {
|
||||||
|
if (command == NULL)
|
||||||
|
return ;
|
||||||
|
cmdfree_argv(command->argv);
|
||||||
|
free(command->path);
|
||||||
|
free(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmdfree_argv(
|
||||||
|
char **argv
|
||||||
|
) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (argv == NULL)
|
||||||
|
return ;
|
||||||
|
i = 0;
|
||||||
|
while (argv[i] != NULL)
|
||||||
|
{
|
||||||
|
free(argv[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
free(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static void debug_print_command_info(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// size_t i;
|
||||||
|
|
||||||
|
// if (command == NULL)
|
||||||
|
// {
|
||||||
|
// printf("Command is NULL\n");
|
||||||
|
// return ;
|
||||||
|
// }
|
||||||
|
// printf("Command info:\n");
|
||||||
|
// printf(" Path: %s\n", command->path);
|
||||||
|
// printf(" Argc: %d\n", command->argc);
|
||||||
|
// printf(" Argv:\n");
|
||||||
|
// for (i = 0; i < (size_t)command->argc; i++)
|
||||||
|
// printf(" arg[%zu]: %s\n", i, command->argv[i]);
|
||||||
|
// printf(" Infile FD: %d\n", command->infile);
|
||||||
|
// printf(" Outfile FD: %d\n", command->outfile);
|
||||||
|
// printf("--------------------------\n");
|
||||||
|
// }
|
||||||
256
executor_old.c
Normal file
256
executor_old.c
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* executor.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/10/28 13:03:44 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2026/02/08 03:42:11 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "executor.h"
|
||||||
|
#include "builtins.h"
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
static inline bool can_execute_command(t_command *command, t_minishell *minishell);
|
||||||
|
static inline bool command_exists(t_command *command, t_minishell *minishell);
|
||||||
|
static u_int8_t execute_command(t_command *command, t_minishell *minishell);
|
||||||
|
static u_int8_t execute_command(t_command *command, t_minishell *minishell);
|
||||||
|
// static inline bool is_piped(t_command *command);
|
||||||
|
static void cmdfree(t_command *command);
|
||||||
|
static void cmdfree_argv(char **argv);
|
||||||
|
|
||||||
|
# define WRITE_PIPE 1
|
||||||
|
# define READ_PIPE 0
|
||||||
|
# define CHILD_PID 0
|
||||||
|
|
||||||
|
u_int8_t execute(
|
||||||
|
t_list *command_list,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
t_list *current_command;
|
||||||
|
int prev_read_fd;
|
||||||
|
pid_t pid;
|
||||||
|
u_int8_t exit_status;
|
||||||
|
int child_exit_status;
|
||||||
|
|
||||||
|
prev_read_fd = -1;
|
||||||
|
exit_status = EXIT_SUCCESS;
|
||||||
|
current_command = command_list;
|
||||||
|
while (current_command != NULL)
|
||||||
|
{
|
||||||
|
t_command *command = (t_command *)current_command->content;
|
||||||
|
int pipefd[2];
|
||||||
|
|
||||||
|
/* Create pipe ONLY if there is a next command */
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
if (pipe(pipefd) == -1)
|
||||||
|
{
|
||||||
|
perror("pipe");
|
||||||
|
exit_status = EXIT_FAILURE;
|
||||||
|
break ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create fork
|
||||||
|
pid = 0;
|
||||||
|
if (current_command->next != NULL
|
||||||
|
|| !is_builtin(command->path, minishell))
|
||||||
|
pid = fork();
|
||||||
|
// handle fork error
|
||||||
|
if (pid == -1)
|
||||||
|
{
|
||||||
|
perror("fork");
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle child process
|
||||||
|
if (pid == CHILD_PID)
|
||||||
|
{
|
||||||
|
/* If we have input from previous pipe */
|
||||||
|
if (prev_read_fd != -1)
|
||||||
|
{
|
||||||
|
dup2(prev_read_fd, STDIN_FILENO);
|
||||||
|
close(prev_read_fd);
|
||||||
|
}
|
||||||
|
/* If we pipe output to next command */
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
dup2(pipefd[WRITE_PIPE], STDOUT_FILENO);
|
||||||
|
close(pipefd[READ_PIPE]);
|
||||||
|
close(pipefd[WRITE_PIPE]);
|
||||||
|
}
|
||||||
|
execute_command(command, minishell); // child process exits here
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle parent process
|
||||||
|
waitpid(pid, &child_exit_status, 0); // wait for child to finish
|
||||||
|
if (prev_read_fd != -1)
|
||||||
|
close(prev_read_fd);
|
||||||
|
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
close(pipefd[WRITE_PIPE]); /* parent does not write */
|
||||||
|
prev_read_fd = pipefd[READ_PIPE]; /* pass read end forward */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
prev_read_fd = -1;
|
||||||
|
|
||||||
|
// continue executing
|
||||||
|
current_command = current_command->next;
|
||||||
|
}
|
||||||
|
ft_lstclear(&command_list, (void (*)(void *))cmdfree);
|
||||||
|
exit_status = child_exit_status;
|
||||||
|
return (exit_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool can_execute_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
if (!is_builtin(command->path, minishell)
|
||||||
|
&& access(command->path, X_OK) != EXIT_SUCCESS)
|
||||||
|
return (false);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool command_exists(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
if (!is_builtin(command->path, minishell)
|
||||||
|
&& access(command->path, F_OK) != EXIT_SUCCESS)
|
||||||
|
return (false);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u_int8_t execute_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
char **envp;
|
||||||
|
t_builtin_func builtin;
|
||||||
|
|
||||||
|
if (is_builtin(command->path, minishell))
|
||||||
|
{
|
||||||
|
builtin = ft_hashmap_get(minishell->builtins, command->path);
|
||||||
|
return (builtin(*command, minishell));
|
||||||
|
}
|
||||||
|
envp = get_envp(minishell);
|
||||||
|
execve(command->path, command->argv, envp);
|
||||||
|
// handle error if execve fails
|
||||||
|
perror("execve");
|
||||||
|
free_envp(envp);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static inline bool is_piped(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// return (command->piped_to != NULL || command->piped_from != NULL);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static u_int8_t handle_child_process(
|
||||||
|
// t_command *command,
|
||||||
|
// t_minishell *minishell
|
||||||
|
// ) {
|
||||||
|
// if (!redirect_pipes(command))
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// execute_command(command, minishell);
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static int redirect_pipe(
|
||||||
|
// int from,
|
||||||
|
// int to
|
||||||
|
// ) {
|
||||||
|
// if (dup2(from, to) == -1)
|
||||||
|
// return (-1);
|
||||||
|
// close(from);
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static int redirect_pipes(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// if (command->piped_from &&
|
||||||
|
// redirect_pipe(command->piped_from->outfile, STDIN_FILENO) == -1)
|
||||||
|
// {
|
||||||
|
// perror("dup2");
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
// if (command->piped_to &&
|
||||||
|
// redirect_pipe(command->outfile, STDOUT_FILENO) == -1)
|
||||||
|
// {
|
||||||
|
// perror("dup2");
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
// return (1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static void show_error(
|
||||||
|
// t_command *command,
|
||||||
|
// t_minishell *minishell
|
||||||
|
// ) {
|
||||||
|
// if (!command_exists(command, minishell))
|
||||||
|
// {
|
||||||
|
// ft_eprintf("minishell: %s: command not found\n", command->argv[0]);
|
||||||
|
// minishell->exit_status = 127;
|
||||||
|
// }
|
||||||
|
// else if (!can_execute_command(command, minishell))
|
||||||
|
// {
|
||||||
|
// ft_eprintf("minishell: %s: %s\n", command->path, strerror(errno));
|
||||||
|
// minishell->exit_status = errno;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
static void cmdfree(
|
||||||
|
t_command *command
|
||||||
|
) {
|
||||||
|
if (command == NULL)
|
||||||
|
return ;
|
||||||
|
cmdfree_argv(command->argv);
|
||||||
|
free(command->path);
|
||||||
|
free(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmdfree_argv(
|
||||||
|
char **argv
|
||||||
|
) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (argv == NULL)
|
||||||
|
return ;
|
||||||
|
i = 0;
|
||||||
|
while (argv[i] != NULL)
|
||||||
|
{
|
||||||
|
free(argv[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
free(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static void debug_print_command_info(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// size_t i;
|
||||||
|
|
||||||
|
// if (command == NULL)
|
||||||
|
// {
|
||||||
|
// printf("Command is NULL\n");
|
||||||
|
// return ;
|
||||||
|
// }
|
||||||
|
// printf("Command info:\n");
|
||||||
|
// printf(" Path: %s\n", command->path);
|
||||||
|
// printf(" Argc: %d\n", command->argc);
|
||||||
|
// printf(" Argv:\n");
|
||||||
|
// for (i = 0; i < (size_t)command->argc; i++)
|
||||||
|
// printf(" arg[%zu]: %s\n", i, command->argv[i]);
|
||||||
|
// printf(" Infile FD: %d\n", command->infile);
|
||||||
|
// printf(" Outfile FD: %d\n", command->outfile);
|
||||||
|
// printf("--------------------------\n");
|
||||||
|
// }
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/29 22:09:51 by sede-san #+# #+# */
|
/* Created: 2025/10/29 22:09:51 by sede-san #+# #+# */
|
||||||
/* Updated: 2026/02/14 01:15:34 by sede-san ### ########.fr */
|
/* Updated: 2026/02/08 19:42:50 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -16,9 +16,8 @@
|
|||||||
# include "ft_args.h"
|
# include "ft_args.h"
|
||||||
# include "minishell.h"
|
# include "minishell.h"
|
||||||
# include "core.h"
|
# include "core.h"
|
||||||
# include "variables.h"
|
|
||||||
|
|
||||||
typedef uint8_t (*t_builtin_func)(t_command cmd, t_minishell *minishell);
|
typedef unsigned char (*t_builtin_func)(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* Functions */
|
/* Functions */
|
||||||
@@ -26,36 +25,24 @@ typedef uint8_t (*t_builtin_func)(t_command cmd, t_minishell *minishell);
|
|||||||
|
|
||||||
/* builtins.c */
|
/* builtins.c */
|
||||||
|
|
||||||
extern uint8_t set_builtins(t_minishell *minishell);
|
extern u_int8_t set_builtins(t_minishell *minishell);
|
||||||
|
|
||||||
extern uint8_t is_builtin(const char *command_name, t_minishell *minishell);
|
extern u_int8_t is_builtin(const char *command_name, t_minishell *minishell);
|
||||||
|
|
||||||
/* cd.c */
|
/* cd.c */
|
||||||
|
|
||||||
extern uint8_t builtin_cd(t_command cmd, t_minishell *minishell);
|
extern u_int8_t builtin_cd(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
/* echo.c */
|
/* echo.c */
|
||||||
|
|
||||||
extern uint8_t builtin_echo(t_command cmd, t_minishell *minishell);
|
extern u_int8_t builtin_echo(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
/* exit.c */
|
/* exit.c */
|
||||||
|
|
||||||
extern uint8_t builtin_exit(t_command cmd, t_minishell *minishell);
|
extern u_int8_t builtin_exit(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
/* pwd.c */
|
/* pwd.c */
|
||||||
|
|
||||||
extern uint8_t builtin_pwd(t_command cmd, t_minishell *minishell);
|
extern u_int8_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 */
|
||||||
|
|||||||
@@ -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:10:13 by sede-san #+# #+# */
|
/* Created: 2025/10/22 19:10:13 by sede-san #+# #+# */
|
||||||
/* Updated: 2026/02/14 01:25:43 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 18:45:41 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -19,37 +19,9 @@
|
|||||||
/* Structures & Data Types */
|
/* Structures & Data Types */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
# define TOKENS_COUNT 5
|
typedef struct s_minishell t_minishell;
|
||||||
|
typedef struct s_variables t_variables;
|
||||||
typedef enum e_token_type
|
typedef struct s_command t_command;
|
||||||
{
|
|
||||||
TOKEN_WORD,
|
|
||||||
TOKEN_PIPE,
|
|
||||||
TOKEN_REDIRECT_IN,
|
|
||||||
TOKEN_REDIRECT_OUT,
|
|
||||||
TOKEN_APPEND,
|
|
||||||
TOKEN_HEREDOC
|
|
||||||
} t_token_type;
|
|
||||||
|
|
||||||
typedef struct s_token
|
|
||||||
{
|
|
||||||
t_token_type type;
|
|
||||||
char *value;
|
|
||||||
} t_token;
|
|
||||||
|
|
||||||
typedef enum e_redirection_type
|
|
||||||
{
|
|
||||||
REDIRECT_IN,
|
|
||||||
REDIRECT_OUT,
|
|
||||||
APPEND,
|
|
||||||
HEREDOC
|
|
||||||
} t_redirection_type;
|
|
||||||
|
|
||||||
typedef struct s_redirection
|
|
||||||
{
|
|
||||||
t_token_type type;
|
|
||||||
char *target;
|
|
||||||
} t_redirection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Structure that holds both environment and internal variables
|
* @brief Structure that holds both environment and internal variables
|
||||||
@@ -63,7 +35,7 @@ typedef struct s_redirection
|
|||||||
typedef struct s_variables
|
typedef struct s_variables
|
||||||
{
|
{
|
||||||
t_hashmap *environment;
|
t_hashmap *environment;
|
||||||
t_hashmap *internal;
|
// char **internal;
|
||||||
} t_variables;
|
} t_variables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,7 +70,6 @@ typedef struct s_command
|
|||||||
char **argv;
|
char **argv;
|
||||||
char *path;
|
char *path;
|
||||||
t_list *redirections;
|
t_list *redirections;
|
||||||
t_list *heredocs;
|
|
||||||
} t_command;
|
} t_command;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,20 +88,12 @@ typedef struct s_command
|
|||||||
|
|
||||||
/* minishell.c */
|
/* minishell.c */
|
||||||
|
|
||||||
extern void minishell_init(t_minishell *minishell, char **envp);
|
extern int minishell_init(t_minishell *minishell, char **envp);
|
||||||
|
|
||||||
extern void minishell_run(t_minishell *minishell);
|
extern uint8_t minishell_run(t_minishell *minishell);
|
||||||
|
|
||||||
extern void minishell_clear(t_minishell *minishell);
|
extern void minishell_clear(t_minishell *minishell);
|
||||||
|
|
||||||
extern void minishell_set_interactive_signals(void);
|
|
||||||
|
|
||||||
extern void minishell_set_execution_signals(void);
|
|
||||||
|
|
||||||
extern void minishell_set_child_signals(void);
|
|
||||||
|
|
||||||
extern bool minishell_consume_sigint(void);
|
|
||||||
|
|
||||||
/* environment.c */
|
/* environment.c */
|
||||||
|
|
||||||
extern void set_envp(char **envp, t_minishell *msh);
|
extern void set_envp(char **envp, t_minishell *msh);
|
||||||
@@ -142,8 +105,6 @@ extern char **get_envp(t_minishell *msh);
|
|||||||
|
|
||||||
extern void free_envp(char **envp);
|
extern void free_envp(char **envp);
|
||||||
|
|
||||||
void handle_sigint_status(t_minishell *minishell);
|
extern char *get_env(const char *env_name, t_minishell *msh);
|
||||||
bool handle_eof(char *line, t_minishell *minishell);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* CORE_H */
|
#endif /* CORE_H */
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* errors.h :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/10 22:28:01 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 01:11:38 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#ifndef ERRORS_H
|
|
||||||
# define ERRORS_H
|
|
||||||
|
|
||||||
# include "minishell.h"
|
|
||||||
# include "core.h"
|
|
||||||
# include "parser.h"
|
|
||||||
|
|
||||||
extern void syntax_error_unexpected_token(t_token *token);
|
|
||||||
extern void malloc_error(void);
|
|
||||||
extern void command_not_found_error(const char *command);
|
|
||||||
|
|
||||||
#endif /* ERRORS_H */
|
|
||||||
@@ -19,8 +19,6 @@
|
|||||||
|
|
||||||
# define READ_PIPE 0
|
# define READ_PIPE 0
|
||||||
# define WRITE_PIPE 1
|
# define WRITE_PIPE 1
|
||||||
# define PIPE_ERROR -1
|
|
||||||
# define FORK_ERROR -1
|
|
||||||
|
|
||||||
typedef struct s_pipeline
|
typedef struct s_pipeline
|
||||||
{
|
{
|
||||||
@@ -28,33 +26,16 @@ typedef struct s_pipeline
|
|||||||
int pipefd[2];
|
int pipefd[2];
|
||||||
} t_pipeline;
|
} t_pipeline;
|
||||||
|
|
||||||
typedef struct s_exec_state
|
|
||||||
{
|
/******************************************************************************/
|
||||||
uint8_t exit_status;
|
/* Functions */
|
||||||
t_pipeline pipeline;
|
/******************************************************************************/
|
||||||
t_list *current_command;
|
|
||||||
pid_t last_child_pid;
|
// executor.c
|
||||||
} t_exec_state;
|
|
||||||
|
# define PIPE_ERROR -1
|
||||||
|
# define FORK_ERROR -1
|
||||||
|
|
||||||
extern uint8_t execute(t_list *command, t_minishell *minishell);
|
extern uint8_t execute(t_list *command, t_minishell *minishell);
|
||||||
extern uint8_t executor_execute_command(t_command *cmd, t_minishell *msh);
|
|
||||||
extern char *executor_resolve_command_path(const t_command *cmd,
|
|
||||||
t_minishell *msh);
|
|
||||||
extern bool executor_is_builtin_command(const t_command *cmd,
|
|
||||||
t_minishell *msh);
|
|
||||||
extern int executor_create_pipe_if_needed(t_list *node, t_pipeline *pl);
|
|
||||||
extern bool executor_is_fork_required(t_list *node, const t_pipeline *pl,
|
|
||||||
t_minishell *msh);
|
|
||||||
extern void executor_setup_child_input(t_pipeline *pipeline);
|
|
||||||
extern void executor_setup_child_output(t_list *node, t_pipeline *pl);
|
|
||||||
extern void executor_child_process(t_list *node, t_pipeline *pl,
|
|
||||||
t_minishell *msh);
|
|
||||||
extern void executor_parent_cleanup(t_list *node, t_pipeline *pl);
|
|
||||||
extern uint8_t executor_wait_for_children(pid_t last_child_pid);
|
|
||||||
extern void executor_cmdfree(t_command *command);
|
|
||||||
extern bool executor_apply_redirections(const t_command *command,
|
|
||||||
int *saved_stdin, int *saved_stdout);
|
|
||||||
extern void executor_restore_redirections(int saved_stdin,
|
|
||||||
int saved_stdout);
|
|
||||||
|
|
||||||
#endif /* EXECUTOR_H */
|
#endif /* EXECUTOR_H */
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/20 16:35:10 by sede-san #+# #+# */
|
/* Created: 2025/10/20 16:35:10 by sede-san #+# #+# */
|
||||||
/* Updated: 2026/02/14 01:13:42 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 18:38:16 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -15,11 +15,9 @@
|
|||||||
|
|
||||||
# include "libft.h"
|
# include "libft.h"
|
||||||
# include "ft_printf.h"
|
# include "ft_printf.h"
|
||||||
# include "get_next_line.h"
|
|
||||||
# include "chardefs.h"
|
# include "chardefs.h"
|
||||||
# include <stdbool.h>
|
# include <stdbool.h>
|
||||||
# include <stdint.h>
|
# include <stdint.h>
|
||||||
# include <errno.h>
|
|
||||||
# include <readline/readline.h> // readline(3), rl_clear_history(),
|
# include <readline/readline.h> // readline(3), rl_clear_history(),
|
||||||
// rl_on_new_line(), rl_replace_line(),
|
// rl_on_new_line(), rl_replace_line(),
|
||||||
// rl_redisplay()
|
// rl_redisplay()
|
||||||
|
|||||||
@@ -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/12 18:34:11 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 20:36:19 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -17,6 +17,44 @@
|
|||||||
# 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
|
||||||
|
|
||||||
|
typedef enum e_token_type
|
||||||
|
{
|
||||||
|
TOKEN_WORD,
|
||||||
|
TOKEN_PIPE,
|
||||||
|
TOKEN_REDIRECT_IN,
|
||||||
|
TOKEN_REDIRECT_OUT,
|
||||||
|
TOKEN_APPEND,
|
||||||
|
TOKEN_HEREDOC
|
||||||
|
} t_token_type;
|
||||||
|
|
||||||
|
typedef struct s_token
|
||||||
|
{
|
||||||
|
t_token_type type;
|
||||||
|
char *value;
|
||||||
|
} t_token;
|
||||||
|
|
||||||
|
typedef enum e_redirection_type
|
||||||
|
{
|
||||||
|
REDIRECT_IN,
|
||||||
|
REDIRECT_OUT,
|
||||||
|
APPEND,
|
||||||
|
HEREDOC
|
||||||
|
} t_redirection_type;
|
||||||
|
|
||||||
|
typedef struct s_redirection
|
||||||
|
{
|
||||||
|
t_redirection_type type;
|
||||||
|
char *target;
|
||||||
|
} t_redirection;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* Functions */
|
/* Functions */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@@ -25,27 +63,4 @@
|
|||||||
|
|
||||||
extern t_list *parse(char *line, t_minishell *minishell);
|
extern t_list *parse(char *line, t_minishell *minishell);
|
||||||
|
|
||||||
// lexer.c
|
|
||||||
|
|
||||||
t_token_type get_token_type(const char *str);
|
|
||||||
t_token *token_new(t_token_type type, char *text);
|
|
||||||
t_token *read_token(t_token_type type, const char *line,
|
|
||||||
size_t *i);
|
|
||||||
t_token *read_word(const char *line, size_t *i);
|
|
||||||
|
|
||||||
extern t_list *lex(const char *line);
|
|
||||||
|
|
||||||
extern void token_clear(t_token *token);
|
|
||||||
extern t_command *command_new(t_list **tokens);
|
|
||||||
extern void command_clear(t_command *command);
|
|
||||||
extern void command_add_tokens(t_command **command, t_list **tokens);
|
|
||||||
extern bool is_pipe(t_token *token);
|
|
||||||
extern bool is_redirection(t_token *token);
|
|
||||||
void redirection_add(t_list **tokens, t_token *token,
|
|
||||||
t_command **command);
|
|
||||||
void words_add(t_list **tokens, t_command **command);
|
|
||||||
|
|
||||||
void expand(t_list **commands, t_minishell *minishell);
|
|
||||||
void redirection_clear(t_redirection *redirection);
|
|
||||||
|
|
||||||
#endif /* PARSER_H */
|
#endif /* PARSER_H */
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* variables.h :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 21:45:35 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 01:24:49 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#ifndef VARIABLES_H
|
|
||||||
# define VARIABLES_H
|
|
||||||
|
|
||||||
# include "minishell.h"
|
|
||||||
# include "core.h"
|
|
||||||
|
|
||||||
// variables.c
|
|
||||||
|
|
||||||
extern char *get_var(const char *name, t_minishell *minishell);
|
|
||||||
extern void set_var(const char *name, char *value, t_minishell *minishell);
|
|
||||||
extern void unset_var(const char *name, t_minishell *minishell);
|
|
||||||
|
|
||||||
// environment.c
|
|
||||||
|
|
||||||
extern char *get_env(const char *name, t_minishell *minishell);
|
|
||||||
extern void set_env(const char *name, char *value, t_minishell *minishell);
|
|
||||||
extern void unset_env(const char *name, t_minishell *minishell);
|
|
||||||
|
|
||||||
extern void set_envp(char **envp, t_minishell *minishell);
|
|
||||||
extern char **get_envp(t_minishell *minishell);
|
|
||||||
extern void free_envp(char **envp);
|
|
||||||
|
|
||||||
// internal.c
|
|
||||||
|
|
||||||
extern char *get_int(const char *name, t_minishell *minishell);
|
|
||||||
extern void set_int(const char *name, char *value, t_minishell *minishell);
|
|
||||||
extern void unset_int(const char *name, t_minishell *minishell);
|
|
||||||
|
|
||||||
extern void set_intp(t_minishell *minishell);
|
|
||||||
|
|
||||||
#endif /* VARIABLES_H */
|
|
||||||
27
minishell-codex/Makefile
Normal file
27
minishell-codex/Makefile
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
NAME := minishell
|
||||||
|
CC := cc
|
||||||
|
CFLAGS := -Wall -Wextra -Werror -g
|
||||||
|
INCLUDES := -Iinclude
|
||||||
|
READLINE_LIBS := -lreadline -lncurses
|
||||||
|
|
||||||
|
SRCS := $(shell find src -name '*.c')
|
||||||
|
OBJS := $(SRCS:src/%.c=build/%.o)
|
||||||
|
|
||||||
|
all: $(NAME)
|
||||||
|
|
||||||
|
$(NAME): $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(OBJS) $(READLINE_LIBS) -o $(NAME)
|
||||||
|
|
||||||
|
build/%.o: src/%.c
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build
|
||||||
|
|
||||||
|
fclean: clean
|
||||||
|
rm -f $(NAME)
|
||||||
|
|
||||||
|
re: fclean all
|
||||||
|
|
||||||
|
.PHONY: all clean fclean re
|
||||||
72
minishell-codex/docs/defensa.md
Normal file
72
minishell-codex/docs/defensa.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# Minishell - Guion de defensa (version codex)
|
||||||
|
|
||||||
|
Este guion esta alineado con la estructura real en `minishell-codex/`.
|
||||||
|
|
||||||
|
## 1. Explicacion corta del proyecto
|
||||||
|
- Minishell es un interprete de comandos interactivo.
|
||||||
|
- Implementa pipes, redirecciones, variables y builtins basicos.
|
||||||
|
- Se basa en el flujo: lectura -> lexer -> parser -> expansion -> ejecucion.
|
||||||
|
|
||||||
|
## 2. Flujo completo (paso a paso)
|
||||||
|
1. `readline()` muestra el prompt y devuelve la linea.
|
||||||
|
2. `lex_line()` divide la linea en tokens (`TOK_WORD`, `TOK_PIPE`, redirecciones).
|
||||||
|
3. `parse_tokens()` construye la pipeline con comandos y redirecciones.
|
||||||
|
4. `expand_pipeline()` aplica expansion de `$VAR` y `$?` respetando comillas.
|
||||||
|
5. `execute_pipeline()` resuelve `PATH`, prepara heredocs y ejecuta.
|
||||||
|
|
||||||
|
## 3. Estructuras clave
|
||||||
|
- `t_token`: tipo y texto de tokens (`minishell-codex/include/minishell.h`).
|
||||||
|
- `t_command`: argv, redirecciones, path.
|
||||||
|
- `t_pipeline`: lista de comandos.
|
||||||
|
- `t_redir`: tipo, target y fd.
|
||||||
|
- `t_shell`: estado global (env, exit_status, flags).
|
||||||
|
|
||||||
|
## 4. Lexer (por que esta separado)
|
||||||
|
- Maneja comillas y metacaracteres sin mezclar con ejecucion.
|
||||||
|
- Detecta errores de comillas sin cerrar.
|
||||||
|
- Facilita el parseo posterior.
|
||||||
|
|
||||||
|
## 5. Parser
|
||||||
|
- Convierte tokens en comandos reales.
|
||||||
|
- Cada `TOK_PIPE` crea un nuevo comando.
|
||||||
|
- Redirecciones se guardan en lista separada (`t_redir`).
|
||||||
|
- Valida errores (pipe sin comando, redireccion sin destino).
|
||||||
|
|
||||||
|
## 6. Expansion
|
||||||
|
- `expand_pipeline()` recorre argv y targets de redireccion.
|
||||||
|
- Reglas:
|
||||||
|
- En comilla simple no se expande.
|
||||||
|
- En comilla doble si se expande.
|
||||||
|
- `$?` es el exit status anterior.
|
||||||
|
|
||||||
|
## 7. Redirecciones y heredoc
|
||||||
|
- `apply_redirections()` abre y hace `dup2()`.
|
||||||
|
- `prepare_heredocs()` genera un pipe con el contenido.
|
||||||
|
- Heredoc no se mete en el historial.
|
||||||
|
|
||||||
|
## 8. Ejecucion y pipes
|
||||||
|
- Si hay un solo builtin, se ejecuta en el padre.
|
||||||
|
- Si hay pipeline, todos se forkean.
|
||||||
|
- Se conectan con `pipe()` y `dup2()`.
|
||||||
|
- Se espera a todos, y el exit status es el del ultimo comando.
|
||||||
|
|
||||||
|
## 9. Builtins
|
||||||
|
- Implementados en `src/builtins/builtins.c`.
|
||||||
|
- `echo`, `cd`, `pwd`, `env`, `export`, `unset`, `exit`.
|
||||||
|
- `export` valida identificadores y permite `KEY=VALUE`.
|
||||||
|
|
||||||
|
## 10. Señales
|
||||||
|
- Una sola global: `g_signal`.
|
||||||
|
- `ctrl-C`: limpia linea y muestra prompt.
|
||||||
|
- `ctrl-\`: se ignora en interactivo.
|
||||||
|
- En child se restauran señales por defecto.
|
||||||
|
|
||||||
|
## 11. Ejemplos rapidos para demostrar
|
||||||
|
- Pipes: `ls | wc -l`
|
||||||
|
- Redireccion: `echo hola > out.txt`
|
||||||
|
- Heredoc: `cat << EOF` -> texto -> `EOF`
|
||||||
|
- Expansion: `echo $HOME`, `echo $?`
|
||||||
|
|
||||||
|
## 12. Mensaje final recomendado
|
||||||
|
"Separar lexer, parser, expansion y ejecucion me permitio mantener el codigo claro
|
||||||
|
y replicar el comportamiento de bash para el minimo requerido por el subject."
|
||||||
59
minishell-codex/docs/tests_manual.md
Normal file
59
minishell-codex/docs/tests_manual.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# Minishell - Checklist de pruebas manuales
|
||||||
|
|
||||||
|
Ejecuta en `minishell-codex/`:
|
||||||
|
- `make`
|
||||||
|
- `./minishell`
|
||||||
|
|
||||||
|
## 1. Prompt y salida
|
||||||
|
- Iniciar y salir con `ctrl-D`.
|
||||||
|
- `exit` debe cerrar el shell con el ultimo status.
|
||||||
|
|
||||||
|
## 2. Comandos simples
|
||||||
|
- `ls`
|
||||||
|
- `pwd`
|
||||||
|
- `echo hola`
|
||||||
|
|
||||||
|
## 3. Builtins
|
||||||
|
- `echo -n hola` (sin salto de linea)
|
||||||
|
- `cd /` luego `pwd`
|
||||||
|
- `export TEST=42` luego `env | grep TEST`
|
||||||
|
- `unset TEST` luego `env | grep TEST` (no debe aparecer)
|
||||||
|
- `env` sin argumentos
|
||||||
|
- `exit 2`
|
||||||
|
|
||||||
|
## 4. Expansion
|
||||||
|
- `echo $HOME`
|
||||||
|
- `echo $?` despues de un comando que falle (ej: `ls noexiste`)
|
||||||
|
- `echo '$HOME'` (no expande)
|
||||||
|
- `echo "$HOME"` (si expande)
|
||||||
|
|
||||||
|
## 5. Pipes
|
||||||
|
- `ls | wc -l`
|
||||||
|
- `echo hola | cat`
|
||||||
|
- `cat /etc/passwd | grep root | wc -l`
|
||||||
|
|
||||||
|
## 6. Redirecciones
|
||||||
|
- `echo hola > out.txt` y luego `cat out.txt`
|
||||||
|
- `echo 1 >> out.txt` y luego `cat out.txt`
|
||||||
|
- `cat < out.txt`
|
||||||
|
|
||||||
|
## 7. Heredoc
|
||||||
|
- `cat << EOF`
|
||||||
|
- escribir varias lineas
|
||||||
|
- `EOF`
|
||||||
|
- Ver que se imprime todo lo escrito.
|
||||||
|
|
||||||
|
## 8. Comillas
|
||||||
|
- `echo "a b c"` (una sola palabra)
|
||||||
|
- `echo 'a b c'` (una sola palabra)
|
||||||
|
- `echo "a 'b' c"`
|
||||||
|
|
||||||
|
## 9. Errores de parseo
|
||||||
|
- `| ls` (no debe ejecutar)
|
||||||
|
- `echo hola >` (error)
|
||||||
|
- `echo "hola` (comillas sin cerrar)
|
||||||
|
|
||||||
|
## 10. Senales
|
||||||
|
- `ctrl-C` en prompt: debe limpiar linea y mostrar prompt nuevo.
|
||||||
|
- `sleep 5` y `ctrl-C`: debe interrumpir el proceso.
|
||||||
|
- `ctrl-\` no debe imprimir nada en prompt interactivo.
|
||||||
136
minishell-codex/include/minishell.h
Normal file
136
minishell-codex/include/minishell.h
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
#ifndef MINISHELL_H
|
||||||
|
#define MINISHELL_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#include <readline/history.h>
|
||||||
|
|
||||||
|
#define MS_PROMPT "minishell> "
|
||||||
|
|
||||||
|
extern int g_signal;
|
||||||
|
|
||||||
|
typedef enum e_tokentype
|
||||||
|
{
|
||||||
|
TOK_WORD,
|
||||||
|
TOK_PIPE,
|
||||||
|
TOK_REDIR_IN,
|
||||||
|
TOK_REDIR_OUT,
|
||||||
|
TOK_REDIR_APPEND,
|
||||||
|
TOK_HEREDOC
|
||||||
|
} t_tokentype;
|
||||||
|
|
||||||
|
typedef struct s_token
|
||||||
|
{
|
||||||
|
char *text;
|
||||||
|
t_tokentype type;
|
||||||
|
struct s_token *next;
|
||||||
|
} t_token;
|
||||||
|
|
||||||
|
typedef enum e_redirtype
|
||||||
|
{
|
||||||
|
REDIR_IN,
|
||||||
|
REDIR_OUT,
|
||||||
|
REDIR_APPEND,
|
||||||
|
REDIR_HEREDOC
|
||||||
|
} t_redirtype;
|
||||||
|
|
||||||
|
typedef struct s_redir
|
||||||
|
{
|
||||||
|
t_redirtype type;
|
||||||
|
char *target;
|
||||||
|
int fd;
|
||||||
|
struct s_redir *next;
|
||||||
|
} t_redir;
|
||||||
|
|
||||||
|
typedef struct s_command
|
||||||
|
{
|
||||||
|
char **argv;
|
||||||
|
int argc;
|
||||||
|
char *path;
|
||||||
|
t_redir *redirs;
|
||||||
|
} t_command;
|
||||||
|
|
||||||
|
typedef struct s_pipeline
|
||||||
|
{
|
||||||
|
t_command **cmds;
|
||||||
|
size_t count;
|
||||||
|
} t_pipeline;
|
||||||
|
|
||||||
|
typedef struct s_env
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
char *value;
|
||||||
|
struct s_env *next;
|
||||||
|
} t_env;
|
||||||
|
|
||||||
|
typedef struct s_shell
|
||||||
|
{
|
||||||
|
t_env *env;
|
||||||
|
int exit_status;
|
||||||
|
int last_status;
|
||||||
|
int exit_requested;
|
||||||
|
int interactive;
|
||||||
|
} t_shell;
|
||||||
|
|
||||||
|
/* core */
|
||||||
|
void ms_init(t_shell *sh, char **envp);
|
||||||
|
void ms_loop(t_shell *sh);
|
||||||
|
void ms_cleanup(t_shell *sh);
|
||||||
|
void ms_setup_signals(t_shell *sh);
|
||||||
|
void ms_set_child_signals(void);
|
||||||
|
void ms_set_heredoc_signals(void);
|
||||||
|
|
||||||
|
/* parser */
|
||||||
|
t_token *lex_line(const char *line, int *error);
|
||||||
|
void free_tokens(t_token *toks);
|
||||||
|
|
||||||
|
t_pipeline *parse_tokens(t_token *toks, int *error);
|
||||||
|
void free_pipeline(t_pipeline *p);
|
||||||
|
|
||||||
|
int expand_pipeline(t_pipeline *p, t_shell *sh);
|
||||||
|
|
||||||
|
/* executor */
|
||||||
|
int execute_pipeline(t_pipeline *p, t_shell *sh);
|
||||||
|
int prepare_heredocs(t_pipeline *p, t_shell *sh);
|
||||||
|
int apply_redirections(t_command *cmd, int *saved_stdin, int *saved_stdout);
|
||||||
|
void restore_redirections(int saved_stdin, int saved_stdout);
|
||||||
|
char *resolve_path(const char *cmd, t_shell *sh);
|
||||||
|
|
||||||
|
/* env */
|
||||||
|
void env_init(t_shell *sh, char **envp);
|
||||||
|
void env_clear(t_shell *sh);
|
||||||
|
char *env_get(t_shell *sh, const char *key);
|
||||||
|
int env_set(t_shell *sh, const char *key, const char *value);
|
||||||
|
int env_unset(t_shell *sh, const char *key);
|
||||||
|
char **env_to_envp(t_shell *sh);
|
||||||
|
void env_free_envp(char **envp);
|
||||||
|
void env_print(t_shell *sh);
|
||||||
|
|
||||||
|
/* builtins */
|
||||||
|
int is_builtin(const char *name);
|
||||||
|
int exec_builtin(t_command *cmd, t_shell *sh);
|
||||||
|
|
||||||
|
/* utils */
|
||||||
|
int ms_is_space(int c);
|
||||||
|
int ms_is_alpha(int c);
|
||||||
|
int ms_is_alnum(int c);
|
||||||
|
int ms_is_digit(int c);
|
||||||
|
char *ms_strdup(const char *s);
|
||||||
|
char *ms_strndup(const char *s, size_t n);
|
||||||
|
char *ms_strjoin(const char *a, const char *b);
|
||||||
|
char *ms_strjoin3(const char *a, const char *b, const char *c);
|
||||||
|
char *ms_substr(const char *s, size_t start, size_t len);
|
||||||
|
char **ms_split(const char *s, char delim);
|
||||||
|
void ms_free_split(char **sp);
|
||||||
|
char *ms_itoa(int n);
|
||||||
|
|
||||||
|
#endif
|
||||||
221
minishell-codex/src/builtins/builtins.c
Normal file
221
minishell-codex/src/builtins/builtins.c
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static int builtin_echo(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_cd(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_pwd(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_env(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_export(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_unset(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_exit(t_command *cmd, t_shell *sh);
|
||||||
|
|
||||||
|
int is_builtin(const char *name)
|
||||||
|
{
|
||||||
|
if (!name)
|
||||||
|
return 0;
|
||||||
|
return (strcmp(name, "echo") == 0 || strcmp(name, "cd") == 0
|
||||||
|
|| strcmp(name, "pwd") == 0 || strcmp(name, "env") == 0
|
||||||
|
|| strcmp(name, "export") == 0 || strcmp(name, "unset") == 0
|
||||||
|
|| strcmp(name, "exit") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int exec_builtin(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
if (strcmp(cmd->argv[0], "echo") == 0)
|
||||||
|
return builtin_echo(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "cd") == 0)
|
||||||
|
return builtin_cd(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "pwd") == 0)
|
||||||
|
return builtin_pwd(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "env") == 0)
|
||||||
|
return builtin_env(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "export") == 0)
|
||||||
|
return builtin_export(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "unset") == 0)
|
||||||
|
return builtin_unset(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "exit") == 0)
|
||||||
|
return builtin_exit(cmd, sh);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_echo(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
int newline = 1;
|
||||||
|
|
||||||
|
(void)sh;
|
||||||
|
while (cmd->argv[i] && cmd->argv[i][0] == '-' && cmd->argv[i][1] == 'n')
|
||||||
|
{
|
||||||
|
int j = 2;
|
||||||
|
while (cmd->argv[i][j] == 'n')
|
||||||
|
j++;
|
||||||
|
if (cmd->argv[i][j] != '\0')
|
||||||
|
break;
|
||||||
|
newline = 0;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
while (cmd->argv[i])
|
||||||
|
{
|
||||||
|
printf("%s", cmd->argv[i]);
|
||||||
|
if (cmd->argv[i + 1])
|
||||||
|
printf(" ");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (newline)
|
||||||
|
printf("\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_pwd(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
char buf[4096];
|
||||||
|
(void)cmd;
|
||||||
|
(void)sh;
|
||||||
|
if (getcwd(buf, sizeof(buf)))
|
||||||
|
printf("%s\n", buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_cd(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
char cwd[4096];
|
||||||
|
|
||||||
|
if (cmd->argc > 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: cd: too many arguments\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (cmd->argc == 1)
|
||||||
|
path = env_get(sh, "HOME");
|
||||||
|
else
|
||||||
|
path = cmd->argv[1];
|
||||||
|
if (!path)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: cd: HOME not set\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (getcwd(cwd, sizeof(cwd)))
|
||||||
|
env_set(sh, "OLDPWD", cwd);
|
||||||
|
if (chdir(path) != 0)
|
||||||
|
{
|
||||||
|
perror("minishell: cd");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (getcwd(cwd, sizeof(cwd)))
|
||||||
|
env_set(sh, "PWD", cwd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_env(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
if (cmd->argc > 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: env: too many arguments\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
env_print(sh);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int valid_identifier(const char *s)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
if (!s || !ms_is_alpha((unsigned char)s[0]))
|
||||||
|
return 0;
|
||||||
|
while (s[i])
|
||||||
|
{
|
||||||
|
if (!ms_is_alnum((unsigned char)s[i]))
|
||||||
|
return 0;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_export(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
if (cmd->argc == 1)
|
||||||
|
{
|
||||||
|
env_print(sh);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
while (cmd->argv[i])
|
||||||
|
{
|
||||||
|
char *eq = strchr(cmd->argv[i], '=');
|
||||||
|
if (eq)
|
||||||
|
{
|
||||||
|
char *key = ms_strndup(cmd->argv[i], (size_t)(eq - cmd->argv[i]));
|
||||||
|
char *val = ms_strdup(eq + 1);
|
||||||
|
if (!valid_identifier(key))
|
||||||
|
fprintf(stderr, "minishell: export: `%s': not a valid identifier\n", cmd->argv[i]);
|
||||||
|
else
|
||||||
|
env_set(sh, key, val);
|
||||||
|
free(key);
|
||||||
|
free(val);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!valid_identifier(cmd->argv[i]))
|
||||||
|
fprintf(stderr, "minishell: export: `%s': not a valid identifier\n", cmd->argv[i]);
|
||||||
|
else
|
||||||
|
env_set(sh, cmd->argv[i], "");
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_unset(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
while (cmd->argv[i])
|
||||||
|
{
|
||||||
|
env_unset(sh, cmd->argv[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_exit(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
long code = sh->exit_status;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (sh->interactive)
|
||||||
|
printf("exit\n");
|
||||||
|
if (cmd->argc == 1)
|
||||||
|
{
|
||||||
|
sh->exit_requested = 1;
|
||||||
|
sh->exit_status = (int)(code & 0xFF);
|
||||||
|
return sh->exit_status;
|
||||||
|
}
|
||||||
|
if (cmd->argc > 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: exit: too many arguments\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (cmd->argv[1][i] == '+' || cmd->argv[1][i] == '-')
|
||||||
|
i++;
|
||||||
|
if (cmd->argv[1][i] == '\0')
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: exit: %s: numeric argument required\n", cmd->argv[1]);
|
||||||
|
sh->exit_requested = 1;
|
||||||
|
sh->exit_status = 2;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
while (cmd->argv[1][i])
|
||||||
|
{
|
||||||
|
if (!ms_is_digit((unsigned char)cmd->argv[1][i]))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: exit: %s: numeric argument required\n", cmd->argv[1]);
|
||||||
|
sh->exit_requested = 1;
|
||||||
|
sh->exit_status = 2;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
code = strtol(cmd->argv[1], NULL, 10);
|
||||||
|
sh->exit_requested = 1;
|
||||||
|
sh->exit_status = (int)(code & 0xFF);
|
||||||
|
return sh->exit_status;
|
||||||
|
}
|
||||||
18
minishell-codex/src/core/init.c
Normal file
18
minishell-codex/src/core/init.c
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
void ms_init(t_shell *sh, char **envp)
|
||||||
|
{
|
||||||
|
memset(sh, 0, sizeof(*sh));
|
||||||
|
sh->interactive = isatty(STDIN_FILENO);
|
||||||
|
sh->exit_status = 0;
|
||||||
|
sh->last_status = 0;
|
||||||
|
sh->exit_requested = 0;
|
||||||
|
env_init(sh, envp);
|
||||||
|
ms_setup_signals(sh);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_cleanup(t_shell *sh)
|
||||||
|
{
|
||||||
|
env_clear(sh);
|
||||||
|
rl_clear_history();
|
||||||
|
}
|
||||||
46
minishell-codex/src/core/loop.c
Normal file
46
minishell-codex/src/core/loop.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static void handle_line(t_shell *sh, char *line)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
t_token *toks = NULL;
|
||||||
|
t_pipeline *p = NULL;
|
||||||
|
|
||||||
|
toks = lex_line(line, &error);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
free_tokens(toks);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p = parse_tokens(toks, &error);
|
||||||
|
free_tokens(toks);
|
||||||
|
if (error || !p)
|
||||||
|
{
|
||||||
|
free_pipeline(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (expand_pipeline(p, sh) != 0)
|
||||||
|
{
|
||||||
|
free_pipeline(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sh->exit_status = execute_pipeline(p, sh);
|
||||||
|
sh->last_status = sh->exit_status;
|
||||||
|
free_pipeline(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_loop(t_shell *sh)
|
||||||
|
{
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
while (!sh->exit_requested)
|
||||||
|
{
|
||||||
|
line = readline(MS_PROMPT);
|
||||||
|
if (!line)
|
||||||
|
break;
|
||||||
|
if (line[0] != '\0')
|
||||||
|
add_history(line);
|
||||||
|
handle_line(sh, line);
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
minishell-codex/src/core/signals.c
Normal file
46
minishell-codex/src/core/signals.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
int g_signal = 0;
|
||||||
|
|
||||||
|
static void sigint_handler(int sig)
|
||||||
|
{
|
||||||
|
g_signal = sig;
|
||||||
|
write(STDOUT_FILENO, "\n", 1);
|
||||||
|
rl_on_new_line();
|
||||||
|
rl_replace_line("", 0);
|
||||||
|
rl_redisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sigquit_handler(int sig)
|
||||||
|
{
|
||||||
|
g_signal = sig;
|
||||||
|
(void)sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_setup_signals(t_shell *sh)
|
||||||
|
{
|
||||||
|
struct sigaction sa_int;
|
||||||
|
struct sigaction sa_quit;
|
||||||
|
|
||||||
|
(void)sh;
|
||||||
|
memset(&sa_int, 0, sizeof(sa_int));
|
||||||
|
memset(&sa_quit, 0, sizeof(sa_quit));
|
||||||
|
sa_int.sa_handler = sigint_handler;
|
||||||
|
sa_quit.sa_handler = sigquit_handler;
|
||||||
|
sigemptyset(&sa_int.sa_mask);
|
||||||
|
sigemptyset(&sa_quit.sa_mask);
|
||||||
|
sigaction(SIGINT, &sa_int, NULL);
|
||||||
|
sigaction(SIGQUIT, &sa_quit, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_set_child_signals(void)
|
||||||
|
{
|
||||||
|
signal(SIGINT, SIG_DFL);
|
||||||
|
signal(SIGQUIT, SIG_DFL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_set_heredoc_signals(void)
|
||||||
|
{
|
||||||
|
signal(SIGINT, SIG_DFL);
|
||||||
|
signal(SIGQUIT, SIG_IGN);
|
||||||
|
}
|
||||||
171
minishell-codex/src/env/env.c
vendored
Normal file
171
minishell-codex/src/env/env.c
vendored
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static t_env *env_new(const char *key, const char *value)
|
||||||
|
{
|
||||||
|
t_env *n = (t_env *)calloc(1, sizeof(t_env));
|
||||||
|
if (!n)
|
||||||
|
return NULL;
|
||||||
|
n->key = ms_strdup(key);
|
||||||
|
n->value = value ? ms_strdup(value) : ms_strdup("");
|
||||||
|
if (!n->key || !n->value)
|
||||||
|
{
|
||||||
|
free(n->key);
|
||||||
|
free(n->value);
|
||||||
|
free(n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void env_init(t_shell *sh, char **envp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *eq;
|
||||||
|
|
||||||
|
sh->env = NULL;
|
||||||
|
if (!envp)
|
||||||
|
return;
|
||||||
|
i = 0;
|
||||||
|
while (envp[i])
|
||||||
|
{
|
||||||
|
eq = strchr(envp[i], '=');
|
||||||
|
if (eq)
|
||||||
|
{
|
||||||
|
char *key = ms_strndup(envp[i], (size_t)(eq - envp[i]));
|
||||||
|
char *val = ms_strdup(eq + 1);
|
||||||
|
env_set(sh, key, val);
|
||||||
|
free(key);
|
||||||
|
free(val);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void env_clear(t_shell *sh)
|
||||||
|
{
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
t_env *next;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
next = cur->next;
|
||||||
|
free(cur->key);
|
||||||
|
free(cur->value);
|
||||||
|
free(cur);
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
sh->env = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *env_get(t_shell *sh, const char *key)
|
||||||
|
{
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (strcmp(cur->key, key) == 0)
|
||||||
|
return cur->value;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int env_set(t_shell *sh, const char *key, const char *value)
|
||||||
|
{
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
t_env *prev = NULL;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (strcmp(cur->key, key) == 0)
|
||||||
|
{
|
||||||
|
char *dup = ms_strdup(value ? value : "");
|
||||||
|
if (!dup)
|
||||||
|
return 1;
|
||||||
|
free(cur->value);
|
||||||
|
cur->value = dup;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
cur = env_new(key, value);
|
||||||
|
if (!cur)
|
||||||
|
return 1;
|
||||||
|
if (prev)
|
||||||
|
prev->next = cur;
|
||||||
|
else
|
||||||
|
sh->env = cur;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int env_unset(t_shell *sh, const char *key)
|
||||||
|
{
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
t_env *prev = NULL;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (strcmp(cur->key, key) == 0)
|
||||||
|
{
|
||||||
|
if (prev)
|
||||||
|
prev->next = cur->next;
|
||||||
|
else
|
||||||
|
sh->env = cur->next;
|
||||||
|
free(cur->key);
|
||||||
|
free(cur->value);
|
||||||
|
free(cur);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **env_to_envp(t_shell *sh)
|
||||||
|
{
|
||||||
|
char **envp;
|
||||||
|
int count = 0;
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
envp = (char **)calloc((size_t)count + 1, sizeof(char *));
|
||||||
|
if (!envp)
|
||||||
|
return NULL;
|
||||||
|
cur = sh->env;
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
char *kv = ms_strjoin3(cur->key, "=", cur->value);
|
||||||
|
envp[i++] = kv;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
envp[i] = NULL;
|
||||||
|
return envp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void env_free_envp(char **envp)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
if (!envp)
|
||||||
|
return;
|
||||||
|
while (envp[i])
|
||||||
|
free(envp[i++]);
|
||||||
|
free(envp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void env_print(t_shell *sh)
|
||||||
|
{
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (cur->value)
|
||||||
|
printf("%s=%s\n", cur->key, cur->value);
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
134
minishell-codex/src/executor/exec.c
Normal file
134
minishell-codex/src/executor/exec.c
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static int exec_external(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
char **envp = env_to_envp(sh);
|
||||||
|
if (!envp)
|
||||||
|
return 1;
|
||||||
|
execve(cmd->path, cmd->argv, envp);
|
||||||
|
perror(cmd->path);
|
||||||
|
env_free_envp(envp);
|
||||||
|
return 126;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run_command_child(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
int saved_in, saved_out;
|
||||||
|
|
||||||
|
if (apply_redirections(cmd, &saved_in, &saved_out) != 0)
|
||||||
|
return 1;
|
||||||
|
if (is_builtin(cmd->argv[0]))
|
||||||
|
return exec_builtin(cmd, sh);
|
||||||
|
return exec_external(cmd, sh);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run_command_parent_builtin(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
int saved_in, saved_out;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (apply_redirections(cmd, &saved_in, &saved_out) != 0)
|
||||||
|
return 1;
|
||||||
|
status = exec_builtin(cmd, sh);
|
||||||
|
restore_redirections(saved_in, saved_out);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setup_pipes(int idx, int count, int pipefd[2])
|
||||||
|
{
|
||||||
|
if (idx + 1 >= count)
|
||||||
|
return 0;
|
||||||
|
if (pipe(pipefd) == -1)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setup_child_fds(int idx, int count, int prev_read, int pipefd[2])
|
||||||
|
{
|
||||||
|
if (prev_read != -1)
|
||||||
|
{
|
||||||
|
dup2(prev_read, STDIN_FILENO);
|
||||||
|
close(prev_read);
|
||||||
|
}
|
||||||
|
if (idx + 1 < count)
|
||||||
|
{
|
||||||
|
dup2(pipefd[1], STDOUT_FILENO);
|
||||||
|
close(pipefd[0]);
|
||||||
|
close(pipefd[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_parent_fds(int idx, int count, int *prev_read, int pipefd[2])
|
||||||
|
{
|
||||||
|
if (*prev_read != -1)
|
||||||
|
close(*prev_read);
|
||||||
|
if (idx + 1 < count)
|
||||||
|
{
|
||||||
|
close(pipefd[1]);
|
||||||
|
*prev_read = pipefd[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*prev_read = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int execute_pipeline(t_pipeline *p, t_shell *sh)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int prev_read = -1;
|
||||||
|
pid_t *pids;
|
||||||
|
int status = 0;
|
||||||
|
|
||||||
|
if (!p || p->count == 0)
|
||||||
|
return 0;
|
||||||
|
if (prepare_heredocs(p, sh) != 0)
|
||||||
|
return sh->exit_status;
|
||||||
|
for (size_t k = 0; k < p->count; k++)
|
||||||
|
{
|
||||||
|
if (!p->cmds[k]->argv || !p->cmds[k]->argv[0])
|
||||||
|
return 1;
|
||||||
|
free(p->cmds[k]->path);
|
||||||
|
p->cmds[k]->path = resolve_path(p->cmds[k]->argv[0], sh);
|
||||||
|
}
|
||||||
|
if (p->count == 1 && is_builtin(p->cmds[0]->argv[0]))
|
||||||
|
return run_command_parent_builtin(p->cmds[0], sh);
|
||||||
|
pids = (pid_t *)calloc(p->count, sizeof(pid_t));
|
||||||
|
if (!pids)
|
||||||
|
return 1;
|
||||||
|
for (i = 0; i < (int)p->count; i++)
|
||||||
|
{
|
||||||
|
int pipefd[2] = {-1, -1};
|
||||||
|
if (setup_pipes(i, (int)p->count, pipefd))
|
||||||
|
break;
|
||||||
|
pids[i] = fork();
|
||||||
|
if (pids[i] == 0)
|
||||||
|
{
|
||||||
|
ms_set_child_signals();
|
||||||
|
setup_child_fds(i, (int)p->count, prev_read, pipefd);
|
||||||
|
if (!p->cmds[i]->path)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: %s: command not found\n", p->cmds[i]->argv[0]);
|
||||||
|
exit(127);
|
||||||
|
}
|
||||||
|
status = run_command_child(p->cmds[i], sh);
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
close_parent_fds(i, (int)p->count, &prev_read, pipefd);
|
||||||
|
}
|
||||||
|
for (i = 0; i < (int)p->count; i++)
|
||||||
|
{
|
||||||
|
int wstatus = 0;
|
||||||
|
if (pids[i] > 0)
|
||||||
|
{
|
||||||
|
waitpid(pids[i], &wstatus, 0);
|
||||||
|
if (i == (int)p->count - 1)
|
||||||
|
{
|
||||||
|
if (WIFEXITED(wstatus))
|
||||||
|
status = WEXITSTATUS(wstatus);
|
||||||
|
else if (WIFSIGNALED(wstatus))
|
||||||
|
status = 128 + WTERMSIG(wstatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(pids);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
41
minishell-codex/src/executor/path.c
Normal file
41
minishell-codex/src/executor/path.c
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static int has_slash(const char *s)
|
||||||
|
{
|
||||||
|
return (s && strchr(s, '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *resolve_path(const char *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
char *path_env;
|
||||||
|
char **parts;
|
||||||
|
char *candidate;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!cmd)
|
||||||
|
return NULL;
|
||||||
|
if (is_builtin(cmd))
|
||||||
|
return ms_strdup(cmd);
|
||||||
|
if (has_slash(cmd))
|
||||||
|
return ms_strdup(cmd);
|
||||||
|
path_env = env_get(sh, "PATH");
|
||||||
|
if (!path_env)
|
||||||
|
return NULL;
|
||||||
|
parts = ms_split(path_env, ':');
|
||||||
|
if (!parts)
|
||||||
|
return NULL;
|
||||||
|
i = 0;
|
||||||
|
while (parts[i])
|
||||||
|
{
|
||||||
|
candidate = ms_strjoin3(parts[i], "/", cmd);
|
||||||
|
if (candidate && access(candidate, X_OK) == 0)
|
||||||
|
{
|
||||||
|
ms_free_split(parts);
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
free(candidate);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
ms_free_split(parts);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
108
minishell-codex/src/executor/redir.c
Normal file
108
minishell-codex/src/executor/redir.c
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static int open_redir(t_redir *r)
|
||||||
|
{
|
||||||
|
if (r->type == REDIR_IN)
|
||||||
|
return open(r->target, O_RDONLY);
|
||||||
|
if (r->type == REDIR_OUT)
|
||||||
|
return open(r->target, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (r->type == REDIR_APPEND)
|
||||||
|
return open(r->target, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int prepare_heredocs(t_pipeline *p, t_shell *sh)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
g_signal = 0;
|
||||||
|
for (i = 0; i < p->count; i++)
|
||||||
|
{
|
||||||
|
t_redir *r = p->cmds[i]->redirs;
|
||||||
|
while (r)
|
||||||
|
{
|
||||||
|
if (r->type == REDIR_HEREDOC)
|
||||||
|
{
|
||||||
|
int fds[2];
|
||||||
|
char *line;
|
||||||
|
ms_set_heredoc_signals();
|
||||||
|
if (pipe(fds) == -1)
|
||||||
|
return 1;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
line = readline("> ");
|
||||||
|
if (!line)
|
||||||
|
break;
|
||||||
|
if (strcmp(line, r->target) == 0)
|
||||||
|
{
|
||||||
|
free(line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write(fds[1], line, strlen(line));
|
||||||
|
write(fds[1], "\n", 1);
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
close(fds[1]);
|
||||||
|
r->fd = fds[0];
|
||||||
|
ms_setup_signals(sh);
|
||||||
|
if (g_signal == SIGINT)
|
||||||
|
{
|
||||||
|
sh->exit_status = 130;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r = r->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int apply_redirections(t_command *cmd, int *saved_stdin, int *saved_stdout)
|
||||||
|
{
|
||||||
|
t_redir *r = cmd->redirs;
|
||||||
|
*saved_stdin = -1;
|
||||||
|
*saved_stdout = -1;
|
||||||
|
while (r)
|
||||||
|
{
|
||||||
|
int fd = -1;
|
||||||
|
if (r->type == REDIR_HEREDOC)
|
||||||
|
fd = r->fd;
|
||||||
|
else
|
||||||
|
fd = open_redir(r);
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
perror(r->target);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (r->type == REDIR_IN || r->type == REDIR_HEREDOC)
|
||||||
|
{
|
||||||
|
if (*saved_stdin == -1)
|
||||||
|
*saved_stdin = dup(STDIN_FILENO);
|
||||||
|
dup2(fd, STDIN_FILENO);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (*saved_stdout == -1)
|
||||||
|
*saved_stdout = dup(STDOUT_FILENO);
|
||||||
|
dup2(fd, STDOUT_FILENO);
|
||||||
|
}
|
||||||
|
if (r->type != REDIR_HEREDOC)
|
||||||
|
close(fd);
|
||||||
|
r = r->next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void restore_redirections(int saved_stdin, int saved_stdout)
|
||||||
|
{
|
||||||
|
if (saved_stdin != -1)
|
||||||
|
{
|
||||||
|
dup2(saved_stdin, STDIN_FILENO);
|
||||||
|
close(saved_stdin);
|
||||||
|
}
|
||||||
|
if (saved_stdout != -1)
|
||||||
|
{
|
||||||
|
dup2(saved_stdout, STDOUT_FILENO);
|
||||||
|
close(saved_stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
minishell-codex/src/main.c
Normal file
17
minishell-codex/src/main.c
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv, char **envp)
|
||||||
|
{
|
||||||
|
t_shell sh;
|
||||||
|
|
||||||
|
(void)argv;
|
||||||
|
if (argc != 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: ./minishell\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ms_init(&sh, envp);
|
||||||
|
ms_loop(&sh);
|
||||||
|
ms_cleanup(&sh);
|
||||||
|
return sh.exit_status;
|
||||||
|
}
|
||||||
113
minishell-codex/src/parser/expand.c
Normal file
113
minishell-codex/src/parser/expand.c
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static void buf_append(char **buf, size_t *len, size_t *cap, const char *s)
|
||||||
|
{
|
||||||
|
size_t slen;
|
||||||
|
char *newbuf;
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
return;
|
||||||
|
slen = strlen(s);
|
||||||
|
if (*len + slen + 1 > *cap)
|
||||||
|
{
|
||||||
|
*cap = (*len + slen + 1) * 2;
|
||||||
|
newbuf = (char *)realloc(*buf, *cap);
|
||||||
|
if (!newbuf)
|
||||||
|
return;
|
||||||
|
*buf = newbuf;
|
||||||
|
}
|
||||||
|
memcpy(*buf + *len, s, slen);
|
||||||
|
*len += slen;
|
||||||
|
(*buf)[*len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void buf_append_char(char **buf, size_t *len, size_t *cap, char c)
|
||||||
|
{
|
||||||
|
char tmp[2];
|
||||||
|
tmp[0] = c;
|
||||||
|
tmp[1] = '\0';
|
||||||
|
buf_append(buf, len, cap, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *expand_word(const char *word, t_shell *sh)
|
||||||
|
{
|
||||||
|
int in_single = 0;
|
||||||
|
int in_double = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
char *out = NULL;
|
||||||
|
size_t len = 0, cap = 0;
|
||||||
|
|
||||||
|
while (word && word[i])
|
||||||
|
{
|
||||||
|
if (word[i] == '\'' && !in_double)
|
||||||
|
{
|
||||||
|
in_single = !in_single;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (word[i] == '"' && !in_single)
|
||||||
|
{
|
||||||
|
in_double = !in_double;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (word[i] == '$' && !in_single)
|
||||||
|
{
|
||||||
|
if (word[i + 1] == '?')
|
||||||
|
{
|
||||||
|
char *v = ms_itoa(sh->last_status);
|
||||||
|
buf_append(&out, &len, &cap, v);
|
||||||
|
free(v);
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ms_is_alpha((unsigned char)word[i + 1]))
|
||||||
|
{
|
||||||
|
size_t j = i + 1;
|
||||||
|
while (word[j] && ms_is_alnum((unsigned char)word[j]))
|
||||||
|
j++;
|
||||||
|
char *name = ms_strndup(word + i + 1, j - (i + 1));
|
||||||
|
char *val = env_get(sh, name);
|
||||||
|
buf_append(&out, &len, &cap, val ? val : "");
|
||||||
|
free(name);
|
||||||
|
i = j;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf_append_char(&out, &len, &cap, word[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (!out)
|
||||||
|
out = ms_strdup("");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int expand_pipeline(t_pipeline *p, t_shell *sh)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
int j;
|
||||||
|
for (i = 0; i < p->count; i++)
|
||||||
|
{
|
||||||
|
for (j = 0; p->cmds[i]->argv && p->cmds[i]->argv[j]; j++)
|
||||||
|
{
|
||||||
|
char *neww = expand_word(p->cmds[i]->argv[j], sh);
|
||||||
|
free(p->cmds[i]->argv[j]);
|
||||||
|
p->cmds[i]->argv[j] = neww;
|
||||||
|
}
|
||||||
|
if (p->cmds[i]->redirs)
|
||||||
|
{
|
||||||
|
t_redir *r = p->cmds[i]->redirs;
|
||||||
|
while (r)
|
||||||
|
{
|
||||||
|
if (r->target)
|
||||||
|
{
|
||||||
|
char *nt = expand_word(r->target, sh);
|
||||||
|
free(r->target);
|
||||||
|
r->target = nt;
|
||||||
|
}
|
||||||
|
r = r->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
131
minishell-codex/src/parser/lexer.c
Normal file
131
minishell-codex/src/parser/lexer.c
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static t_token *token_new(t_tokentype type, const char *text, size_t len)
|
||||||
|
{
|
||||||
|
t_token *t = (t_token *)calloc(1, sizeof(t_token));
|
||||||
|
if (!t)
|
||||||
|
return NULL;
|
||||||
|
t->type = type;
|
||||||
|
if (text)
|
||||||
|
t->text = ms_strndup(text, len);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void token_add(t_token **head, t_token *new)
|
||||||
|
{
|
||||||
|
t_token *cur;
|
||||||
|
if (!new)
|
||||||
|
return;
|
||||||
|
if (!*head)
|
||||||
|
{
|
||||||
|
*head = new;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cur = *head;
|
||||||
|
while (cur->next)
|
||||||
|
cur = cur->next;
|
||||||
|
cur->next = new;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_tokens(t_token *toks)
|
||||||
|
{
|
||||||
|
t_token *n;
|
||||||
|
while (toks)
|
||||||
|
{
|
||||||
|
n = toks->next;
|
||||||
|
free(toks->text);
|
||||||
|
free(toks);
|
||||||
|
toks = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_meta(char c)
|
||||||
|
{
|
||||||
|
return (c == '|' || c == '<' || c == '>');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_word(const char *line, size_t *i, t_token **out)
|
||||||
|
{
|
||||||
|
size_t start = *i;
|
||||||
|
int in_single = 0;
|
||||||
|
int in_double = 0;
|
||||||
|
|
||||||
|
while (line[*i])
|
||||||
|
{
|
||||||
|
if (line[*i] == '\'' && !in_double)
|
||||||
|
in_single = !in_single;
|
||||||
|
else if (line[*i] == '"' && !in_single)
|
||||||
|
in_double = !in_double;
|
||||||
|
else if (!in_single && !in_double)
|
||||||
|
{
|
||||||
|
if (ms_is_space(line[*i]) || is_meta(line[*i]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*i)++;
|
||||||
|
}
|
||||||
|
if (in_single || in_double)
|
||||||
|
return 1;
|
||||||
|
*out = token_new(TOK_WORD, line + start, *i - start);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_token *lex_line(const char *line, int *error)
|
||||||
|
{
|
||||||
|
t_token *toks = NULL;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
*error = 0;
|
||||||
|
while (line[i])
|
||||||
|
{
|
||||||
|
if (ms_is_space(line[i]))
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line[i] == '|')
|
||||||
|
{
|
||||||
|
token_add(&toks, token_new(TOK_PIPE, "|", 1));
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line[i] == '<')
|
||||||
|
{
|
||||||
|
if (line[i + 1] == '<')
|
||||||
|
{
|
||||||
|
token_add(&toks, token_new(TOK_HEREDOC, "<<", 2));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token_add(&toks, token_new(TOK_REDIR_IN, "<", 1));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line[i] == '>')
|
||||||
|
{
|
||||||
|
if (line[i + 1] == '>')
|
||||||
|
{
|
||||||
|
token_add(&toks, token_new(TOK_REDIR_APPEND, ">>", 2));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token_add(&toks, token_new(TOK_REDIR_OUT, ">", 1));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
t_token *w = NULL;
|
||||||
|
if (read_word(line, &i, &w))
|
||||||
|
{
|
||||||
|
*error = 1;
|
||||||
|
free_tokens(toks);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
token_add(&toks, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toks;
|
||||||
|
}
|
||||||
175
minishell-codex/src/parser/parser.c
Normal file
175
minishell-codex/src/parser/parser.c
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static t_command *command_new(void)
|
||||||
|
{
|
||||||
|
t_command *cmd = (t_command *)calloc(1, sizeof(t_command));
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void command_add_arg(t_command *cmd, const char *text)
|
||||||
|
{
|
||||||
|
char **new_argv;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!cmd || !text)
|
||||||
|
return;
|
||||||
|
i = cmd->argc;
|
||||||
|
new_argv = (char **)calloc((size_t)i + 2, sizeof(char *));
|
||||||
|
if (!new_argv)
|
||||||
|
return;
|
||||||
|
for (int j = 0; j < i; j++)
|
||||||
|
new_argv[j] = cmd->argv[j];
|
||||||
|
new_argv[i] = ms_strdup(text);
|
||||||
|
new_argv[i + 1] = NULL;
|
||||||
|
free(cmd->argv);
|
||||||
|
cmd->argv = new_argv;
|
||||||
|
cmd->argc += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void command_add_redir(t_command *cmd, t_redirtype type, const char *target)
|
||||||
|
{
|
||||||
|
t_redir *r = (t_redir *)calloc(1, sizeof(t_redir));
|
||||||
|
t_redir *cur;
|
||||||
|
if (!r)
|
||||||
|
return;
|
||||||
|
r->type = type;
|
||||||
|
r->target = ms_strdup(target);
|
||||||
|
r->fd = -1;
|
||||||
|
r->next = NULL;
|
||||||
|
if (!cmd->redirs)
|
||||||
|
{
|
||||||
|
cmd->redirs = r;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cur = cmd->redirs;
|
||||||
|
while (cur->next)
|
||||||
|
cur = cur->next;
|
||||||
|
cur->next = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_redirs(t_redir *r)
|
||||||
|
{
|
||||||
|
t_redir *n;
|
||||||
|
while (r)
|
||||||
|
{
|
||||||
|
n = r->next;
|
||||||
|
if (r->fd != -1)
|
||||||
|
close(r->fd);
|
||||||
|
free(r->target);
|
||||||
|
free(r);
|
||||||
|
r = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_pipeline(t_pipeline *p)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
if (!p)
|
||||||
|
return;
|
||||||
|
for (i = 0; i < p->count; i++)
|
||||||
|
{
|
||||||
|
if (p->cmds[i])
|
||||||
|
{
|
||||||
|
for (int j = 0; p->cmds[i]->argv && p->cmds[i]->argv[j]; j++)
|
||||||
|
free(p->cmds[i]->argv[j]);
|
||||||
|
free(p->cmds[i]->argv);
|
||||||
|
free(p->cmds[i]->path);
|
||||||
|
free_redirs(p->cmds[i]->redirs);
|
||||||
|
free(p->cmds[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(p->cmds);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pipeline_add_cmd(t_pipeline *p, t_command *cmd)
|
||||||
|
{
|
||||||
|
t_command **new_cmds;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
new_cmds = (t_command **)calloc(p->count + 1, sizeof(t_command *));
|
||||||
|
if (!new_cmds)
|
||||||
|
return 1;
|
||||||
|
for (i = 0; i < p->count; i++)
|
||||||
|
new_cmds[i] = p->cmds[i];
|
||||||
|
new_cmds[p->count] = cmd;
|
||||||
|
free(p->cmds);
|
||||||
|
p->cmds = new_cmds;
|
||||||
|
p->count += 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_pipeline *parse_tokens(t_token *toks, int *error)
|
||||||
|
{
|
||||||
|
t_pipeline *p;
|
||||||
|
t_command *cmd;
|
||||||
|
t_token *cur;
|
||||||
|
|
||||||
|
*error = 0;
|
||||||
|
p = (t_pipeline *)calloc(1, sizeof(t_pipeline));
|
||||||
|
if (!p)
|
||||||
|
return NULL;
|
||||||
|
cmd = command_new();
|
||||||
|
if (!cmd)
|
||||||
|
{
|
||||||
|
free(p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cur = toks;
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (cur->type == TOK_PIPE)
|
||||||
|
{
|
||||||
|
if (cmd->argc == 0)
|
||||||
|
{
|
||||||
|
*error = 1;
|
||||||
|
free_pipeline(p);
|
||||||
|
free(cmd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pipeline_add_cmd(p, cmd);
|
||||||
|
cmd = command_new();
|
||||||
|
if (!cmd)
|
||||||
|
{
|
||||||
|
*error = 1;
|
||||||
|
free_pipeline(p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur->type == TOK_REDIR_IN || cur->type == TOK_REDIR_OUT
|
||||||
|
|| cur->type == TOK_REDIR_APPEND || cur->type == TOK_HEREDOC)
|
||||||
|
{
|
||||||
|
if (!cur->next || cur->next->type != TOK_WORD)
|
||||||
|
{
|
||||||
|
*error = 1;
|
||||||
|
free_pipeline(p);
|
||||||
|
free(cmd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (cur->type == TOK_REDIR_IN)
|
||||||
|
command_add_redir(cmd, REDIR_IN, cur->next->text);
|
||||||
|
else if (cur->type == TOK_REDIR_OUT)
|
||||||
|
command_add_redir(cmd, REDIR_OUT, cur->next->text);
|
||||||
|
else if (cur->type == TOK_REDIR_APPEND)
|
||||||
|
command_add_redir(cmd, REDIR_APPEND, cur->next->text);
|
||||||
|
else if (cur->type == TOK_HEREDOC)
|
||||||
|
command_add_redir(cmd, REDIR_HEREDOC, cur->next->text);
|
||||||
|
cur = cur->next->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur->type == TOK_WORD)
|
||||||
|
command_add_arg(cmd, cur->text);
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
if (cmd->argc == 0)
|
||||||
|
{
|
||||||
|
*error = 1;
|
||||||
|
free_pipeline(p);
|
||||||
|
free(cmd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pipeline_add_cmd(p, cmd);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
6
minishell-codex/src/utils/char_utils.c
Normal file
6
minishell-codex/src/utils/char_utils.c
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
int ms_is_space(int c) { return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f'); }
|
||||||
|
int ms_is_alpha(int c) { return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'); }
|
||||||
|
int ms_is_digit(int c) { return (c >= '0' && c <= '9'); }
|
||||||
|
int ms_is_alnum(int c) { return (ms_is_alpha(c) || ms_is_digit(c)); }
|
||||||
67
minishell-codex/src/utils/split_utils.c
Normal file
67
minishell-codex/src/utils/split_utils.c
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static size_t count_parts(const char *s, char delim)
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
int in = 0;
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
if (*s == delim)
|
||||||
|
in = 0;
|
||||||
|
else if (!in)
|
||||||
|
{
|
||||||
|
in = 1;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **ms_split(const char *s, char delim)
|
||||||
|
{
|
||||||
|
char **out;
|
||||||
|
size_t parts;
|
||||||
|
size_t i = 0;
|
||||||
|
size_t start = 0;
|
||||||
|
size_t len = 0;
|
||||||
|
int in = 0;
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
parts = count_parts(s, delim);
|
||||||
|
out = (char **)calloc(parts + 1, sizeof(char *));
|
||||||
|
if (!out)
|
||||||
|
return NULL;
|
||||||
|
while (s[i])
|
||||||
|
{
|
||||||
|
if (s[i] == delim)
|
||||||
|
{
|
||||||
|
if (in)
|
||||||
|
{
|
||||||
|
out[len++] = ms_strndup(s + start, i - start);
|
||||||
|
in = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!in)
|
||||||
|
{
|
||||||
|
in = 1;
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (in)
|
||||||
|
out[len++] = ms_strndup(s + start, i - start);
|
||||||
|
out[len] = NULL;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_free_split(char **sp)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
if (!sp)
|
||||||
|
return;
|
||||||
|
while (sp[i])
|
||||||
|
free(sp[i++]);
|
||||||
|
free(sp);
|
||||||
|
}
|
||||||
100
minishell-codex/src/utils/str_utils.c
Normal file
100
minishell-codex/src/utils/str_utils.c
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
char *ms_strdup(const char *s)
|
||||||
|
{
|
||||||
|
char *out;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
len = strlen(s);
|
||||||
|
out = (char *)malloc(len + 1);
|
||||||
|
if (!out)
|
||||||
|
return NULL;
|
||||||
|
memcpy(out, s, len);
|
||||||
|
out[len] = '\0';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ms_strndup(const char *s, size_t n)
|
||||||
|
{
|
||||||
|
char *out;
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
out = (char *)malloc(n + 1);
|
||||||
|
if (!out)
|
||||||
|
return NULL;
|
||||||
|
memcpy(out, s, n);
|
||||||
|
out[n] = '\0';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ms_substr(const char *s, size_t start, size_t len)
|
||||||
|
{
|
||||||
|
size_t slen;
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
slen = strlen(s);
|
||||||
|
if (start >= slen)
|
||||||
|
return ms_strdup("");
|
||||||
|
if (start + len > slen)
|
||||||
|
len = slen - start;
|
||||||
|
return ms_strndup(s + start, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ms_strjoin(const char *a, const char *b)
|
||||||
|
{
|
||||||
|
size_t la;
|
||||||
|
size_t lb;
|
||||||
|
char *out;
|
||||||
|
|
||||||
|
if (!a || !b)
|
||||||
|
return NULL;
|
||||||
|
la = strlen(a);
|
||||||
|
lb = strlen(b);
|
||||||
|
out = (char *)malloc(la + lb + 1);
|
||||||
|
if (!out)
|
||||||
|
return NULL;
|
||||||
|
memcpy(out, a, la);
|
||||||
|
memcpy(out + la, b, lb);
|
||||||
|
out[la + lb] = '\0';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ms_strjoin3(const char *a, const char *b, const char *c)
|
||||||
|
{
|
||||||
|
char *ab;
|
||||||
|
char *abc;
|
||||||
|
|
||||||
|
ab = ms_strjoin(a, b);
|
||||||
|
if (!ab)
|
||||||
|
return NULL;
|
||||||
|
abc = ms_strjoin(ab, c);
|
||||||
|
free(ab);
|
||||||
|
return abc;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ms_itoa(int n)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
int i;
|
||||||
|
int neg;
|
||||||
|
long nb;
|
||||||
|
|
||||||
|
nb = n;
|
||||||
|
neg = (nb < 0);
|
||||||
|
if (neg)
|
||||||
|
nb = -nb;
|
||||||
|
i = 30;
|
||||||
|
buf[31] = '\0';
|
||||||
|
if (nb == 0)
|
||||||
|
buf[i--] = '0';
|
||||||
|
while (nb > 0)
|
||||||
|
{
|
||||||
|
buf[i--] = (char)('0' + (nb % 10));
|
||||||
|
nb /= 10;
|
||||||
|
}
|
||||||
|
if (neg)
|
||||||
|
buf[i--] = '-';
|
||||||
|
return ms_strdup(&buf[i + 1]);
|
||||||
|
}
|
||||||
@@ -12,53 +12,23 @@
|
|||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
|
|
||||||
static uint8_t register_builtin(
|
u_int8_t set_builtins(
|
||||||
t_minishell *minishell,
|
|
||||||
const char *name,
|
|
||||||
t_builtin_func builtin
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *key;
|
|
||||||
|
|
||||||
key = ft_strdup(name);
|
|
||||||
if (key == NULL)
|
|
||||||
return (0);
|
|
||||||
ft_hashmap_put(minishell->builtins, key, builtin);
|
|
||||||
if (!ft_hashmap_contains_key(minishell->builtins, name))
|
|
||||||
{
|
|
||||||
free(key);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t set_builtins(
|
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
) {
|
) {
|
||||||
minishell->builtins
|
minishell->builtins
|
||||||
= ft_hashmap_new(7, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
= ft_hashmap_new(4, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
||||||
if (minishell->builtins == NULL)
|
if (minishell->builtins == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
if (!register_builtin(minishell, "cd", builtin_cd)
|
ft_hashmap_put(minishell->builtins, ft_strdup("cd"), builtin_cd);
|
||||||
|| !register_builtin(minishell, "echo", builtin_echo)
|
ft_hashmap_put(minishell->builtins, ft_strdup("echo"), builtin_echo);
|
||||||
|| !register_builtin(minishell, "env", builtin_env)
|
ft_hashmap_put(minishell->builtins, ft_strdup("exit"), builtin_exit);
|
||||||
|| !register_builtin(minishell, "exit", builtin_exit)
|
ft_hashmap_put(minishell->builtins, ft_strdup("pwd"), builtin_pwd);
|
||||||
|| !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);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t is_builtin(
|
u_int8_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,104 +12,43 @@
|
|||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
|
|
||||||
static uint8_t handle_error(void);
|
static u_int8_t handle_error(t_command cmd, t_minishell *msh, char *path);
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
uint8_t builtin_cd(
|
u_int8_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;
|
|
||||||
|
|
||||||
path = resolve_cd_path(cmd, msh, &status);
|
|
||||||
if (status != EXIT_SUCCESS)
|
|
||||||
return (status);
|
|
||||||
show_pwd = (cmd.argc == 2 && ft_strcmp(cmd.argv[1], "-") == 0);
|
|
||||||
oldpwd = getcwd(NULL, 0);
|
|
||||||
if (chdir(path) == -1)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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_minishell *msh,
|
|
||||||
uint8_t *status
|
|
||||||
){
|
|
||||||
if (cmd.argc > 2)
|
if (cmd.argc > 2)
|
||||||
{
|
{
|
||||||
ft_eputendl("minishell: cd: too many arguments");
|
ft_eputendl("minishell: cd: too many arguments");
|
||||||
*status = EXIT_FAILURE;
|
return (2);
|
||||||
return (NULL);
|
|
||||||
}
|
}
|
||||||
if (cmd.argc == 2 && ft_strcmp(cmd.argv[1], "-") == 0)
|
else if (cmd.argc == 1)
|
||||||
return (get_path_from_env(msh, "OLDPWD",
|
path = get_env("HOME", msh);
|
||||||
"minishell: cd: OLDPWD not set", status));
|
else
|
||||||
if (cmd.argc == 1)
|
path = cmd.argv[1];
|
||||||
return (get_path_from_env(msh, "HOME",
|
if (chdir(path) == -1)
|
||||||
"minishell: cd: HOME not set", status));
|
return (handle_error(cmd, msh, path));
|
||||||
*status = EXIT_SUCCESS;
|
return (EXIT_SUCCESS);
|
||||||
return (cmd.argv[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *get_path_from_env(
|
static u_int8_t handle_error(
|
||||||
|
t_command cmd,
|
||||||
t_minishell *msh,
|
t_minishell *msh,
|
||||||
const char *env_name,
|
char *path
|
||||||
const char *error,
|
|
||||||
uint8_t *status
|
|
||||||
){
|
){
|
||||||
char *path;
|
u_int8_t exit_code;
|
||||||
|
|
||||||
path = get_env(env_name, msh);
|
(void)msh;
|
||||||
if (path == NULL)
|
exit_code = 0;
|
||||||
{
|
if (access(path, F_OK) != -1)
|
||||||
ft_eputendl((char *)error);
|
// No such file or directory
|
||||||
*status = EXIT_FAILURE;
|
exit_code = 1;
|
||||||
return (NULL);
|
else if (access(path, X_OK) == -1)
|
||||||
}
|
// Permission denied
|
||||||
*status = EXIT_SUCCESS;
|
exit_code = 2;
|
||||||
return (path);
|
perror(cmd.argv[0]);
|
||||||
|
return (exit_code);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
#include "echo_def.h"
|
#include "echo_def.h"
|
||||||
|
|
||||||
uint8_t builtin_echo(
|
u_int8_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 uint8_t is_valid_flag(t_args *args, char *flag);
|
static u_int8_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 uint8_t is_valid_flag(
|
static u_int8_t is_valid_flag(
|
||||||
t_args *args,
|
t_args *args,
|
||||||
char *flag
|
char *flag
|
||||||
){
|
){
|
||||||
|
|||||||
58
src/builtins/env/env.c
vendored
58
src/builtins/env/env.c
vendored
@@ -1,58 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* 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,45 +11,36 @@
|
|||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
|
#include <stdint.h>
|
||||||
static bool exit_arg_is_invalid(const char *arg)
|
|
||||||
{
|
|
||||||
if (arg == NULL)
|
|
||||||
return (true);
|
|
||||||
if ((*arg == '+' || *arg == '-') && arg[1] == '\0')
|
|
||||||
return (true);
|
|
||||||
if (!ft_strisnum(arg))
|
|
||||||
return (true);
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t builtin_exit(
|
uint8_t builtin_exit(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
t_minishell *msh
|
t_minishell *msh
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (isatty(STDIN_FILENO))
|
uint8_t exit_status;
|
||||||
|
|
||||||
ft_eputendl("exit");
|
ft_eputendl("exit");
|
||||||
if (cmd.argc == 1)
|
if (cmd.argc == 1)
|
||||||
|
exit_status = msh->exit_status;
|
||||||
|
else if (!ft_strisnum(cmd.argv[1]))
|
||||||
{
|
{
|
||||||
msh->exit = 1;
|
ft_eprintf(
|
||||||
return (msh->exit_status);
|
"minishell: exit: %s: numeric argument required\n",
|
||||||
}
|
|
||||||
if (exit_arg_is_invalid(cmd.argv[1]))
|
|
||||||
{
|
|
||||||
ft_eprintf("minishell: exit: %s: numeric argument required\n",
|
|
||||||
cmd.argv[1]);
|
cmd.argv[1]);
|
||||||
msh->exit = 1;
|
return (2);
|
||||||
msh->exit_status = 2;
|
|
||||||
return (msh->exit_status);
|
|
||||||
}
|
}
|
||||||
if (cmd.argc > 2)
|
else if (cmd.argc > 2)
|
||||||
{
|
{
|
||||||
ft_eputendl("minishell: exit: too many arguments");
|
ft_eputendl("exit: too many arguments");
|
||||||
msh->exit_status = EXIT_FAILURE;
|
return (2);
|
||||||
return (msh->exit_status);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
exit_status = (uint8_t)ft_atol(cmd.argv[1]);
|
||||||
|
|
||||||
|
printf("builtin_exit: exit_status=%d\n", exit_status); // Debug print
|
||||||
|
|
||||||
msh->exit = 1;
|
msh->exit = 1;
|
||||||
msh->exit_status = (uint8_t)ft_atol(cmd.argv[1]);
|
msh->exit_status = exit_status;
|
||||||
return (msh->exit_status);
|
return (exit_status);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* export.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/09 22:05:00 by codex #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 02:02:14 by sede-san ### ########.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_var(name, eq_pos + 1, msh);
|
|
||||||
else
|
|
||||||
set_var(name, "", msh);
|
|
||||||
free(name);
|
|
||||||
return (EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
@@ -12,21 +12,15 @@
|
|||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
|
|
||||||
uint8_t builtin_pwd(
|
u_int8_t builtin_pwd(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
t_minishell *msh
|
t_minishell *msh
|
||||||
){
|
){
|
||||||
char *cwd;
|
char cwd[PATH_MAX];
|
||||||
|
|
||||||
(void)cmd;
|
(void)cmd;
|
||||||
(void)msh;
|
(void)msh;
|
||||||
cwd = getcwd(NULL, 0);
|
if (getcwd(cwd, PATH_MAX) != NULL)
|
||||||
if (cwd == NULL)
|
|
||||||
{
|
|
||||||
perror("minishell: pwd");
|
|
||||||
return (EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
ft_putendl(cwd);
|
ft_putendl(cwd);
|
||||||
free(cwd);
|
|
||||||
return (EXIT_SUCCESS);
|
return (EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
@@ -9,3 +9,4 @@
|
|||||||
/* Updated: 2026/02/09 18:40:04 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 18:40:04 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* signals.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 00:00:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/13 00:00:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "core.h"
|
|
||||||
|
|
||||||
static int g_signal = 0;
|
|
||||||
|
|
||||||
static void sigint_handler(int signal)
|
|
||||||
{
|
|
||||||
g_signal = signal;
|
|
||||||
write(STDOUT_FILENO, "\n", 1);
|
|
||||||
rl_on_new_line();
|
|
||||||
rl_replace_line("", 0);
|
|
||||||
rl_redisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
void minishell_set_interactive_signals(void)
|
|
||||||
{
|
|
||||||
struct sigaction action;
|
|
||||||
|
|
||||||
ft_bzero(&action, sizeof(action));
|
|
||||||
action.sa_handler = sigint_handler;
|
|
||||||
sigemptyset(&action.sa_mask);
|
|
||||||
sigaction(SIGINT, &action, NULL);
|
|
||||||
action.sa_handler = SIG_IGN;
|
|
||||||
sigaction(SIGQUIT, &action, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void minishell_set_execution_signals(void)
|
|
||||||
{
|
|
||||||
struct sigaction action;
|
|
||||||
|
|
||||||
ft_bzero(&action, sizeof(action));
|
|
||||||
action.sa_handler = SIG_IGN;
|
|
||||||
sigemptyset(&action.sa_mask);
|
|
||||||
sigaction(SIGINT, &action, NULL);
|
|
||||||
sigaction(SIGQUIT, &action, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void minishell_set_child_signals(void)
|
|
||||||
{
|
|
||||||
struct sigaction action;
|
|
||||||
|
|
||||||
ft_bzero(&action, sizeof(action));
|
|
||||||
action.sa_handler = SIG_DFL;
|
|
||||||
sigemptyset(&action.sa_mask);
|
|
||||||
sigaction(SIGINT, &action, NULL);
|
|
||||||
sigaction(SIGQUIT, &action, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool minishell_consume_sigint(void)
|
|
||||||
{
|
|
||||||
if (g_signal != SIGINT)
|
|
||||||
return (false);
|
|
||||||
g_signal = 0;
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* errors.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/10 22:22:06 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/12 02:55:36 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "errors.h"
|
|
||||||
|
|
||||||
void syntax_error_unexpected_token(
|
|
||||||
t_token *token
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *cause;
|
|
||||||
|
|
||||||
if (token == NULL)
|
|
||||||
cause = "newline";
|
|
||||||
else
|
|
||||||
cause = token->value;
|
|
||||||
ft_eprintf("minishell: syntax error near unexpected token `%s'\n", cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
void malloc_error(void)
|
|
||||||
{
|
|
||||||
ft_eprintf("minishell: %s\n", strerror(ENOMEM));
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* command_exec.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/11 00:00:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 01:34:37 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "executor.h"
|
|
||||||
#include "builtins.h"
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
static uint8_t resolve_execve_status(void)
|
|
||||||
{
|
|
||||||
if (errno == ENOENT)
|
|
||||||
return (127);
|
|
||||||
if (errno == EACCES || errno == ENOEXEC || errno == EISDIR)
|
|
||||||
return (126);
|
|
||||||
return (EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t execute_builtin(
|
|
||||||
const t_command *command,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
const t_builtin_func builtin
|
|
||||||
= ft_hashmap_get(minishell->builtins, command->argv[0]);
|
|
||||||
|
|
||||||
return (builtin(*command, minishell));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_execve_error(
|
|
||||||
const t_command *command,
|
|
||||||
char **envp
|
|
||||||
)
|
|
||||||
{
|
|
||||||
uint8_t exit_status;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
exit_status = resolve_execve_status();
|
|
||||||
i = 0;
|
|
||||||
while (envp[i] != NULL)
|
|
||||||
free(envp[i++]);
|
|
||||||
free(envp);
|
|
||||||
perror(command->path);
|
|
||||||
exit(exit_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void execute_external_command(
|
|
||||||
const t_command *command,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char **envp;
|
|
||||||
|
|
||||||
envp = get_envp(minishell);
|
|
||||||
if (envp == NULL)
|
|
||||||
{
|
|
||||||
perror("get_envp");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
execve(command->path, command->argv, envp);
|
|
||||||
handle_execve_error(command, envp);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t executor_execute_command(
|
|
||||||
t_command *command,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (command == NULL || command->argv == NULL || command->argv[0] == NULL)
|
|
||||||
return (EXIT_SUCCESS);
|
|
||||||
if (executor_is_builtin_command(command, minishell))
|
|
||||||
return (execute_builtin(command, minishell));
|
|
||||||
if (command->path != NULL)
|
|
||||||
free(command->path);
|
|
||||||
command->path = executor_resolve_command_path(command, minishell);
|
|
||||||
if (command->path == NULL)
|
|
||||||
{
|
|
||||||
ft_eprintf("minishell: %s: command not found\n", command->argv[0]);
|
|
||||||
return (127);
|
|
||||||
}
|
|
||||||
execute_external_command(command, minishell);
|
|
||||||
return (EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* command_free.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 00:00:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/13 00:00:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "executor.h"
|
|
||||||
|
|
||||||
static void cmdfree_argv(
|
|
||||||
char **argv
|
|
||||||
)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (argv == NULL)
|
|
||||||
return ;
|
|
||||||
i = 0;
|
|
||||||
while (argv[i] != NULL)
|
|
||||||
{
|
|
||||||
free(argv[i]);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
free(argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cmdfree_redirection(
|
|
||||||
t_redirection *redirection
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (redirection == NULL)
|
|
||||||
return ;
|
|
||||||
free(redirection->target);
|
|
||||||
free(redirection);
|
|
||||||
}
|
|
||||||
|
|
||||||
void executor_cmdfree(
|
|
||||||
t_command *command
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (command == NULL)
|
|
||||||
return ;
|
|
||||||
cmdfree_argv(command->argv);
|
|
||||||
free(command->path);
|
|
||||||
ft_lstclear(&command->redirections,
|
|
||||||
(void (*)(void *))cmdfree_redirection);
|
|
||||||
ft_lstclear(&command->heredocs, (void (*)(void *))cmdfree_redirection);
|
|
||||||
free(command);
|
|
||||||
}
|
|
||||||
@@ -6,79 +6,198 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2026/02/08 19:10:47 by sede-san #+# #+# */
|
/* Created: 2026/02/08 19:10:47 by sede-san #+# #+# */
|
||||||
/* Updated: 2026/02/11 00:00:00 by sede-san ### ########.fr */
|
/* Updated: 2026/02/08 21:32:03 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "executor.h"
|
#include "executor.h"
|
||||||
|
#include "builtins.h"
|
||||||
|
|
||||||
static void init_exec_state(
|
static inline uint8_t execute_builtin(
|
||||||
t_exec_state *state,
|
const t_command *command,
|
||||||
t_list *command_list
|
t_minishell *minishell
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
state->exit_status = EXIT_SUCCESS;
|
const t_builtin_func builtin
|
||||||
state->pipeline.prev_read_fd = -1;
|
= ft_hashmap_get(minishell->builtins, command->path);
|
||||||
state->current_command = command_list;
|
|
||||||
state->last_child_pid = -1;
|
return (builtin(*command, minishell));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool fork_current_command(
|
static void handle_execve_error(
|
||||||
t_exec_state *state,
|
const t_command *command,
|
||||||
|
char **envp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
free_envp(envp);
|
||||||
|
perror(command->path);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void execute_external_command(
|
||||||
|
const t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
char **envp;
|
||||||
|
|
||||||
|
envp = get_envp(minishell);
|
||||||
|
if (envp == NULL)
|
||||||
|
{
|
||||||
|
perror("get_envp");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
execve(command->path, command->argv, envp);
|
||||||
|
handle_execve_error(command, envp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t execute_command(
|
||||||
|
const t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (is_builtin(command->path, minishell))
|
||||||
|
return (execute_builtin(command, minishell));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
execute_external_command(command, minishell);
|
||||||
|
return (EXIT_FAILURE); //! should never reach here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmdfree_argv(
|
||||||
|
char **argv
|
||||||
|
)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (argv == NULL)
|
||||||
|
return ;
|
||||||
|
i = 0;
|
||||||
|
while (argv[i] != NULL)
|
||||||
|
{
|
||||||
|
free(argv[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
free(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmdfree(
|
||||||
|
t_command *command
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (command == NULL)
|
||||||
|
return ;
|
||||||
|
cmdfree_argv(command->argv);
|
||||||
|
free(command->path);
|
||||||
|
free(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int create_pipe_if_needed(
|
||||||
|
t_list *current_command,
|
||||||
|
t_pipeline *pipeline
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!current_command->next)
|
||||||
|
return (0);
|
||||||
|
if (pipe(pipeline->pipefd) == PIPE_ERROR)
|
||||||
|
return (perror("pipe"), PIPE_ERROR);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_fork_required(
|
||||||
|
t_list *current_command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
const t_command *command = current_command->content;
|
||||||
|
|
||||||
|
return (current_command->next != NULL
|
||||||
|
|| !is_builtin(command->path, minishell));
|
||||||
|
}
|
||||||
|
|
||||||
|
static pid_t fork_process(
|
||||||
|
t_list *current_command,
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
|
pid = 0;
|
||||||
|
if (is_fork_required(current_command, minishell))
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid == FORK_ERROR)
|
if (pid == FORK_ERROR)
|
||||||
return (perror("fork"), state->exit_status = EXIT_FAILURE, false);
|
perror("fork");
|
||||||
if (pid == 0)
|
return (pid);
|
||||||
executor_child_process(state->current_command, &state->pipeline,
|
|
||||||
minishell);
|
|
||||||
state->last_child_pid = pid;
|
|
||||||
return (true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run_command_in_parent(
|
static void setup_child_input(
|
||||||
t_exec_state *state,
|
t_pipeline *pipeline
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (pipeline->prev_read_fd != -1)
|
||||||
|
{
|
||||||
|
dup2(pipeline->prev_read_fd, STDIN_FILENO);
|
||||||
|
close(pipeline->prev_read_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setup_child_output(
|
||||||
|
t_list *current_command,
|
||||||
|
t_pipeline *pipeline
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
dup2(pipeline->pipefd[WRITE_PIPE], STDOUT_FILENO);
|
||||||
|
close(pipeline->pipefd[READ_PIPE]);
|
||||||
|
close(pipeline->pipefd[WRITE_PIPE]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void child_process(
|
||||||
|
t_list *current_command,
|
||||||
|
t_pipeline *pipeline,
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
t_command *command;
|
uint8_t exit_status;
|
||||||
int saved_stdin;
|
const t_command *command = current_command->content;
|
||||||
int saved_stdout;
|
|
||||||
|
|
||||||
command = state->current_command->content;
|
setup_child_input(pipeline);
|
||||||
if (!executor_apply_redirections(command, &saved_stdin, &saved_stdout))
|
setup_child_output(current_command, pipeline);
|
||||||
state->exit_status = EXIT_FAILURE;
|
exit_status = execute_command(command, minishell);
|
||||||
else
|
if (is_fork_required(current_command, minishell))
|
||||||
state->exit_status = executor_execute_command(command, minishell);
|
exit(exit_status);
|
||||||
executor_restore_redirections(saved_stdin, saved_stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool run_current_command(
|
static void parent_cleanup(
|
||||||
t_exec_state *state,
|
t_list *current_command,
|
||||||
t_minishell *minishell
|
t_pipeline *pipeline
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
bool should_fork;
|
if (pipeline->prev_read_fd != -1)
|
||||||
|
close(pipeline->prev_read_fd);
|
||||||
if (executor_create_pipe_if_needed(state->current_command,
|
if (current_command->next)
|
||||||
&state->pipeline) == PIPE_ERROR)
|
|
||||||
return (state->exit_status = EXIT_FAILURE, false);
|
|
||||||
should_fork = executor_is_fork_required(state->current_command,
|
|
||||||
&state->pipeline, minishell);
|
|
||||||
if (should_fork)
|
|
||||||
{
|
{
|
||||||
if (!fork_current_command(state, minishell))
|
close(pipeline->pipefd[WRITE_PIPE]);
|
||||||
return (false);
|
pipeline->prev_read_fd = pipeline->pipefd[READ_PIPE];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
run_command_in_parent(state, minishell);
|
pipeline->prev_read_fd = -1;
|
||||||
executor_parent_cleanup(state->current_command, &state->pipeline);
|
}
|
||||||
state->current_command = state->current_command->next;
|
|
||||||
return (true);
|
static uint8_t wait_for_children(void)
|
||||||
|
{
|
||||||
|
uint8_t exit_status;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
exit_status = EXIT_SUCCESS;
|
||||||
|
while (wait(&status) > 0)
|
||||||
|
{
|
||||||
|
if (WIFEXITED(status))
|
||||||
|
exit_status = WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
return (exit_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t execute(
|
uint8_t execute(
|
||||||
@@ -86,19 +205,26 @@ uint8_t execute(
|
|||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
t_exec_state state;
|
uint8_t exit_status;
|
||||||
|
t_pipeline pipeline;
|
||||||
|
t_list *current_command;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
init_exec_state(&state, command_list);
|
pipeline.prev_read_fd = -1;
|
||||||
minishell_set_execution_signals();
|
current_command = command_list;
|
||||||
while (state.current_command)
|
while (current_command)
|
||||||
{
|
{
|
||||||
if (!run_current_command(&state, minishell))
|
if (create_pipe_if_needed(current_command, &pipeline) == PIPE_ERROR)
|
||||||
break ;
|
break ;
|
||||||
|
pid = fork_process(current_command, minishell);
|
||||||
|
if (pid == FORK_ERROR)
|
||||||
|
break ;
|
||||||
|
if (pid == 0)
|
||||||
|
child_process(current_command, &pipeline, minishell);
|
||||||
|
parent_cleanup(current_command, &pipeline);
|
||||||
|
current_command = current_command->next;
|
||||||
}
|
}
|
||||||
if (state.last_child_pid > 0)
|
exit_status = wait_for_children();
|
||||||
state.exit_status = executor_wait_for_children(state.last_child_pid);
|
ft_lstclear(&command_list, (void (*)(void *))cmdfree);
|
||||||
minishell_set_interactive_signals();
|
return (exit_status);
|
||||||
minishell->exit_status = state.exit_status;
|
|
||||||
ft_lstclear(&command_list, (void (*)(void *))executor_cmdfree);
|
|
||||||
return (state.exit_status);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* path_resolver.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/11 00:00:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 01:17:01 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "executor.h"
|
|
||||||
#include "variables.h"
|
|
||||||
|
|
||||||
static bool is_path_explicit(
|
|
||||||
const char *command_name
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return (command_name != NULL && ft_strchr(command_name, '/') != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *resolve_path_from_env(
|
|
||||||
const char *command_name,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *command_path;
|
|
||||||
char **path_env;
|
|
||||||
char *path_value;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
path_value = get_env("PATH", minishell);
|
|
||||||
if (path_value == NULL)
|
|
||||||
return (NULL);
|
|
||||||
path_env = ft_split(path_value, ':');
|
|
||||||
if (path_env == NULL)
|
|
||||||
return (NULL);
|
|
||||||
command_path = NULL;
|
|
||||||
i = -1;
|
|
||||||
while (!command_path && path_env[++i] != NULL)
|
|
||||||
{
|
|
||||||
command_path = ft_strnjoin(3, path_env[i], "/", command_name);
|
|
||||||
if (command_path != NULL && access(command_path, X_OK) != EXIT_SUCCESS)
|
|
||||||
{
|
|
||||||
free(command_path);
|
|
||||||
command_path = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ft_free_split(path_env);
|
|
||||||
return (command_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *resolve_explicit_path(
|
|
||||||
const char *command_name
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (access(command_name, F_OK) != EXIT_SUCCESS)
|
|
||||||
return (NULL);
|
|
||||||
if (access(command_name, X_OK) != EXIT_SUCCESS)
|
|
||||||
return (NULL);
|
|
||||||
return (ft_strdup(command_name));
|
|
||||||
}
|
|
||||||
|
|
||||||
char *executor_resolve_command_path(
|
|
||||||
const t_command *command,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
const char *command_name;
|
|
||||||
|
|
||||||
if (command == NULL || command->argv == NULL || command->argv[0] == NULL)
|
|
||||||
return (NULL);
|
|
||||||
command_name = command->argv[0];
|
|
||||||
if (is_path_explicit(command_name))
|
|
||||||
return (resolve_explicit_path(command_name));
|
|
||||||
return (resolve_path_from_env(command_name, minishell));
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* pipeline_helpers.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/11 00:00:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/11 00:00:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "executor.h"
|
|
||||||
#include "builtins.h"
|
|
||||||
|
|
||||||
int executor_create_pipe_if_needed(
|
|
||||||
t_list *current_command,
|
|
||||||
t_pipeline *pipeline
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (!current_command->next)
|
|
||||||
return (0);
|
|
||||||
if (pipe(pipeline->pipefd) == PIPE_ERROR)
|
|
||||||
return (perror("pipe"), PIPE_ERROR);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool executor_is_builtin_command(
|
|
||||||
const t_command *command,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (command == NULL || command->argv == NULL || command->argv[0] == NULL)
|
|
||||||
return (false);
|
|
||||||
return (is_builtin(command->argv[0], minishell));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool executor_is_fork_required(
|
|
||||||
t_list *current_command,
|
|
||||||
const t_pipeline *pipeline,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
const t_command *command;
|
|
||||||
|
|
||||||
command = current_command->content;
|
|
||||||
return (pipeline->prev_read_fd != -1 || current_command->next != NULL
|
|
||||||
|| !executor_is_builtin_command(command, minishell));
|
|
||||||
}
|
|
||||||
|
|
||||||
void executor_setup_child_input(
|
|
||||||
t_pipeline *pipeline
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (pipeline->prev_read_fd != -1)
|
|
||||||
{
|
|
||||||
dup2(pipeline->prev_read_fd, STDIN_FILENO);
|
|
||||||
close(pipeline->prev_read_fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void executor_setup_child_output(
|
|
||||||
t_list *current_command,
|
|
||||||
t_pipeline *pipeline
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (current_command->next)
|
|
||||||
{
|
|
||||||
dup2(pipeline->pipefd[WRITE_PIPE], STDOUT_FILENO);
|
|
||||||
close(pipeline->pipefd[READ_PIPE]);
|
|
||||||
close(pipeline->pipefd[WRITE_PIPE]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* process_helpers.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/11 00:00:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/11 00:00:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "executor.h"
|
|
||||||
|
|
||||||
static void set_signal_exit_status(
|
|
||||||
int status,
|
|
||||||
uint8_t *exit_status
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (WTERMSIG(status) == SIGINT)
|
|
||||||
write(STDOUT_FILENO, "\n", 1);
|
|
||||||
else if (WTERMSIG(status) == SIGQUIT)
|
|
||||||
{
|
|
||||||
if (WCOREDUMP(status))
|
|
||||||
write(STDERR_FILENO, "Quit (core dumped)\n", 19);
|
|
||||||
else
|
|
||||||
write(STDERR_FILENO, "Quit\n", 5);
|
|
||||||
}
|
|
||||||
*exit_status = 128 + WTERMSIG(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_last_child_status(
|
|
||||||
int status,
|
|
||||||
uint8_t *exit_status
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (WIFEXITED(status))
|
|
||||||
*exit_status = WEXITSTATUS(status);
|
|
||||||
else if (WIFSIGNALED(status))
|
|
||||||
set_signal_exit_status(status, exit_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
void executor_child_process(
|
|
||||||
t_list *current_command,
|
|
||||||
t_pipeline *pipeline,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
uint8_t exit_status;
|
|
||||||
t_command *command;
|
|
||||||
|
|
||||||
command = current_command->content;
|
|
||||||
minishell_set_child_signals();
|
|
||||||
executor_setup_child_input(pipeline);
|
|
||||||
executor_setup_child_output(current_command, pipeline);
|
|
||||||
if (!executor_apply_redirections(command, NULL, NULL))
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
exit_status = executor_execute_command(command, minishell);
|
|
||||||
exit(exit_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
void executor_parent_cleanup(
|
|
||||||
t_list *current_command,
|
|
||||||
t_pipeline *pipeline
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (pipeline->prev_read_fd != -1)
|
|
||||||
close(pipeline->prev_read_fd);
|
|
||||||
if (current_command->next)
|
|
||||||
{
|
|
||||||
close(pipeline->pipefd[WRITE_PIPE]);
|
|
||||||
pipeline->prev_read_fd = pipeline->pipefd[READ_PIPE];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
pipeline->prev_read_fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t executor_wait_for_children(
|
|
||||||
pid_t last_child_pid
|
|
||||||
)
|
|
||||||
{
|
|
||||||
uint8_t exit_status;
|
|
||||||
int status;
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
exit_status = EXIT_SUCCESS;
|
|
||||||
pid = wait(&status);
|
|
||||||
while (last_child_pid > 0 && pid > 0)
|
|
||||||
{
|
|
||||||
if (pid == last_child_pid)
|
|
||||||
set_last_child_status(status, &exit_status);
|
|
||||||
pid = wait(&status);
|
|
||||||
}
|
|
||||||
return (exit_status);
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* redirections.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/12 00:00:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/12 00:00:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "executor.h"
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
static int open_redirection_target(
|
|
||||||
const t_redirection *redirection
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (redirection->type == TOKEN_REDIRECT_IN)
|
|
||||||
return (open(redirection->target, O_RDONLY));
|
|
||||||
if (redirection->type == TOKEN_REDIRECT_OUT)
|
|
||||||
return (open(redirection->target, O_WRONLY | O_CREAT | O_TRUNC, 0644));
|
|
||||||
if (redirection->type == TOKEN_APPEND)
|
|
||||||
return (open(redirection->target, O_WRONLY | O_CREAT | O_APPEND, 0644));
|
|
||||||
errno = EINVAL;
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool backup_stream_if_needed(
|
|
||||||
const t_redirection *redirection,
|
|
||||||
int *saved_stdin,
|
|
||||||
int *saved_stdout
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (redirection->type == TOKEN_REDIRECT_IN && saved_stdin != NULL
|
|
||||||
&& *saved_stdin == -1)
|
|
||||||
{
|
|
||||||
*saved_stdin = dup(STDIN_FILENO);
|
|
||||||
if (*saved_stdin == -1)
|
|
||||||
return (perror("dup"), false);
|
|
||||||
}
|
|
||||||
if ((redirection->type == TOKEN_REDIRECT_OUT
|
|
||||||
|| redirection->type == TOKEN_APPEND) && saved_stdout != NULL
|
|
||||||
&& *saved_stdout == -1)
|
|
||||||
{
|
|
||||||
*saved_stdout = dup(STDOUT_FILENO);
|
|
||||||
if (*saved_stdout == -1)
|
|
||||||
return (perror("dup"), false);
|
|
||||||
}
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool executor_apply_redirections(
|
|
||||||
const t_command *command,
|
|
||||||
int *saved_stdin,
|
|
||||||
int *saved_stdout
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_list *node;
|
|
||||||
t_redirection *redirection;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
if (saved_stdin != NULL)
|
|
||||||
*saved_stdin = -1;
|
|
||||||
if (saved_stdout != NULL)
|
|
||||||
*saved_stdout = -1;
|
|
||||||
if (command == NULL || command->redirections == NULL)
|
|
||||||
return (true);
|
|
||||||
node = command->redirections;
|
|
||||||
while (node != NULL)
|
|
||||||
{
|
|
||||||
redirection = (t_redirection *)node->content;
|
|
||||||
if (redirection == NULL || redirection->target == NULL)
|
|
||||||
return (false);
|
|
||||||
if (!backup_stream_if_needed(redirection, saved_stdin, saved_stdout))
|
|
||||||
return (false);
|
|
||||||
fd = open_redirection_target(redirection);
|
|
||||||
if (fd == -1)
|
|
||||||
return (perror(redirection->target), false);
|
|
||||||
if (redirection->type == TOKEN_REDIRECT_IN
|
|
||||||
&& dup2(fd, STDIN_FILENO) == -1)
|
|
||||||
return (close(fd), perror("dup2"), false);
|
|
||||||
if ((redirection->type == TOKEN_REDIRECT_OUT
|
|
||||||
|| redirection->type == TOKEN_APPEND)
|
|
||||||
&& dup2(fd, STDOUT_FILENO) == -1)
|
|
||||||
return (close(fd), perror("dup2"), false);
|
|
||||||
close(fd);
|
|
||||||
node = node->next;
|
|
||||||
}
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void executor_restore_redirections(
|
|
||||||
int saved_stdin,
|
|
||||||
int saved_stdout
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (saved_stdin != -1)
|
|
||||||
{
|
|
||||||
if (dup2(saved_stdin, STDIN_FILENO) == -1)
|
|
||||||
perror("dup2");
|
|
||||||
close(saved_stdin);
|
|
||||||
}
|
|
||||||
if (saved_stdout != -1)
|
|
||||||
{
|
|
||||||
if (dup2(saved_stdout, STDOUT_FILENO) == -1)
|
|
||||||
perror("dup2");
|
|
||||||
close(saved_stdout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/20 20:51:33 by sede-san #+# #+# */
|
/* Created: 2025/10/20 20:51:33 by sede-san #+# #+# */
|
||||||
/* Updated: 2026/02/14 02:23:17 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 18:48:34 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -20,7 +20,6 @@ void minishell_init(
|
|||||||
char **envp
|
char **envp
|
||||||
){
|
){
|
||||||
ft_bzero(minishell, sizeof(t_minishell));
|
ft_bzero(minishell, sizeof(t_minishell));
|
||||||
set_intp(minishell);
|
|
||||||
set_envp(envp, minishell);
|
set_envp(envp, minishell);
|
||||||
set_builtins(minishell);
|
set_builtins(minishell);
|
||||||
if (minishell->variables.environment == NULL || minishell->builtins == NULL)
|
if (minishell->variables.environment == NULL || minishell->builtins == NULL)
|
||||||
@@ -29,39 +28,35 @@ void minishell_init(
|
|||||||
|
|
||||||
void minishell_run(
|
void minishell_run(
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
)
|
){
|
||||||
{
|
|
||||||
char *line;
|
char *line;
|
||||||
t_list *commands;
|
t_list *commands;
|
||||||
|
|
||||||
if (minishell == NULL)
|
if (minishell == NULL)
|
||||||
|
{
|
||||||
|
minishell->exit_status = EXIT_FAILURE;
|
||||||
return ;
|
return ;
|
||||||
minishell_set_interactive_signals();
|
}
|
||||||
while (!minishell->exit)
|
while (!minishell->exit)
|
||||||
{
|
{
|
||||||
if (isatty(STDIN_FILENO))
|
line = readline(DEFAULT_PS1);
|
||||||
line = readline(get_var("PS1", minishell));
|
if (line != NULL)
|
||||||
else
|
{
|
||||||
line = get_next_line(STDIN_FILENO);
|
|
||||||
handle_sigint_status(minishell);
|
|
||||||
if (handle_eof(line, minishell))
|
|
||||||
continue ;
|
|
||||||
if (*line != '\0')
|
if (*line != '\0')
|
||||||
{
|
{
|
||||||
add_history(line);
|
add_history(line);
|
||||||
commands = parse(line, minishell);
|
commands = parse(line, minishell);
|
||||||
minishell->exit_status = execute(commands, minishell);
|
execute(commands, minishell);
|
||||||
}
|
}
|
||||||
free(line);
|
free(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void minishell_clear(
|
void minishell_clear(
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
){
|
){
|
||||||
rl_clear_history();
|
rl_clear_history();
|
||||||
if (minishell->variables.internal != NULL)
|
|
||||||
ft_hashmap_clear(&minishell->variables.internal, free);
|
|
||||||
if (minishell->variables.environment != NULL)
|
if (minishell->variables.environment != NULL)
|
||||||
ft_hashmap_clear(&minishell->variables.environment, free);
|
ft_hashmap_clear(&minishell->variables.environment, free);
|
||||||
if (minishell->builtins != NULL)
|
if (minishell->builtins != NULL)
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* minishell_helpers.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 21:09:23 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/13 21:09:23 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "minishell.h"
|
|
||||||
#include "core.h"
|
|
||||||
|
|
||||||
void handle_sigint_status(
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (!minishell_consume_sigint())
|
|
||||||
return ;
|
|
||||||
minishell->exit_status = 130;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handle_eof(
|
|
||||||
char *line,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (line != NULL)
|
|
||||||
return (false);
|
|
||||||
if (isatty(STDIN_FILENO))
|
|
||||||
ft_putendl("exit");
|
|
||||||
minishell->exit = true;
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
@@ -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/11 02:06:36 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 20:42:50 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -14,16 +14,11 @@
|
|||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Converts a command line string into a list of tokens.
|
|
||||||
*
|
|
||||||
* @return A list of tokens or NULL on error.
|
|
||||||
*/
|
|
||||||
t_list *lex(
|
t_list *lex(
|
||||||
const char *line
|
const char *line
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
t_list *tokens;
|
t_list *tokens;
|
||||||
t_token *token;
|
t_token *token;
|
||||||
size_t i;
|
size_t i;
|
||||||
@@ -32,38 +27,53 @@ t_list *lex(
|
|||||||
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);
|
||||||
|
// add token to list
|
||||||
|
if (token != NULL)
|
||||||
ft_lstadd_back(&tokens, ft_lstnew(token));
|
ft_lstadd_back(&tokens, ft_lstnew(token));
|
||||||
if (token == NULL)
|
|
||||||
{
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
static t_token *tokenize(
|
|
||||||
const char *line,
|
|
||||||
size_t *start
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_token *token;
|
t_token *token;
|
||||||
t_token_type type;
|
t_token_type type;
|
||||||
|
|
||||||
|
token = NULL;
|
||||||
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(const char *str)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
static const t_map_entry tokens[TOKENS_COUNT] = {
|
||||||
|
{PIPE_STR, (void *)TOKEN_PIPE},
|
||||||
|
{REDIRECT_IN_STR, (void *)TOKEN_REDIRECT_IN},
|
||||||
|
{REDIRECT_OUT_STR, (void *)TOKEN_REDIRECT_OUT},
|
||||||
|
{APPEND_STR, (void *)TOKEN_APPEND},
|
||||||
|
{HEREDOC_STR, (void *)TOKEN_HEREDOC}
|
||||||
|
};
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < TOKENS_COUNT)
|
||||||
|
{
|
||||||
|
if (ft_strcmp(str, tokens[i].key) == 0)
|
||||||
|
return ((t_token_type)tokens[i].value);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return (TOKEN_WORD);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* lexer_reader.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 21:13:23 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/13 21:13:23 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
static inline bool is_meta(char c);
|
|
||||||
|
|
||||||
t_token *read_token(
|
|
||||||
t_token_type type,
|
|
||||||
const char *line,
|
|
||||||
size_t *i
|
|
||||||
)
|
|
||||||
{
|
|
||||||
const size_t start = *i;
|
|
||||||
size_t end;
|
|
||||||
|
|
||||||
while (is_meta(line[*i]))
|
|
||||||
(*i)++;
|
|
||||||
end = *i;
|
|
||||||
while (ft_isspace(line[*i]))
|
|
||||||
(*i)++;
|
|
||||||
return (token_new(type, ft_substr(line, start, end - start)));
|
|
||||||
}
|
|
||||||
|
|
||||||
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')
|
|
||||||
{
|
|
||||||
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 == '>');
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* lexer_token.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 21:12:49 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/13 21:12:49 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
t_token_type get_token_type(
|
|
||||||
const char *str
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (str == NULL || str[0] == '\0')
|
|
||||||
return (TOKEN_WORD);
|
|
||||||
if (str[0] == '|')
|
|
||||||
return (TOKEN_PIPE);
|
|
||||||
if (str[0] == '<')
|
|
||||||
{
|
|
||||||
if (str[1] == '<')
|
|
||||||
return (TOKEN_HEREDOC);
|
|
||||||
return (TOKEN_REDIRECT_IN);
|
|
||||||
}
|
|
||||||
if (str[0] == '>')
|
|
||||||
{
|
|
||||||
if (str[1] == '>')
|
|
||||||
return (TOKEN_APPEND);
|
|
||||||
return (TOKEN_REDIRECT_OUT);
|
|
||||||
}
|
|
||||||
return (TOKEN_WORD);
|
|
||||||
}
|
|
||||||
|
|
||||||
t_token *token_new(
|
|
||||||
t_token_type type,
|
|
||||||
char *text
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_token *token;
|
|
||||||
|
|
||||||
token = (t_token *)malloc(sizeof(t_token));
|
|
||||||
if (token == NULL)
|
|
||||||
return (NULL);
|
|
||||||
token->type = type;
|
|
||||||
token->value = text;
|
|
||||||
if (token->type == TOKEN_WORD && token->value == NULL)
|
|
||||||
{
|
|
||||||
free(token);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
return (token);
|
|
||||||
}
|
|
||||||
|
|
||||||
void token_clear(
|
|
||||||
t_token *token
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (token != NULL)
|
|
||||||
{
|
|
||||||
free(token->value);
|
|
||||||
free(token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,89 +6,295 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/22 18:37:38 by sede-san #+# #+# */
|
/* Created: 2025/10/22 18:37:38 by sede-san #+# #+# */
|
||||||
/* Updated: 2026/02/13 21:22:06 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 18:50:43 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "errors.h"
|
|
||||||
|
|
||||||
static t_list *parse_tokens(t_list *tokens);
|
// parse exclusive
|
||||||
|
static char *extract_next_command(char *line, size_t *index);
|
||||||
|
static char *trim_whitespaces(char *line, size_t *start, size_t *end);
|
||||||
|
// static void set_pipes(t_list *commands);
|
||||||
|
|
||||||
|
// common
|
||||||
|
static void find_boundary(char *line, size_t *index, char bound_char);
|
||||||
|
|
||||||
|
// command exclusive
|
||||||
|
static t_command *cmdnew(char *line, t_minishell *minishell);
|
||||||
|
static void set_argv(t_command *command, char *line, t_minishell *minishell);
|
||||||
|
static void expand_envs(char *arg, t_minishell *minishell);
|
||||||
|
static char **lst_to_argv(t_list *argv_list);
|
||||||
|
static void set_argc(t_command *command);
|
||||||
|
static void set_infile(t_command *command);
|
||||||
|
static void set_outfile(t_command *command);
|
||||||
|
static void set_path(t_command *command, t_minishell *minishell);
|
||||||
|
static u_int8_t path_is_solved(char *cmd_name, t_minishell *msh);
|
||||||
|
static char *solve_path(char *command_name, t_minishell *minishell);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Converts a command line string into a list of commands.
|
|
||||||
*
|
|
||||||
* @param line The command line string to parse.
|
|
||||||
* @param minishell The minishell instance.
|
|
||||||
*
|
|
||||||
* @return A list of commands or `NULL` on error.
|
|
||||||
*/
|
|
||||||
t_list *parse(
|
t_list *parse(
|
||||||
char *line,
|
char *line,
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
t_list *commands;
|
t_list *commands;
|
||||||
t_list *tokens;
|
t_list *tokens;
|
||||||
|
t_command *command;
|
||||||
|
char *command_str;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
if (line == NULL)
|
if (line == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
tokens = lex(line);
|
|
||||||
commands = parse_tokens(tokens);
|
|
||||||
ft_lstclear(&tokens, (void (*)(void *))token_clear);
|
|
||||||
expand(&commands, minishell);
|
|
||||||
return (commands);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Converts a list of tokens into a list of commands.
|
|
||||||
*
|
|
||||||
* @param tokens The list of tokens to parse.
|
|
||||||
*
|
|
||||||
* @return A list of commands or `NULL` on error.
|
|
||||||
*/
|
|
||||||
static t_list *parse_tokens(
|
|
||||||
t_list *tokens
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_list *commands;
|
|
||||||
t_command *command;
|
|
||||||
t_list *current_token;
|
|
||||||
t_list *new_command;
|
|
||||||
|
|
||||||
if (tokens == NULL)
|
|
||||||
return (NULL);
|
|
||||||
commands = NULL;
|
commands = NULL;
|
||||||
current_token = tokens;
|
i = 0;
|
||||||
if (((t_token *)current_token->content)->type == TOKEN_PIPE)
|
while (line[i] != '\0')
|
||||||
return (syntax_error_unexpected_token(
|
|
||||||
(t_token *)current_token->content), NULL);
|
|
||||||
while (current_token != NULL)
|
|
||||||
{
|
{
|
||||||
command = command_new(¤t_token);
|
tokens = tokenize();
|
||||||
if (command == NULL)
|
command_str = extract_next_command(line, &i);
|
||||||
|
if (command_str != NULL)
|
||||||
{
|
{
|
||||||
ft_lstclear(&commands, (void (*)(void *))command_clear);
|
command = cmdnew(command_str, minishell);
|
||||||
return (NULL);
|
free(command_str);
|
||||||
}
|
if (command != NULL)
|
||||||
new_command = ft_lstnew(command);
|
ft_lstadd_back(&commands, ft_lstnew(command));
|
||||||
if (new_command == NULL)
|
|
||||||
{
|
|
||||||
command_clear(command);
|
|
||||||
ft_lstclear(&commands, (void (*)(void *))command_clear);
|
|
||||||
return (malloc_error(), NULL);
|
|
||||||
}
|
|
||||||
ft_lstadd_back(&commands, new_command);
|
|
||||||
if (current_token != NULL)
|
|
||||||
{
|
|
||||||
if (current_token->next == NULL)
|
|
||||||
{
|
|
||||||
ft_lstclear(&commands, (void (*)(void *))command_clear);
|
|
||||||
return (syntax_error_unexpected_token(
|
|
||||||
(t_token *)current_token->content), NULL);
|
|
||||||
}
|
|
||||||
current_token = current_token->next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// set_pipes(commands);
|
||||||
return (commands);
|
return (commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static void set_pipes(
|
||||||
|
// t_list *commands
|
||||||
|
// ) {
|
||||||
|
// t_list *current_command;
|
||||||
|
// t_list *previous_command;
|
||||||
|
// t_list *next_command;
|
||||||
|
// t_command *command;
|
||||||
|
|
||||||
|
// previous_command = NULL;
|
||||||
|
// current_command = commands;
|
||||||
|
// while (current_command != NULL)
|
||||||
|
// {
|
||||||
|
// command = (t_command *)current_command->content;
|
||||||
|
// if (previous_command != NULL)
|
||||||
|
// command->piped_from = (t_command *)previous_command->content;
|
||||||
|
// next_command = current_command->next;
|
||||||
|
// if (next_command != NULL)
|
||||||
|
// command->piped_to = (t_command *)next_command->content;
|
||||||
|
// previous_command = current_command;
|
||||||
|
// current_command = current_command->next;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
static char *extract_next_command(
|
||||||
|
char *line,
|
||||||
|
size_t *index
|
||||||
|
) {
|
||||||
|
char *command_str;
|
||||||
|
size_t start;
|
||||||
|
size_t end;
|
||||||
|
|
||||||
|
start = *index;
|
||||||
|
find_boundary(line, index, '|');
|
||||||
|
end = *index;
|
||||||
|
command_str = trim_whitespaces(line, &start, &end);
|
||||||
|
while (line[*index] == '|' || ft_isspace(line[*index]))
|
||||||
|
(*index)++;
|
||||||
|
return (command_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void find_boundary(
|
||||||
|
char *line,
|
||||||
|
size_t *index,
|
||||||
|
char bound_char
|
||||||
|
) {
|
||||||
|
bool in_single_quote;
|
||||||
|
bool in_double_quote;
|
||||||
|
|
||||||
|
in_single_quote = false;
|
||||||
|
in_double_quote = false;
|
||||||
|
while (line[*index] != '\0')
|
||||||
|
{
|
||||||
|
if (line[*index] == '\'' && !in_double_quote)
|
||||||
|
in_single_quote = !in_single_quote;
|
||||||
|
else if (line[*index] == '"' && !in_single_quote)
|
||||||
|
in_double_quote = !in_double_quote;
|
||||||
|
if (line[*index] == bound_char && !in_single_quote && !in_double_quote)
|
||||||
|
break ;
|
||||||
|
(*index)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *trim_whitespaces(
|
||||||
|
char *line,
|
||||||
|
size_t *start,
|
||||||
|
size_t *end
|
||||||
|
) {
|
||||||
|
while (*start < *end && ft_isspace(line[*start]))
|
||||||
|
(*start)++;
|
||||||
|
while (*end > *start && ft_isspace(line[*end - 1]))
|
||||||
|
(*end)--;
|
||||||
|
if (*end > *start)
|
||||||
|
return (ft_substr(line, *start, *end - *start));
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static t_command *cmdnew(
|
||||||
|
char *line,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
t_command *command;
|
||||||
|
|
||||||
|
command = (t_command *)ft_calloc(1, sizeof(t_command));
|
||||||
|
if (!command)
|
||||||
|
return (NULL);
|
||||||
|
// resolve_heredoc
|
||||||
|
set_argv(command, line, minishell);
|
||||||
|
if (!command->argv)
|
||||||
|
{
|
||||||
|
free(command);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
set_argc(command);
|
||||||
|
set_infile(command);
|
||||||
|
set_outfile(command);
|
||||||
|
set_path(command, minishell);
|
||||||
|
return (command);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_argv(
|
||||||
|
t_command *command,
|
||||||
|
char *line,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
t_list *argv_list;
|
||||||
|
char *arg;
|
||||||
|
size_t i;
|
||||||
|
size_t start;
|
||||||
|
size_t end;
|
||||||
|
|
||||||
|
if (line == NULL)
|
||||||
|
return ;
|
||||||
|
i = 0;
|
||||||
|
argv_list = NULL;
|
||||||
|
while (line[i] != '\0')
|
||||||
|
{
|
||||||
|
start = i;
|
||||||
|
find_boundary(line, &i, ' ');
|
||||||
|
end = i;
|
||||||
|
arg = trim_whitespaces(line, &start, &end);
|
||||||
|
expand_envs(arg, minishell);
|
||||||
|
if (arg != NULL)
|
||||||
|
ft_lstadd_back(&argv_list, ft_lstnew(arg));
|
||||||
|
while (ft_isspace(line[i]))
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
command->argv = lst_to_argv(argv_list);
|
||||||
|
ft_lstclear(&argv_list, free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void expand_envs(
|
||||||
|
char *arg,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
// TODO
|
||||||
|
(void)arg;
|
||||||
|
(void)minishell;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char **lst_to_argv(
|
||||||
|
t_list *argv_list
|
||||||
|
) {
|
||||||
|
char **argv;
|
||||||
|
t_list *current_arg;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
argv = (char **)ft_calloc(ft_lstsize(argv_list) + 1, sizeof(char *));
|
||||||
|
if (!argv)
|
||||||
|
return (NULL);
|
||||||
|
i = 0;
|
||||||
|
current_arg = argv_list;
|
||||||
|
while (current_arg != NULL)
|
||||||
|
{
|
||||||
|
argv[i] = ft_strdup((char *)current_arg->content);
|
||||||
|
i++;
|
||||||
|
current_arg = current_arg->next;
|
||||||
|
}
|
||||||
|
return (argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_argc(
|
||||||
|
t_command *command
|
||||||
|
) {
|
||||||
|
int argc;
|
||||||
|
|
||||||
|
argc = 0;
|
||||||
|
while (command->argv[argc] != NULL)
|
||||||
|
argc++;
|
||||||
|
command->argc = argc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_infile(
|
||||||
|
t_command *command
|
||||||
|
) {
|
||||||
|
// test_infile
|
||||||
|
command->infile = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_outfile(
|
||||||
|
t_command *command
|
||||||
|
) {
|
||||||
|
// test_outfile
|
||||||
|
command->outfile = STDOUT_FILENO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_path(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
char *command_path;
|
||||||
|
char *command_name;
|
||||||
|
|
||||||
|
command_name = command->argv[0];
|
||||||
|
if (!path_is_solved(command_name, minishell))
|
||||||
|
command_path = solve_path(command_name, minishell);
|
||||||
|
else
|
||||||
|
command_path = ft_strdup(command_name);
|
||||||
|
command->path = command_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *solve_path(
|
||||||
|
char *command_name,
|
||||||
|
t_minishell *minishell
|
||||||
|
){
|
||||||
|
char *command_path;
|
||||||
|
char **path_env;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
path_env = ft_split(get_env("PATH", minishell), ':');
|
||||||
|
if (!path_env)
|
||||||
|
return (NULL);
|
||||||
|
command_path = NULL;
|
||||||
|
i = -1;
|
||||||
|
while (!command_path && path_env[++i])
|
||||||
|
{
|
||||||
|
command_path = ft_strnjoin(3, path_env[i], "/", command_name);
|
||||||
|
if (command_path != NULL && access(command_path, F_OK) != EXIT_SUCCESS)
|
||||||
|
{
|
||||||
|
free(command_path);
|
||||||
|
command_path = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ft_free_split(path_env);
|
||||||
|
return (command_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u_int8_t path_is_solved(
|
||||||
|
char *command_name,
|
||||||
|
t_minishell *minishell
|
||||||
|
){
|
||||||
|
return (ft_strncmp(command_name, "/", 1) == 0
|
||||||
|
|| (command_name[1] && ft_strncmp(command_name, "./", 2) == 0)
|
||||||
|
|| (command_name[2] && ft_strncmp(command_name, "../", 3) == 0)
|
||||||
|
|| is_builtin(command_name, minishell)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,120 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* parser_command.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 21:26:42 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/13 21:26:42 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
static t_list *ft_lstfind(t_list *lst, bool (*pre)(void *));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates a new command from a list of tokens.
|
|
||||||
*
|
|
||||||
* @param tokens The list of tokens to create the command from.
|
|
||||||
*
|
|
||||||
* @return A new command or NULL on error.
|
|
||||||
*
|
|
||||||
* @note The `tokens` pointer is moved to the next command's tokens.
|
|
||||||
*/
|
|
||||||
t_command *command_new(
|
|
||||||
t_list **tokens
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_command *command;
|
|
||||||
t_list *current_token;
|
|
||||||
t_list *delimiter_token;
|
|
||||||
|
|
||||||
command = (t_command *)ft_calloc(1, sizeof(t_command));
|
|
||||||
if (command == NULL)
|
|
||||||
return (NULL);
|
|
||||||
current_token = *tokens;
|
|
||||||
delimiter_token = ft_lstfind(current_token, (bool (*)(void *))is_pipe);
|
|
||||||
while (command != NULL && current_token != delimiter_token)
|
|
||||||
command_add_tokens(&command, ¤t_token);
|
|
||||||
*tokens = current_token;
|
|
||||||
return (command);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Adds a token to a command, updating the command's arguments and
|
|
||||||
* redirections as necessary.
|
|
||||||
*
|
|
||||||
* @param command The command to add the token to.
|
|
||||||
* @param tokens The list of tokens to add to the command.
|
|
||||||
*
|
|
||||||
* @note The `command` pointer can be free'd if there is an error while adding
|
|
||||||
* the token.
|
|
||||||
*/
|
|
||||||
void command_add_tokens(
|
|
||||||
t_command **command,
|
|
||||||
t_list **tokens
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_token *token;
|
|
||||||
|
|
||||||
token = (t_token *)(*tokens)->content;
|
|
||||||
if (is_redirection(token))
|
|
||||||
redirection_add(tokens, token, command);
|
|
||||||
else
|
|
||||||
words_add(tokens, command);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if a token is a redirection token.
|
|
||||||
*
|
|
||||||
* @param token The token to check.
|
|
||||||
*
|
|
||||||
* @return `true` if the token is a redirection token, `false` otherwise.
|
|
||||||
*/
|
|
||||||
bool is_redirection(
|
|
||||||
t_token *token
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return (token->type == TOKEN_REDIRECT_IN
|
|
||||||
|| token->type == TOKEN_REDIRECT_OUT
|
|
||||||
|| token->type == TOKEN_APPEND
|
|
||||||
|| token->type == TOKEN_HEREDOC);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if a token is a pipe token.
|
|
||||||
*
|
|
||||||
* @param token The token to check.
|
|
||||||
*
|
|
||||||
* @return `true` if the token is a pipe token, `false` otherwise.
|
|
||||||
*/
|
|
||||||
bool is_pipe(
|
|
||||||
t_token *token
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return (token->type == TOKEN_PIPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Finds a node in a linked list that satisfies a given predicate.
|
|
||||||
*
|
|
||||||
* @param lst The linked list to search through.
|
|
||||||
* @param pre The predicate function to apply to each node's content.
|
|
||||||
*
|
|
||||||
* @returns The first node that satisfies the predicate or `NULL` if no such
|
|
||||||
* node exists or if the list is `NULL`.
|
|
||||||
*/
|
|
||||||
static t_list *ft_lstfind(
|
|
||||||
t_list *lst,
|
|
||||||
bool (*pre)(void *))
|
|
||||||
{
|
|
||||||
while (lst != NULL)
|
|
||||||
{
|
|
||||||
if (pre(lst->content))
|
|
||||||
return (lst);
|
|
||||||
lst = lst->next;
|
|
||||||
}
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* parser_expand.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 21:24:45 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 06:56:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "parser_expand_internal.h"
|
|
||||||
|
|
||||||
static void command_clear_argv_expand(
|
|
||||||
t_command *command
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (i < command->argc)
|
|
||||||
{
|
|
||||||
free(command->argv[i]);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
free(command->argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool command_set_expanded_argv(
|
|
||||||
t_command *command,
|
|
||||||
t_list *expanded_args,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char **argv;
|
|
||||||
t_list *current;
|
|
||||||
int argc;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
argc = ft_lstsize(expanded_args);
|
|
||||||
argv = (char **)malloc(sizeof(char *) * (argc + 1));
|
|
||||||
if (argv == NULL)
|
|
||||||
return (ft_lstclear(&expanded_args, free),
|
|
||||||
parser_expand_malloc_error(minishell), false);
|
|
||||||
i = 0;
|
|
||||||
current = expanded_args;
|
|
||||||
while (current != NULL)
|
|
||||||
{
|
|
||||||
argv[i++] = (char *)current->content;
|
|
||||||
current = current->next;
|
|
||||||
}
|
|
||||||
argv[i] = NULL;
|
|
||||||
ft_lstclear_nodes(&expanded_args);
|
|
||||||
command_clear_argv_expand(command);
|
|
||||||
command->argc = argc;
|
|
||||||
command->argv = argv;
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool expand_argv(
|
|
||||||
t_command *command,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
t_list *expanded_args;
|
|
||||||
t_list *fields;
|
|
||||||
t_list *last;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
expanded_args = NULL;
|
|
||||||
while (i < command->argc)
|
|
||||||
{
|
|
||||||
fields = NULL;
|
|
||||||
if (!parser_expand_word_fields(command->argv[i], minishell,
|
|
||||||
true, &fields))
|
|
||||||
return (ft_lstclear(&expanded_args, free), false);
|
|
||||||
if (expanded_args == NULL)
|
|
||||||
expanded_args = fields;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
last = ft_lstlast(expanded_args);
|
|
||||||
if (last != NULL)
|
|
||||||
last->next = fields;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return (command_set_expanded_argv(command, expanded_args, minishell));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool expand_redirections(
|
|
||||||
t_list *redirections,
|
|
||||||
t_minishell *minishell,
|
|
||||||
bool expand_vars
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_redirection *redirection;
|
|
||||||
char *expanded;
|
|
||||||
|
|
||||||
while (redirections != NULL)
|
|
||||||
{
|
|
||||||
redirection = (t_redirection *)redirections->content;
|
|
||||||
expanded = parser_expand_word(redirection->target, minishell,
|
|
||||||
expand_vars);
|
|
||||||
if (expanded == NULL)
|
|
||||||
return (false);
|
|
||||||
free(redirection->target);
|
|
||||||
redirection->target = expanded;
|
|
||||||
redirections = redirections->next;
|
|
||||||
}
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void expand(
|
|
||||||
t_list **commands,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_list *current;
|
|
||||||
t_command *command;
|
|
||||||
|
|
||||||
if (commands == NULL || *commands == NULL)
|
|
||||||
return ;
|
|
||||||
current = *commands;
|
|
||||||
while (current != NULL)
|
|
||||||
{
|
|
||||||
command = (t_command *)current->content;
|
|
||||||
if (!expand_argv(command, minishell)
|
|
||||||
|| !expand_redirections(command->redirections, minishell, true)
|
|
||||||
|| !expand_redirections(command->heredocs, minishell, false))
|
|
||||||
{
|
|
||||||
ft_lstclear(commands, (void (*)(void *))command_clear);
|
|
||||||
*commands = NULL;
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
current = current->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* parser_expand_fields.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/14 13:15:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 13:15:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "parser_expand_internal.h"
|
|
||||||
|
|
||||||
static t_fields_ctx init_fields_ctx(
|
|
||||||
t_list **fields,
|
|
||||||
char **current,
|
|
||||||
t_field_state *state
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_fields_ctx ctx;
|
|
||||||
|
|
||||||
ctx.fields = fields;
|
|
||||||
ctx.current = current;
|
|
||||||
ctx.touched = &state->touched;
|
|
||||||
ctx.in_single_quote = &state->in_single_quote;
|
|
||||||
ctx.in_double_quote = &state->in_double_quote;
|
|
||||||
return (ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool finish_word_fields(
|
|
||||||
t_fields_ctx ctx
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (*ctx.in_single_quote || *ctx.in_double_quote)
|
|
||||||
{
|
|
||||||
ft_lstclear(ctx.fields, free);
|
|
||||||
free(*ctx.current);
|
|
||||||
syntax_error_unexpected_token(NULL);
|
|
||||||
ctx.minishell->exit_status = 2;
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
if ((*ctx.touched || (*ctx.current)[0] != '\0')
|
|
||||||
&& !parser_fields_push_field(ctx))
|
|
||||||
return (false);
|
|
||||||
free(*ctx.current);
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool process_word_fields(
|
|
||||||
const char *word,
|
|
||||||
t_fields_ctx ctx
|
|
||||||
)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (word[i] != '\0')
|
|
||||||
{
|
|
||||||
if (!parser_fields_step(word, &i, ctx))
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parser_expand_word_fields(
|
|
||||||
const char *word,
|
|
||||||
t_minishell *minishell,
|
|
||||||
bool expand_vars,
|
|
||||||
t_list **fields
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *current;
|
|
||||||
t_field_state state;
|
|
||||||
t_fields_ctx ctx;
|
|
||||||
|
|
||||||
*fields = NULL;
|
|
||||||
current = ft_strdup("");
|
|
||||||
if (current == NULL)
|
|
||||||
return (parser_expand_malloc_error(minishell), false);
|
|
||||||
state.touched = false;
|
|
||||||
state.in_single_quote = false;
|
|
||||||
state.in_double_quote = false;
|
|
||||||
ctx = init_fields_ctx(fields, ¤t, &state);
|
|
||||||
ctx.minishell = minishell;
|
|
||||||
ctx.expand_vars = expand_vars;
|
|
||||||
if (!process_word_fields(word, ctx))
|
|
||||||
return (ft_lstclear(fields, free), free(current), false);
|
|
||||||
return (finish_word_fields(ctx));
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* parser_expand_fields_step.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/14 14:12:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 14:12:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "parser_expand_internal.h"
|
|
||||||
|
|
||||||
static bool handle_quote_char(
|
|
||||||
const char *word,
|
|
||||||
size_t *i,
|
|
||||||
t_fields_ctx ctx
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (word[*i] == '\'' && !*ctx.in_double_quote)
|
|
||||||
{
|
|
||||||
*ctx.in_single_quote = !*ctx.in_single_quote;
|
|
||||||
*ctx.touched = true;
|
|
||||||
(*i)++;
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
if (word[*i] == '\"' && !*ctx.in_single_quote)
|
|
||||||
{
|
|
||||||
*ctx.in_double_quote = !*ctx.in_double_quote;
|
|
||||||
*ctx.touched = true;
|
|
||||||
(*i)++;
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool skip_dollar_quote_prefix(
|
|
||||||
const char *word,
|
|
||||||
size_t *i,
|
|
||||||
t_fields_ctx ctx
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (word[*i] == '$' && !*ctx.in_single_quote && !*ctx.in_double_quote
|
|
||||||
&& word[*i + 1] != '\0'
|
|
||||||
&& (word[*i + 1] == '\'' || word[*i + 1] == '\"'))
|
|
||||||
{
|
|
||||||
(*i)++;
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool expand_dollar_token(
|
|
||||||
const char *word,
|
|
||||||
size_t *i,
|
|
||||||
t_fields_ctx ctx,
|
|
||||||
bool *handled
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *expanded;
|
|
||||||
|
|
||||||
*handled = false;
|
|
||||||
if (word[*i] != '$' || *ctx.in_single_quote || !ctx.expand_vars)
|
|
||||||
return (true);
|
|
||||||
*handled = true;
|
|
||||||
expanded = parser_expand_variable(word, i, ctx.minishell);
|
|
||||||
if (expanded == NULL)
|
|
||||||
return (false);
|
|
||||||
if (!*ctx.in_double_quote
|
|
||||||
&& !parser_fields_expand_unquoted_value(ctx, expanded))
|
|
||||||
return (free(expanded), false);
|
|
||||||
if (*ctx.in_double_quote && !parser_fields_append_text(ctx.current,
|
|
||||||
expanded, ctx.minishell))
|
|
||||||
return (free(expanded), false);
|
|
||||||
if (*ctx.in_double_quote && expanded[0] != '\0')
|
|
||||||
*ctx.touched = true;
|
|
||||||
free(expanded);
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parser_fields_step(
|
|
||||||
const char *word,
|
|
||||||
size_t *i,
|
|
||||||
t_fields_ctx ctx
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char value[2];
|
|
||||||
bool handled;
|
|
||||||
|
|
||||||
if (handle_quote_char(word, i, ctx))
|
|
||||||
return (true);
|
|
||||||
if (skip_dollar_quote_prefix(word, i, ctx))
|
|
||||||
return (true);
|
|
||||||
if (!expand_dollar_token(word, i, ctx, &handled))
|
|
||||||
return (false);
|
|
||||||
if (handled)
|
|
||||||
return (true);
|
|
||||||
value[0] = word[(*i)++];
|
|
||||||
value[1] = '\0';
|
|
||||||
if (!parser_fields_append_text(ctx.current, value, ctx.minishell))
|
|
||||||
return (false);
|
|
||||||
*ctx.touched = true;
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* parser_expand_fields_utils.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/14 14:12:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 14:12:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "parser_expand_internal.h"
|
|
||||||
|
|
||||||
bool parser_fields_append_text(
|
|
||||||
char **current,
|
|
||||||
const char *value,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *joined;
|
|
||||||
|
|
||||||
joined = ft_strnjoin(2, *current, (char *)value);
|
|
||||||
if (joined == NULL)
|
|
||||||
{
|
|
||||||
free(*current);
|
|
||||||
*current = NULL;
|
|
||||||
return (parser_expand_malloc_error(minishell), false);
|
|
||||||
}
|
|
||||||
free(*current);
|
|
||||||
*current = joined;
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_ifs_char(
|
|
||||||
char c
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return (c == ' ' || c == '\t' || c == '\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parser_fields_push_field(
|
|
||||||
t_fields_ctx ctx
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_list *node;
|
|
||||||
|
|
||||||
node = ft_lstnew(*ctx.current);
|
|
||||||
if (node == NULL)
|
|
||||||
{
|
|
||||||
free(*ctx.current);
|
|
||||||
*ctx.current = NULL;
|
|
||||||
return (parser_expand_malloc_error(ctx.minishell), false);
|
|
||||||
}
|
|
||||||
ft_lstadd_back(ctx.fields, node);
|
|
||||||
*ctx.current = ft_strdup("");
|
|
||||||
if (*ctx.current == NULL)
|
|
||||||
return (ft_lstclear(ctx.fields, free),
|
|
||||||
parser_expand_malloc_error(ctx.minishell), false);
|
|
||||||
*ctx.touched = false;
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void skip_ifs(
|
|
||||||
const char *expanded,
|
|
||||||
size_t *i
|
|
||||||
)
|
|
||||||
{
|
|
||||||
while (expanded[*i] != '\0' && is_ifs_char(expanded[*i]))
|
|
||||||
(*i)++;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parser_fields_expand_unquoted_value(
|
|
||||||
t_fields_ctx ctx,
|
|
||||||
const char *expanded
|
|
||||||
)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
char value[2];
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (expanded[i] != '\0')
|
|
||||||
{
|
|
||||||
if (is_ifs_char(expanded[i]))
|
|
||||||
{
|
|
||||||
if ((*ctx.touched || (*ctx.current)[0] != '\0')
|
|
||||||
&& !parser_fields_push_field(ctx))
|
|
||||||
return (false);
|
|
||||||
skip_ifs(expanded, &i);
|
|
||||||
continue ;
|
|
||||||
}
|
|
||||||
value[0] = expanded[i];
|
|
||||||
value[1] = '\0';
|
|
||||||
if (!parser_fields_append_text(ctx.current, value, ctx.minishell))
|
|
||||||
return (false);
|
|
||||||
*ctx.touched = true;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* parser_expand_internal.h :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/14 06:56:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 06:56:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#ifndef PARSER_EXPAND_INTERNAL_H
|
|
||||||
# define PARSER_EXPAND_INTERNAL_H
|
|
||||||
|
|
||||||
# include "parser.h"
|
|
||||||
# include "errors.h"
|
|
||||||
# include "variables.h"
|
|
||||||
|
|
||||||
typedef struct s_fields_ctx t_fields_ctx;
|
|
||||||
|
|
||||||
void parser_expand_malloc_error(t_minishell *minishell);
|
|
||||||
char *parser_expand_variable(const char *word, size_t *i,
|
|
||||||
t_minishell *minishell);
|
|
||||||
char *parser_expand_word(const char *word, t_minishell *minishell,
|
|
||||||
bool expand_vars);
|
|
||||||
bool parser_expand_word_fields(const char *word, t_minishell *minishell,
|
|
||||||
bool expand_vars, t_list **fields);
|
|
||||||
bool parser_fields_append_text(char **current, const char *value,
|
|
||||||
t_minishell *minishell);
|
|
||||||
bool parser_fields_push_field(t_fields_ctx ctx);
|
|
||||||
bool parser_fields_expand_unquoted_value(t_fields_ctx ctx,
|
|
||||||
const char *expanded);
|
|
||||||
bool parser_fields_step(const char *word, size_t *i, t_fields_ctx ctx);
|
|
||||||
|
|
||||||
typedef struct s_word_ctx
|
|
||||||
{
|
|
||||||
char **result;
|
|
||||||
bool *in_single_quote;
|
|
||||||
bool *in_double_quote;
|
|
||||||
t_minishell *minishell;
|
|
||||||
bool expand_vars;
|
|
||||||
} t_word_ctx;
|
|
||||||
|
|
||||||
typedef struct s_fields_ctx
|
|
||||||
{
|
|
||||||
t_list **fields;
|
|
||||||
char **current;
|
|
||||||
bool *touched;
|
|
||||||
bool *in_single_quote;
|
|
||||||
bool *in_double_quote;
|
|
||||||
t_minishell *minishell;
|
|
||||||
bool expand_vars;
|
|
||||||
} t_fields_ctx;
|
|
||||||
|
|
||||||
typedef struct s_field_state
|
|
||||||
{
|
|
||||||
bool touched;
|
|
||||||
bool in_single_quote;
|
|
||||||
bool in_double_quote;
|
|
||||||
} t_field_state;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* parser_expand_variable.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/14 06:56:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 06:56:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "parser_expand_internal.h"
|
|
||||||
|
|
||||||
void parser_expand_malloc_error(
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
minishell->exit = true;
|
|
||||||
malloc_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_var_char(
|
|
||||||
char c
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return (ft_isalnum(c) || c == '_');
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *dup_or_error(
|
|
||||||
const char *value,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *dup;
|
|
||||||
|
|
||||||
dup = ft_strdup(value);
|
|
||||||
if (dup == NULL)
|
|
||||||
return (parser_expand_malloc_error(minishell), NULL);
|
|
||||||
return (dup);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *expand_named_variable(
|
|
||||||
const char *word,
|
|
||||||
size_t *i,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *name;
|
|
||||||
char *value;
|
|
||||||
size_t start;
|
|
||||||
|
|
||||||
start = *i;
|
|
||||||
while (word[*i] != '\0' && is_var_char(word[*i]))
|
|
||||||
(*i)++;
|
|
||||||
name = ft_substr(word, start, *i - start);
|
|
||||||
if (name == NULL)
|
|
||||||
return (parser_expand_malloc_error(minishell), NULL);
|
|
||||||
value = get_var(name, minishell);
|
|
||||||
free(name);
|
|
||||||
if (value == NULL)
|
|
||||||
value = "";
|
|
||||||
return (dup_or_error(value, minishell));
|
|
||||||
}
|
|
||||||
|
|
||||||
char *parser_expand_variable(const char *word, size_t *i,
|
|
||||||
t_minishell *minishell)
|
|
||||||
{
|
|
||||||
char *expanded;
|
|
||||||
|
|
||||||
(*i)++;
|
|
||||||
if (word[*i] == '?')
|
|
||||||
{
|
|
||||||
expanded = ft_itoa(minishell->exit_status);
|
|
||||||
if (expanded == NULL)
|
|
||||||
return (parser_expand_malloc_error(minishell), NULL);
|
|
||||||
return ((*i)++, expanded);
|
|
||||||
}
|
|
||||||
if (word[*i] == '\0' || !is_var_char(word[*i]))
|
|
||||||
return (dup_or_error("$", minishell));
|
|
||||||
return (expand_named_variable(word, i, minishell));
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* parser_expand_word.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/14 06:56:00 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 06:56:00 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "parser_expand_internal.h"
|
|
||||||
|
|
||||||
static bool word_toggle_quotes(
|
|
||||||
char c,
|
|
||||||
bool *in_single_quote,
|
|
||||||
bool *in_double_quote
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (c == '\'' && !*in_double_quote)
|
|
||||||
return (*in_single_quote = !*in_single_quote, true);
|
|
||||||
if (c == '\"' && !*in_single_quote)
|
|
||||||
return (*in_double_quote = !*in_double_quote, true);
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool word_append(
|
|
||||||
char **result,
|
|
||||||
const char *value,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *joined;
|
|
||||||
|
|
||||||
joined = ft_strnjoin(2, *result, (char *)value);
|
|
||||||
if (joined == NULL)
|
|
||||||
{
|
|
||||||
free(*result);
|
|
||||||
*result = NULL;
|
|
||||||
return (parser_expand_malloc_error(minishell), false);
|
|
||||||
}
|
|
||||||
free(*result);
|
|
||||||
*result = joined;
|
|
||||||
return (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool word_step(
|
|
||||||
const char *word,
|
|
||||||
size_t *i,
|
|
||||||
t_word_ctx ctx
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *expanded;
|
|
||||||
char value[2];
|
|
||||||
|
|
||||||
if (word_toggle_quotes(word[*i], ctx.in_single_quote, ctx.in_double_quote))
|
|
||||||
return ((*i)++, true);
|
|
||||||
if (word[*i] == '$' && word[*i + 1] != '\0'
|
|
||||||
&& !*ctx.in_single_quote && !*ctx.in_double_quote
|
|
||||||
&& (word[*i + 1] == '\'' || word[*i + 1] == '\"'))
|
|
||||||
return ((*i)++, true);
|
|
||||||
if (word[*i] == '$' && !*ctx.in_single_quote && ctx.expand_vars)
|
|
||||||
{
|
|
||||||
expanded = parser_expand_variable(word, i, ctx.minishell);
|
|
||||||
if (expanded == NULL || !word_append(ctx.result, expanded,
|
|
||||||
ctx.minishell))
|
|
||||||
return (free(expanded), false);
|
|
||||||
return (free(expanded), true);
|
|
||||||
}
|
|
||||||
value[0] = word[*i];
|
|
||||||
value[1] = '\0';
|
|
||||||
(*i)++;
|
|
||||||
return (word_append(ctx.result, value, ctx.minishell));
|
|
||||||
}
|
|
||||||
|
|
||||||
char *parser_expand_word(
|
|
||||||
const char *word,
|
|
||||||
t_minishell *minishell,
|
|
||||||
bool expand_vars
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *result;
|
|
||||||
size_t i;
|
|
||||||
bool in_single_quote;
|
|
||||||
bool in_double_quote;
|
|
||||||
|
|
||||||
result = ft_strdup("");
|
|
||||||
if (result == NULL)
|
|
||||||
return (parser_expand_malloc_error(minishell), NULL);
|
|
||||||
i = 0;
|
|
||||||
in_single_quote = false;
|
|
||||||
in_double_quote = false;
|
|
||||||
while (word[i] != '\0')
|
|
||||||
{
|
|
||||||
if (!word_step(word, &i, (t_word_ctx){&result, &in_single_quote,
|
|
||||||
&in_double_quote, minishell, expand_vars}))
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
if (in_single_quote || in_double_quote)
|
|
||||||
return (free(result), syntax_error_unexpected_token(NULL),
|
|
||||||
minishell->exit_status = 2, NULL);
|
|
||||||
return (result);
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* parser_redirection.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 21:28:35 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/13 21:28:35 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
#include "errors.h"
|
|
||||||
|
|
||||||
static t_redirection *redirection_new(t_list **tokens);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates a new redirection from a list of tokens.
|
|
||||||
*
|
|
||||||
* @param tokens The list of tokens to create the redirection from.
|
|
||||||
*
|
|
||||||
* @return A new redirection or `NULL` on error.
|
|
||||||
*/
|
|
||||||
static t_redirection *redirection_new(
|
|
||||||
t_list **tokens
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_redirection *redirection;
|
|
||||||
t_token *token;
|
|
||||||
|
|
||||||
redirection = (t_redirection *)malloc(sizeof(t_redirection));
|
|
||||||
if (redirection == NULL)
|
|
||||||
return (malloc_error(), NULL);
|
|
||||||
token = (t_token *)(*tokens)->content;
|
|
||||||
redirection->type = token->type;
|
|
||||||
*tokens = (*tokens)->next;
|
|
||||||
if (*tokens == NULL)
|
|
||||||
{
|
|
||||||
free(redirection);
|
|
||||||
return (syntax_error_unexpected_token(NULL), NULL);
|
|
||||||
}
|
|
||||||
token = (t_token *)(*tokens)->content;
|
|
||||||
if (token->type != TOKEN_WORD)
|
|
||||||
{
|
|
||||||
free(redirection);
|
|
||||||
while (*tokens != NULL)
|
|
||||||
*tokens = (*tokens)->next;
|
|
||||||
return (syntax_error_unexpected_token(token), NULL);
|
|
||||||
}
|
|
||||||
redirection->target = ft_strdup(token->value);
|
|
||||||
if (redirection->target == NULL)
|
|
||||||
{
|
|
||||||
free(redirection);
|
|
||||||
return (malloc_error(), NULL);
|
|
||||||
}
|
|
||||||
*tokens = (*tokens)->next;
|
|
||||||
return (redirection);
|
|
||||||
}
|
|
||||||
|
|
||||||
void redirection_clear(
|
|
||||||
t_redirection *redirection
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (redirection != NULL)
|
|
||||||
{
|
|
||||||
free(redirection->target);
|
|
||||||
free(redirection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void redirection_add(
|
|
||||||
t_list **tokens,
|
|
||||||
t_token *token,
|
|
||||||
t_command **command
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_redirection *redirection;
|
|
||||||
t_list *redirection_tokens;
|
|
||||||
|
|
||||||
redirection = redirection_new(tokens);
|
|
||||||
if (redirection == NULL)
|
|
||||||
{
|
|
||||||
command_clear(*command);
|
|
||||||
*command = NULL;
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
redirection_tokens = ft_lstnew(redirection);
|
|
||||||
if (redirection_tokens == NULL)
|
|
||||||
{
|
|
||||||
free(redirection);
|
|
||||||
return (malloc_error());
|
|
||||||
}
|
|
||||||
if (token->type == TOKEN_HEREDOC)
|
|
||||||
ft_lstadd_back(&(*command)->heredocs, redirection_tokens);
|
|
||||||
else
|
|
||||||
ft_lstadd_back(&(*command)->redirections, redirection_tokens);
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* parser_words.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 21:29:44 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 03:34:38 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
char **args_to_array(t_list **args, size_t argc);
|
|
||||||
void command_clear_argv(t_command *command);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Converts a list of arguments into an array.
|
|
||||||
*
|
|
||||||
* @param args The list of arguments to convert.
|
|
||||||
* @param argc The number of arguments in the list.
|
|
||||||
*
|
|
||||||
* @return An array of arguments or `NULL` on error.
|
|
||||||
*
|
|
||||||
* @note The `args` list is cleared after the conversion and set to NULL.
|
|
||||||
*/
|
|
||||||
char **args_to_array(
|
|
||||||
t_list **args,
|
|
||||||
size_t argc
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char **argv;
|
|
||||||
t_list *arg;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
argv = (char **)malloc(sizeof(char *) * (argc + 1));
|
|
||||||
if (argv == NULL)
|
|
||||||
return (NULL);
|
|
||||||
i = 0;
|
|
||||||
arg = *args;
|
|
||||||
while (arg != NULL)
|
|
||||||
{
|
|
||||||
argv[i] = (char *)arg->content;
|
|
||||||
arg = arg->next;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
argv[i] = NULL;
|
|
||||||
ft_lstclear_nodes(args);
|
|
||||||
return (argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Adds all consecutive word tokens to a command's argv and updates its
|
|
||||||
* argc accordingly.
|
|
||||||
*
|
|
||||||
* @param command The command to add the word tokens to.
|
|
||||||
* @param tokens The list of tokens to add to the command.
|
|
||||||
*/
|
|
||||||
void words_add(
|
|
||||||
t_list **tokens,
|
|
||||||
t_command **command
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_list *args;
|
|
||||||
t_list *arg;
|
|
||||||
t_token *token;
|
|
||||||
|
|
||||||
args = NULL;
|
|
||||||
arg = *tokens;
|
|
||||||
token = (t_token *)arg->content;
|
|
||||||
while (arg != NULL && token->type == TOKEN_WORD)
|
|
||||||
{
|
|
||||||
ft_lstadd_back(&args, ft_lstnew(ft_strdup(token->value)));
|
|
||||||
(*command)->argc++;
|
|
||||||
arg = arg->next;
|
|
||||||
if (arg != NULL)
|
|
||||||
token = (t_token *)arg->content;
|
|
||||||
}
|
|
||||||
*tokens = arg;
|
|
||||||
(*command)->argv = args_to_array(&args, (*command)->argc);
|
|
||||||
ft_lstclear_nodes(&args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void command_clear_argv(
|
|
||||||
t_command *command
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (command->argv != NULL)
|
|
||||||
{
|
|
||||||
i = 0;
|
|
||||||
while (i < command->argc)
|
|
||||||
{
|
|
||||||
free(command->argv[i]);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
free(command->argv);
|
|
||||||
command->argv = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clears a command, freeing all associated memory.
|
|
||||||
*
|
|
||||||
* @param command The command to clear.
|
|
||||||
*/
|
|
||||||
void command_clear(
|
|
||||||
t_command *command
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (command != NULL)
|
|
||||||
{
|
|
||||||
command_clear_argv(command);
|
|
||||||
ft_lstclear(&command->redirections,
|
|
||||||
(void (*)(void *))redirection_clear);
|
|
||||||
ft_lstclear(&command->heredocs, (void (*)(void *))redirection_clear);
|
|
||||||
free(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,33 +6,54 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/12/01 09:12:39 by sede-san #+# #+# */
|
/* Created: 2025/12/01 09:12:39 by sede-san #+# #+# */
|
||||||
/* Updated: 2026/02/14 01:32:42 by sede-san ### ########.fr */
|
/* Updated: 2026/02/08 19:44:15 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "minishell.h"
|
#include "minishell.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "variables.h"
|
|
||||||
#include "errors.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieves the value of an environment variable from the shell's
|
* @brief Parses and stores environment variables from envp array into a hashmap
|
||||||
* environment hashmap.
|
|
||||||
*
|
*
|
||||||
* This function searches for the specified environment variable name in the
|
* This function iterates through the environment variables array (envp) and
|
||||||
* minishell's environment variable hashmap and returns its associated value.
|
* splits each variable string on the '=' delimiter to separate the variable
|
||||||
|
* name from its value. Each name-value pair is then stored in the minishell's
|
||||||
|
* environment hashmap for later retrieval.
|
||||||
*
|
*
|
||||||
* @param name The name of the environment variable to retrieve.
|
* @param envp Array of environment variable strings in "NAME=value" format
|
||||||
* @param minishell Pointer to the minishell object.
|
* @param msh Pointer to the minishell structure containing the environment
|
||||||
|
* hashmap
|
||||||
*
|
*
|
||||||
* @return The value of the environment variable if found, NULL if not found
|
* @note The function assumes envp strings are in the standard format
|
||||||
|
* "NAME=value"
|
||||||
*/
|
*/
|
||||||
char *get_env(
|
void set_envp(
|
||||||
const char *name,
|
char **envp,
|
||||||
t_minishell *minishell
|
t_minishell *msh
|
||||||
)
|
) {
|
||||||
|
char **splitted_env;
|
||||||
|
|
||||||
|
msh->variables.environment
|
||||||
|
= ft_hashmap_new(32, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
||||||
|
if (msh == NULL)
|
||||||
{
|
{
|
||||||
return (ft_hashmap_get(minishell->variables.environment, name));
|
ft_hashmap_clear(&msh->variables.environment, free);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
while (*envp != NULL)
|
||||||
|
{
|
||||||
|
splitted_env = ft_split(*envp, '=');
|
||||||
|
if (splitted_env == NULL)
|
||||||
|
{
|
||||||
|
ft_hashmap_clear(&msh->variables.environment, free);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
ft_hashmap_put(msh->variables.environment,
|
||||||
|
ft_strdup(splitted_env[0]), ft_strdup(splitted_env[1]));
|
||||||
|
ft_free_split(splitted_env);
|
||||||
|
envp++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,74 +64,29 @@ char *get_env(
|
|||||||
* the old value is freed to prevent memory leaks. If the variable is new,
|
* the old value is freed to prevent memory leaks. If the variable is new,
|
||||||
* a duplicate of the key name is created for storage.
|
* a duplicate of the key name is created for storage.
|
||||||
*
|
*
|
||||||
* @param name The name of the environment variable to set
|
* @param env_name The name of the environment variable to set
|
||||||
* @param value The value to assign to the environment variable
|
* @param env_value The value to assign to the environment variable
|
||||||
* @param minishell Pointer to the minishell object.
|
* @param msh Pointer to the minishell structure containing the
|
||||||
|
* environment hashmap
|
||||||
*/
|
*/
|
||||||
void set_env(
|
void set_env(
|
||||||
const char *name,
|
const char *env_name,
|
||||||
char *value,
|
char *env_value,
|
||||||
t_minishell *minishell
|
t_minishell *msh
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
t_hashmap *environment;
|
t_hashmap *environment;
|
||||||
char *key;
|
const char *key;
|
||||||
char *val;
|
|
||||||
char *old_value;
|
char *old_value;
|
||||||
|
|
||||||
environment = minishell->variables.environment;
|
environment = msh->variables.environment;
|
||||||
key = (char *)name;
|
key = env_name;
|
||||||
if (key != NULL && !ft_hashmap_contains_key(environment, key))
|
if (!ft_hashmap_contains_key(environment, key))
|
||||||
{
|
key = ft_strdup(env_name);
|
||||||
key = ft_strdup(name);
|
old_value = ft_hashmap_put(environment, key, ft_strdup(env_value));
|
||||||
if (key == NULL)
|
|
||||||
{
|
|
||||||
minishell->exit = true;
|
|
||||||
malloc_error();
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val = value;
|
|
||||||
if (val == NULL)
|
|
||||||
val = ft_strdup("");
|
|
||||||
else
|
|
||||||
val = ft_strdup(value);
|
|
||||||
if (val == NULL)
|
|
||||||
{
|
|
||||||
if (key != name)
|
|
||||||
free(key);
|
|
||||||
minishell->exit = true;
|
|
||||||
malloc_error();
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
old_value = ft_hashmap_put(environment, key, val);
|
|
||||||
if (old_value != NULL)
|
if (old_value != NULL)
|
||||||
free(old_value);
|
free(old_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Removes an environment variable by name.
|
|
||||||
*
|
|
||||||
* @param name The name of the environment variable to remove.
|
|
||||||
* @param minishell Pointer to the minishell structure.
|
|
||||||
*
|
|
||||||
* @note If the environment variable exists, it will be removed from the hashmap
|
|
||||||
* and its associated value will be freed.
|
|
||||||
*/
|
|
||||||
void unset_env(
|
|
||||||
const char *name,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_hashmap *environment;
|
|
||||||
char *val;
|
|
||||||
|
|
||||||
environment = minishell->variables.environment;
|
|
||||||
val = ft_hashmap_remove(environment, (void *)name);
|
|
||||||
if (val != NULL)
|
|
||||||
free(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Converts the environment variables hashmap to an envp array format.
|
* @brief Converts the environment variables hashmap to an envp array format.
|
||||||
*
|
*
|
||||||
@@ -118,7 +94,7 @@ void unset_env(
|
|||||||
* environment hashmap and converts them into a NULL-terminated array of
|
* environment hashmap and converts them into a NULL-terminated array of
|
||||||
* strings in the format "KEY=VALUE".
|
* strings in the format "KEY=VALUE".
|
||||||
*
|
*
|
||||||
* @param minishell Pointer to the minishell structure containing the environment
|
* @param msh Pointer to the minishell structure containing the environment
|
||||||
* variables hashmap.
|
* variables hashmap.
|
||||||
*
|
*
|
||||||
* @return A dynamically allocated array of strings representing environment
|
* @return A dynamically allocated array of strings representing environment
|
||||||
@@ -133,19 +109,16 @@ void unset_env(
|
|||||||
* the NULL terminator.
|
* the NULL terminator.
|
||||||
*/
|
*/
|
||||||
char **get_envp(
|
char **get_envp(
|
||||||
t_minishell *minishell
|
t_minishell *msh
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
char **envp;
|
char **envp;
|
||||||
t_list *env_list;
|
t_list *env_list;
|
||||||
t_list *env;
|
t_list *env;
|
||||||
t_map_entry *entry;
|
t_map_entry *entry;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
env_list = ft_hashmap_entries(minishell->variables.environment);
|
env_list = ft_hashmap_entries(msh->variables.environment);
|
||||||
envp = (char **)malloc(
|
envp = (char **)malloc((msh->variables.environment->size + 1) * sizeof(char *));
|
||||||
(minishell->variables.environment->size + 1) * sizeof(char *)
|
|
||||||
);
|
|
||||||
if (envp != NULL)
|
if (envp != NULL)
|
||||||
{
|
{
|
||||||
i = 0;
|
i = 0;
|
||||||
@@ -163,39 +136,44 @@ char **get_envp(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Parses and stores environment variables from envp array into a hashmap
|
* @brief Frees a dynamically allocated environment variables array
|
||||||
*
|
*
|
||||||
* This function iterates through the environment variables array (envp) and
|
* This function deallocates memory for an array of strings that was previously
|
||||||
* splits each variable string on the '=' delimiter to separate the variable
|
* allocated by `get_envp()`. It iterates through each string in the array,
|
||||||
* name from its value. Each name-value pair is then stored in the minishell's
|
* frees the memory for individual strings, and then frees the array itself.
|
||||||
* environment hashmap for later retrieval.
|
|
||||||
*
|
*
|
||||||
* @param envp Array of environment variable strings in "NAME=value" format
|
* @param envp Pointer to the array of environment variable strings to be freed.
|
||||||
* @param minishell Pointer to the minishell structure containing the environment
|
* Each string in the array should be dynamically allocated.
|
||||||
* hashmap
|
* The array must be NULL-terminated.
|
||||||
*
|
|
||||||
* @note The function assumes envp strings are in the standard format
|
|
||||||
* "NAME=value"
|
|
||||||
*/
|
*/
|
||||||
void set_envp(
|
void free_envp(
|
||||||
char **envp,
|
char **envp
|
||||||
t_minishell *minishell
|
) {
|
||||||
)
|
size_t i;
|
||||||
{
|
|
||||||
t_hashmap **environment;
|
|
||||||
char **key_value;
|
|
||||||
|
|
||||||
if (minishell == NULL || envp == NULL)
|
i = -1;
|
||||||
return ;
|
while (envp[++i] != NULL)
|
||||||
environment = &minishell->variables.environment;
|
free(envp[i]);
|
||||||
*environment = ft_hashmap_new(32, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
free(envp);
|
||||||
if (*environment == NULL)
|
|
||||||
return ;
|
|
||||||
while (*envp != NULL)
|
|
||||||
{
|
|
||||||
key_value = ft_split(*envp, '=');
|
|
||||||
set_env(key_value[0], key_value[1], minishell);
|
|
||||||
ft_free_split(key_value);
|
|
||||||
envp++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the value of an environment variable from the shell's
|
||||||
|
* environment hashmap.
|
||||||
|
*
|
||||||
|
* This function searches for the specified environment variable name in the
|
||||||
|
* minishell's environment variable hashmap and returns its associated value.
|
||||||
|
*
|
||||||
|
* @param env_name The name of the environment variable to retrieve (e.g.,
|
||||||
|
* "PATH", "HOME")
|
||||||
|
* @param msh Pointer to the minishell structure containing the environment
|
||||||
|
* variables hashmap
|
||||||
|
*
|
||||||
|
* @return The value of the environment variable if found, NULL if not found
|
||||||
|
*/
|
||||||
|
char *get_env(
|
||||||
|
const char *env_name,
|
||||||
|
t_minishell *msh
|
||||||
|
) {
|
||||||
|
return (ft_hashmap_get(msh->variables.environment, env_name));
|
||||||
}
|
}
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* internal.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 21:29:43 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 01:39:36 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "minishell.h"
|
|
||||||
#include "core.h"
|
|
||||||
#include "errors.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Retrieves the value of an internal variable by name.
|
|
||||||
*
|
|
||||||
* @param name The name of the internal variable to retrieve.
|
|
||||||
* @param minishell Pointer to the minishell structure.
|
|
||||||
*
|
|
||||||
* @return The value of the internal variable if found, or NULL if not found.
|
|
||||||
*/
|
|
||||||
char *get_int(
|
|
||||||
const char *name,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return (ft_hashmap_get(minishell->variables.internal, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Stores a variable as internal.
|
|
||||||
*
|
|
||||||
* @param name The name of the internal variable to set.
|
|
||||||
* @param value The value to associate with the internal variable.
|
|
||||||
* @param minishell Pointer to the minishell structure.
|
|
||||||
*
|
|
||||||
* @note If the internal variable already exists, its value will be updated and
|
|
||||||
* the old value will be freed. If it does not exist, a new internal
|
|
||||||
* variable will be created.
|
|
||||||
*/
|
|
||||||
void set_int(
|
|
||||||
const char *name,
|
|
||||||
char *value,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
t_hashmap *internal;
|
|
||||||
char *key;
|
|
||||||
char *val;
|
|
||||||
char *old_val;
|
|
||||||
|
|
||||||
internal = minishell->variables.internal;
|
|
||||||
key = (char *)name;
|
|
||||||
if (key != NULL && !ft_hashmap_contains_key(internal, key))
|
|
||||||
{
|
|
||||||
key = ft_strdup(name);
|
|
||||||
if (key == NULL)
|
|
||||||
return (minishell->exit = true, malloc_error());
|
|
||||||
}
|
|
||||||
val = value;
|
|
||||||
if (val != NULL)
|
|
||||||
val = ft_strdup(value);
|
|
||||||
if (val == NULL)
|
|
||||||
{
|
|
||||||
if (key != name)
|
|
||||||
free(key);
|
|
||||||
return (minishell->exit = true, malloc_error());
|
|
||||||
}
|
|
||||||
old_val = ft_hashmap_put(internal, key, val);
|
|
||||||
if (old_val != NULL)
|
|
||||||
free(old_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Removes an internal variable by name.
|
|
||||||
*
|
|
||||||
* @param name The name of the internal variable to remove.
|
|
||||||
* @param minishell Pointer to the minishell structure.
|
|
||||||
*
|
|
||||||
* @note If the internal variable exists, it will be removed from the hashmap
|
|
||||||
* and its associated value will be freed.
|
|
||||||
*/
|
|
||||||
void unset_int(
|
|
||||||
const char *name,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *value;
|
|
||||||
|
|
||||||
value = ft_hashmap_remove(minishell->variables.internal, (void *)name);
|
|
||||||
if (value != NULL)
|
|
||||||
free(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initializes the internal variables hashmap with default values.
|
|
||||||
*
|
|
||||||
* @param minishell Pointer to the minishell structure to initialize.
|
|
||||||
*
|
|
||||||
* @warning This function must be called only once during the initialization of
|
|
||||||
* the minishell.
|
|
||||||
*/
|
|
||||||
void set_intp(
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (minishell == NULL)
|
|
||||||
return ;
|
|
||||||
minishell->variables.internal
|
|
||||||
= ft_hashmap_new(16, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
|
||||||
if (minishell->variables.internal == NULL)
|
|
||||||
return ;
|
|
||||||
set_int("?", "0", minishell);
|
|
||||||
set_int("_", "minishell", minishell);
|
|
||||||
set_int("PS1", DEFAULT_PS1, minishell);
|
|
||||||
set_int("PS2", DEFAULT_PS2, minishell);
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
/* ************************************************************************** */
|
|
||||||
/* */
|
|
||||||
/* ::: :::::::: */
|
|
||||||
/* variables.c :+: :+: :+: */
|
|
||||||
/* +:+ +:+ +:+ */
|
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
||||||
/* +#+#+#+#+#+ +#+ */
|
|
||||||
/* Created: 2026/02/13 21:41:44 by sede-san #+# #+# */
|
|
||||||
/* Updated: 2026/02/14 00:52:12 by sede-san ### ########.fr */
|
|
||||||
/* */
|
|
||||||
/* ************************************************************************** */
|
|
||||||
|
|
||||||
#include "variables.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Retrieves the value of a variable by name.
|
|
||||||
*
|
|
||||||
* @param name The name of the variable to retrieve.
|
|
||||||
* @param minishell Pointer to the minishell structure.
|
|
||||||
*
|
|
||||||
* @return The value of the variable if found, or NULL if not found.
|
|
||||||
*/
|
|
||||||
char *get_var(
|
|
||||||
const char *name,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char *value;
|
|
||||||
|
|
||||||
value = get_int(name, minishell);
|
|
||||||
if (value == NULL)
|
|
||||||
value = get_env(name, minishell);
|
|
||||||
return (value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_var(const char *name, char *value, t_minishell *minishell)
|
|
||||||
{
|
|
||||||
if (ft_hashmap_contains_key(minishell->variables.internal, name))
|
|
||||||
set_int(name, value, minishell);
|
|
||||||
set_env(name, value, minishell);
|
|
||||||
}
|
|
||||||
|
|
||||||
void unset_var(
|
|
||||||
const char *name,
|
|
||||||
t_minishell *minishell
|
|
||||||
)
|
|
||||||
{
|
|
||||||
unset_int(name, minishell);
|
|
||||||
unset_env(name, minishell);
|
|
||||||
}
|
|
||||||
Submodule tests/42_minishell_tester deleted from e391cbc6a1
@@ -1,204 +0,0 @@
|
|||||||
#!/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