Compare commits
17 Commits
fix/builti
...
solo
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b7a11c185 | |||
| 5dc7de73b3 | |||
| 640c22d366 | |||
| 856bc8243f | |||
| fbd2349060 | |||
|
|
77a704a09a | ||
|
|
39e5719183 | ||
|
|
c1e622947d | ||
|
|
001709139b | ||
|
|
73ed56aa16 | ||
| 5852e8618f | |||
|
|
ba40670ace | ||
|
|
dd6101edec | ||
| e2b734cf0c | |||
| 32b3bd72b5 | |||
| c95703b42b | |||
| f4cfae1107 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -69,5 +69,3 @@ dkms.conf
|
||||
|
||||
# debug information files
|
||||
*.dwo
|
||||
|
||||
minishell-codex/
|
||||
72
AGENTS.md
72
AGENTS.md
@@ -1,72 +0,0 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
- `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.
|
||||
- `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`).
|
||||
- `build/` is the object output directory generated by `make`.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
- `make` or `make all`: build `minishell` (auto-clones required libs into `lib/`).
|
||||
- `make clean`: remove objects in `build/`.
|
||||
- `make fclean`: remove objects and the `minishell` binary (also cleans libs).
|
||||
- `make re`: full rebuild.
|
||||
- `./minishell`: run locally after a build.
|
||||
- `make DEBUG=lldb` or `make DEBUG=valgrind` or `make DEBUG=address`: rebuild with debug/ASan-friendly flags.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
- 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 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
|
||||
- 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`.
|
||||
|
||||
## 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 messages in this repo use a simple `type: summary` format (examples: `update: ...`, `fix: ...`). Keep summaries short and specific.
|
||||
- For PRs, include:
|
||||
1. What changed and why.
|
||||
2. How to test (commands or manual steps).
|
||||
3. Notes on any parser/executor/builtin behavior changes.
|
||||
|
||||
## Configuration Tips
|
||||
- The project depends on `readline`; ensure your system has `libreadline-dev` or equivalent before building.
|
||||
32
README.md
32
README.md
@@ -1,3 +1,4 @@
|
||||
<!-- *This project has been made by sede-san and padan-pe* -->
|
||||
<div align="center">
|
||||
|
||||
<!-- Project badge -->
|
||||
@@ -30,7 +31,21 @@
|
||||
|
||||
> The purpose of this project is to create a simple bash-like shell.
|
||||
|
||||
DETAILED INFO
|
||||
**minishell** is a minimal bash-like shell implementation written in C. It provides essential shell functionality including command execution, piping, input/output redirection, and built-in commands.
|
||||
|
||||
### Key Features
|
||||
|
||||
- **Command Execution**: Execute external programs with argument parsing and path resolution.
|
||||
- **Pipelines**: Connect multiple commands using the pipe operator (`|`).
|
||||
- **Redirections**: Support for input (`<`), output (`>`), and append (`>>`) file redirections.
|
||||
- **Built-in Commands**: `cd`, `echo`, `env`, `exit`, `export`, `pwd`, `unset`.
|
||||
- **Environment Variables**: Access and manage shell environment variables.
|
||||
- **Interactive Shell**: Read-Evaluate-Print Loop (REPL) powered by GNU Readline.
|
||||
- **Quote Handling**: Proper handling of single and double quotes for literal strings.
|
||||
|
||||
### Development
|
||||
|
||||
The codebase adheres to **Norminette v4** standards and uses a modular architecture separating parsing, execution, and built-in command logic. Build artifacts are automatically generated; refer to `AGENTS.md` for detailed development guidelines.
|
||||
|
||||
For detailed info, refer to this project [subject](docs/en.subject.pdf).
|
||||
|
||||
@@ -88,7 +103,20 @@ For detailed info, refer to this project [subject](docs/en.subject.pdf).
|
||||
|
||||
### Basic Usage
|
||||
|
||||
INSTRUCTIONS
|
||||
1. **Execute the shell**
|
||||
|
||||
```bash
|
||||
./minishell
|
||||
```
|
||||
|
||||
2. **Interact with it like if it was bash**
|
||||
```bash
|
||||
echo "Hello, World!"
|
||||
...
|
||||
cat groceries.txt | grep tomatoes
|
||||
...
|
||||
echo "Greetings $USER!" > greetings.txt
|
||||
```
|
||||
|
||||
## 📏 Norminette
|
||||
|
||||
|
||||
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.
|
||||
@@ -1,77 +0,0 @@
|
||||
# Minishell - Resumen para defensa
|
||||
|
||||
Este documento es un guion breve para explicar el proyecto con claridad.
|
||||
|
||||
## 1. Que es minishell
|
||||
- Un shell interactivo basico: lee comandos, los parsea y los ejecuta.
|
||||
- Objetivo didactico: procesos, pipes, fds, señales y parsing.
|
||||
|
||||
## 2. Flujo general (en 20 segundos)
|
||||
1. Prompt con readline.
|
||||
2. Lexer divide la linea en tokens validos (respetando comillas).
|
||||
3. Parser crea comandos, argv y redirecciones.
|
||||
4. Expansion de variables ($VAR, $?).
|
||||
5. Resolver PATH.
|
||||
6. Ejecutar con fork/execve y pipes.
|
||||
7. Actualizar exit status.
|
||||
|
||||
## 3. Puntos clave que suelen preguntar
|
||||
|
||||
### Comillas
|
||||
- Comilla simple: todo literal, no expansion.
|
||||
- Comilla doble: expansion de $VAR, pero no metacaracteres.
|
||||
- Si una comilla no se cierra: error y no se ejecuta nada.
|
||||
|
||||
### Redirecciones
|
||||
- <, >, >>, <<.
|
||||
- Se resuelven antes de ejecutar el comando.
|
||||
- Heredoc: lectura hasta delimitador, sin historial.
|
||||
|
||||
### Pipes
|
||||
- Conecto stdout del comando i con stdin del comando i+1.
|
||||
- Uso pipe() y dup2() en el child.
|
||||
- El padre cierra fds que no usa.
|
||||
|
||||
### Builtins
|
||||
- echo -n, cd, pwd, export, unset, env, exit.
|
||||
- Si no hay pipeline, se ejecutan en el padre para afectar el estado.
|
||||
- En pipeline, se ejecutan en el child.
|
||||
|
||||
### Variables
|
||||
- $VAR: entorno.
|
||||
- $?: ultimo exit status.
|
||||
|
||||
### Senales
|
||||
- Una sola variable global para la senal.
|
||||
- ctrl-C: nueva linea y prompt.
|
||||
- ctrl-D: salir del shell.
|
||||
- ctrl-\: no hace nada.
|
||||
|
||||
## 4. Ejemplos rapidos para demostrar
|
||||
|
||||
1. Pipes
|
||||
- `ls | wc -l`
|
||||
|
||||
2. Redirecciones
|
||||
- `echo hola > out.txt`
|
||||
- `cat < out.txt`
|
||||
|
||||
3. Heredoc
|
||||
- `cat << EOF`
|
||||
- escribir lineas
|
||||
- `EOF`
|
||||
|
||||
4. Expansion
|
||||
- `echo $HOME`
|
||||
- `echo $?`
|
||||
|
||||
## 5. Errores y robustez
|
||||
- No ejecuto si hay errores de parseo.
|
||||
- Mensajes con perror cuando fallan syscalls.
|
||||
- exit_status siempre actualizado.
|
||||
|
||||
## 6. Mensajes finales recomendados
|
||||
- "He separado parsing y ejecucion para que el codigo sea mantenible y
|
||||
la defensa mas clara".
|
||||
- "Sigo el comportamiento de bash como referencia".
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -1,292 +0,0 @@
|
||||
# Minishell - Guia de estructura y pasos (parte obligatoria)
|
||||
|
||||
Objetivo de este documento
|
||||
- Proponer una estructura de archivos y datos.
|
||||
- Describir el proceso paso a paso como tutorial.
|
||||
- Servir de guion para defensa (explicaciones claras y ordenadas).
|
||||
|
||||
Este documento no implementa nada. Solo define el plan y el por que.
|
||||
|
||||
---
|
||||
|
||||
## 1. Vision general del flujo
|
||||
|
||||
El shell se puede explicar como una tuberia de fases:
|
||||
1. Lectura interactiva (prompt + historial).
|
||||
2. Tokenizacion (lexing) con comillas y metacaracteres.
|
||||
3. Parseo (construccion de comandos, redirecciones, pipes).
|
||||
4. Expansion de variables ($VAR y $?).
|
||||
5. Preparacion de ejecucion (resolucion de rutas, heredoc).
|
||||
6. Ejecucion (builtins o execve) con pipes y redirecciones.
|
||||
7. Gestion de exit status y señales.
|
||||
|
||||
Esta separacion permite explicar en defensa cada pieza por separado y justificar
|
||||
las decisiones tecnicas.
|
||||
|
||||
---
|
||||
|
||||
## 2. Propuesta de estructuras de datos
|
||||
|
||||
Estas estructuras son solo guia. Adapta nombres a tu estilo y Norminette.
|
||||
|
||||
### 2.1 Token (lexer)
|
||||
Representa una unidad del input (palabra, pipe, redireccion, etc.).
|
||||
|
||||
- enum e_tokentype
|
||||
- TOK_WORD
|
||||
- TOK_PIPE
|
||||
- TOK_REDIR_IN (<)
|
||||
- TOK_REDIR_OUT (>)
|
||||
- TOK_REDIR_APPEND (>>)
|
||||
- TOK_HEREDOC (<<)
|
||||
|
||||
- struct s_token
|
||||
- char *text
|
||||
- t_tokentype type
|
||||
- struct s_token *next
|
||||
|
||||
Uso en defensa: el lexer separa el input en unidades, respetando comillas.
|
||||
|
||||
### 2.2 Redireccion
|
||||
Guarda los datos de redireccion por comando.
|
||||
|
||||
- enum e_redirtype
|
||||
- REDIR_IN
|
||||
- REDIR_OUT
|
||||
- REDIR_APPEND
|
||||
- REDIR_HEREDOC
|
||||
|
||||
- struct s_redir
|
||||
- t_redirtype type
|
||||
- char *target
|
||||
- int fd
|
||||
- struct s_redir *next
|
||||
|
||||
Notas:
|
||||
- target es el filename o delimitador de heredoc.
|
||||
- fd se resuelve en fase de preparacion (open o pipe temporal).
|
||||
|
||||
### 2.3 Comando
|
||||
|
||||
- struct s_command
|
||||
- char **argv
|
||||
- int argc
|
||||
- char *path
|
||||
- t_redir *redirs
|
||||
|
||||
### 2.4 Pipeline
|
||||
Una lista de comandos en orden, unidos por pipes.
|
||||
|
||||
- struct s_pipeline
|
||||
- t_command **cmds
|
||||
- size_t count
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 3. Lexer: reglas y pasos
|
||||
|
||||
Reglas clave del subject:
|
||||
- No interpretar comillas sin cerrar.
|
||||
- Comilla simple: no se expanden variables ni metacaracteres.
|
||||
- Comilla doble: se expanden variables, pero se respetan caracteres normales.
|
||||
- Metacaracteres: |, <, >, <<, >> separan tokens.
|
||||
|
||||
Pasos recomendados:
|
||||
1. Recorrer la linea caracter a caracter.
|
||||
2. Mantener estado: in_single, in_double.
|
||||
3. Cuando no estas en comillas, detectar metacaracteres y cortar tokens.
|
||||
4. Construir TOK_WORD con el texto exacto (sin eliminar comillas aun).
|
||||
5. Si llegas a fin de linea con in_single o in_double activo, error de parseo.
|
||||
|
||||
Explicacion para defensa:
|
||||
- El lexer no sabe de ejecucion, solo separa en tokens validos.
|
||||
- El manejo de comillas se hace aqui para respetar la sintaxis del shell.
|
||||
|
||||
---
|
||||
|
||||
## 4. Parser: construccion de comandos
|
||||
|
||||
Objetivo: transformar tokens en una estructura ejecutable.
|
||||
|
||||
Pasos:
|
||||
1. Recorrer lista de tokens.
|
||||
2. Cada TOK_PIPE cierra un comando actual y abre el siguiente.
|
||||
3. TOK_WORD se agrega a argv.
|
||||
4. TOK_REDIR_* consume el siguiente token (debe ser TOK_WORD) como target.
|
||||
5. Construir lista de redirecciones para cada comando.
|
||||
6. Validar errores: pipe inicial/final, redireccion sin target, etc.
|
||||
|
||||
Explicacion para defensa:
|
||||
- El parser aplica reglas de orden y construye una estructura clara.
|
||||
- Separar argv y redirecciones evita mezclar logica en executor.
|
||||
|
||||
---
|
||||
|
||||
## 5. Expansion de variables
|
||||
|
||||
Reglas:
|
||||
- $VAR se sustituye por getenv/tabla interna.
|
||||
- $? se sustituye por el exit_status anterior.
|
||||
- En comilla simple no se expande.
|
||||
- En comilla doble si se expande.
|
||||
|
||||
Proceso recomendado:
|
||||
1. Durante tokenizacion, guardar el texto con sus comillas o bien
|
||||
marcar segmentos con estado de comillas.
|
||||
2. En expansion, recorrer cada palabra y reemplazar $...
|
||||
3. Si variable no existe, reemplazar por string vacio.
|
||||
4. Eliminar comillas despues de la expansion.
|
||||
|
||||
Explicacion para defensa:
|
||||
- La expansion es una fase separada para no complicar el parser.
|
||||
- $?, variable especial, refleja el estado de la ultima ejecucion.
|
||||
|
||||
---
|
||||
|
||||
## 6. Redirecciones y heredoc
|
||||
|
||||
Redirecciones basicas:
|
||||
- <: open(file, O_RDONLY)
|
||||
- >: open(file, O_WRONLY | O_CREAT | O_TRUNC)
|
||||
- >>: open(file, O_WRONLY | O_CREAT | O_APPEND)
|
||||
|
||||
Heredoc (<<):
|
||||
1. Leer lineas hasta delimitador exacto.
|
||||
2. Guardar el contenido en un pipe o fichero temporal.
|
||||
3. Usar el extremo de lectura como STDIN del comando.
|
||||
4. No guardar el contenido en historial.
|
||||
|
||||
Explicacion para defensa:
|
||||
- Las redirecciones se resuelven antes de ejecutar el proceso.
|
||||
- Heredoc es una fuente especial de entrada.
|
||||
|
||||
---
|
||||
|
||||
## 7. Resolucion de comandos y PATH
|
||||
|
||||
Reglas:
|
||||
- Si argv[0] es una ruta absoluta o relativa (/, ./, ../), usarla tal cual.
|
||||
- Si no, buscar en PATH separando por ':'.
|
||||
- Si es builtin, no se necesita path real.
|
||||
|
||||
Proceso:
|
||||
1. Detectar builtin.
|
||||
2. Si no builtin y no es ruta, recorrer PATH y usar access().
|
||||
3. Guardar path en t_command->path.
|
||||
|
||||
---
|
||||
|
||||
## 8. Ejecucion
|
||||
|
||||
Caso 1: comando unico builtin
|
||||
- Ejecutar en el proceso padre para que pueda modificar estado del shell
|
||||
(ej: cd, export, unset, exit).
|
||||
|
||||
Caso 2: pipeline o comando externo
|
||||
- Usar fork + execve.
|
||||
- Crear pipes entre comandos.
|
||||
- Aplicar redirecciones antes de ejecutar.
|
||||
|
||||
Proceso para pipeline:
|
||||
1. Para cada comando, crear pipe si hay siguiente.
|
||||
2. fork.
|
||||
3. En child: dup2 para redirecciones y pipes, luego ejecutar.
|
||||
4. En parent: cerrar FDs innecesarios y seguir.
|
||||
5. Esperar procesos, guardar exit status del ultimo comando.
|
||||
|
||||
Explicacion para defensa:
|
||||
- Las pipes conectan stdout del comando i con stdin del comando i+1.
|
||||
- Los builtins dentro de pipeline se ejecutan en child.
|
||||
|
||||
---
|
||||
|
||||
## 9. Builtins obligatorios
|
||||
|
||||
- echo con -n
|
||||
- cd (ruta relativa o absoluta)
|
||||
- pwd
|
||||
- export (sin opciones)
|
||||
- unset (sin opciones)
|
||||
- env (sin opciones o argumentos)
|
||||
- exit
|
||||
|
||||
Notas de defensa:
|
||||
- export/unset trabajan sobre la tabla de variables del shell.
|
||||
- env imprime variables de entorno.
|
||||
- exit debe actualizar exit_status y terminar el loop principal.
|
||||
|
||||
---
|
||||
|
||||
## 10. Senales
|
||||
|
||||
Requisitos interactivos:
|
||||
- ctrl-C: imprime nueva linea y muestra prompt.
|
||||
- ctrl-D: termina el shell.
|
||||
- ctrl-\: no hace nada.
|
||||
|
||||
Regla del subject:
|
||||
- Solo una variable global para indicar la senal recibida.
|
||||
|
||||
Proceso:
|
||||
1. Definir una variable global int g_signal.
|
||||
2. Configurar handlers con sigaction.
|
||||
3. En handler: actualizar g_signal y escribir un '\n' si procede.
|
||||
4. En el loop principal: si g_signal indica SIGINT, resetear lineas de readline.
|
||||
|
||||
---
|
||||
|
||||
## 11. Manejo de errores y salida
|
||||
|
||||
- Mostrar errores con perror o mensajes consistentes.
|
||||
- Si parseo falla, no ejecutar nada.
|
||||
- Mantener exit_status actualizado.
|
||||
|
||||
Explicacion en defensa:
|
||||
- Un shell robusto evita ejecutar comandos parcialmente parseados.
|
||||
- exit_status es clave para $?.
|
||||
|
||||
---
|
||||
|
||||
## 12. Checklist para defensa (guion rapido)
|
||||
|
||||
1. Explico el flujo completo: lectura -> lexer -> parser -> expansion -> exec.
|
||||
2. Explico como manejo comillas y metacaracteres.
|
||||
3. Explico como construyo argv y redirecciones.
|
||||
4. Explico expansion de $VAR y $?.
|
||||
5. Explico pipes y redirecciones con dup2.
|
||||
6. Explico por que los builtins se ejecutan en parent o child.
|
||||
7. Explico manejo de senales y la variable global unica.
|
||||
8. Explico exit_status y comportamiento de $?.
|
||||
|
||||
---
|
||||
|
||||
## 13. Sugerencia de estructura de archivos
|
||||
|
||||
- include/
|
||||
- minishell.h
|
||||
- core.h (estructuras globales y estado)
|
||||
- parser.h (tokens, parser)
|
||||
- executor.h
|
||||
- builtins.h
|
||||
|
||||
- src/
|
||||
- core/ (init, signals, util)
|
||||
- parser/ (lexer.c, parser.c, expand.c)
|
||||
- executor/ (executor.c, redirs.c)
|
||||
- builtins/ (echo, cd, pwd, exit, env, export, unset)
|
||||
- variables/ (environment.c)
|
||||
- minishell.c (loop principal)
|
||||
- main.c
|
||||
|
||||
Esto es solo una guia; no es obligatorio seguirla al pie de la letra.
|
||||
|
||||
---
|
||||
|
||||
## 14. Consejos para la defensa
|
||||
|
||||
- Usa bash como referencia de comportamiento.
|
||||
- Demuestra un par de ejemplos: pipe, redireccion y expansion.
|
||||
- Si algo falla, explica que el parser previene ejecucion parcial.
|
||||
- Recalca el manejo correcto de ctrl-C y ctrl-\.
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
### Multiple redirections
|
||||
|
||||
```
|
||||
cmd > file1 > file2
|
||||
```
|
||||
or
|
||||
```
|
||||
cmd >> file1 >> file2
|
||||
```
|
||||
|
||||
Should create both `file1` and `file2` but store the output in `file2`
|
||||
|
||||
### Environment variables
|
||||
|
||||
#### SHLVL
|
||||
|
||||
Must be increased by 1 if executed from any other shell or 1 if executed
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2025/10/29 22:09:51 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/08 19:42:50 by sede-san ### ########.fr */
|
||||
/* Updated: 2026/02/14 01:15:34 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# include "ft_args.h"
|
||||
# include "minishell.h"
|
||||
# include "core.h"
|
||||
# include "variables.h"
|
||||
|
||||
typedef uint8_t (*t_builtin_func)(t_command cmd, t_minishell *minishell);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2025/10/22 19:10:13 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/13 20:06:08 by sede-san ### ########.fr */
|
||||
/* Updated: 2026/02/14 01:25:43 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -48,7 +48,10 @@ typedef enum e_redirection_type
|
||||
typedef struct s_redirection
|
||||
{
|
||||
t_token_type type;
|
||||
int io_number;
|
||||
char *target;
|
||||
bool heredoc_expand;
|
||||
bool heredoc_ready;
|
||||
} t_redirection;
|
||||
|
||||
/**
|
||||
@@ -63,15 +66,9 @@ typedef struct s_redirection
|
||||
typedef struct s_variables
|
||||
{
|
||||
t_hashmap *environment;
|
||||
// char **internal;
|
||||
t_hashmap *internal;
|
||||
} t_variables;
|
||||
|
||||
typedef struct s_prompt
|
||||
{
|
||||
const char *ps1; // primary prompt string
|
||||
const char *ps2; // secondary prompt string for multiline input
|
||||
} t_prompt;
|
||||
|
||||
/**
|
||||
* @brief Main minishell structure containing global state information
|
||||
*
|
||||
@@ -84,7 +81,6 @@ typedef struct s_minishell
|
||||
{
|
||||
t_variables variables;
|
||||
t_hashmap *builtins;
|
||||
t_prompt prompt;
|
||||
uint8_t exit_status;
|
||||
bool exit;
|
||||
} t_minishell;
|
||||
@@ -136,6 +132,8 @@ extern void minishell_set_execution_signals(void);
|
||||
|
||||
extern void minishell_set_child_signals(void);
|
||||
|
||||
extern void minishell_set_heredoc_signals(void);
|
||||
|
||||
extern bool minishell_consume_sigint(void);
|
||||
|
||||
/* environment.c */
|
||||
@@ -149,8 +147,7 @@ extern char **get_envp(t_minishell *msh);
|
||||
|
||||
extern void free_envp(char **envp);
|
||||
|
||||
extern char *get_env(const char *env_name, t_minishell *msh);
|
||||
|
||||
extern void unset_env(const char *env_name, t_minishell *msh);
|
||||
extern void handle_sigint_status(t_minishell *minishell);
|
||||
extern bool handle_eof(char *line, t_minishell *minishell);
|
||||
|
||||
#endif /* CORE_H */
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/10 22:28:01 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/10 23:24:16 by sede-san ### ########.fr */
|
||||
/* Updated: 2026/02/14 01:11:38 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -19,5 +19,6 @@
|
||||
|
||||
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 */
|
||||
@@ -53,8 +53,12 @@ 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);
|
||||
int *saved_stdin, int *saved_stdout, int *saved_stderr);
|
||||
extern void executor_restore_redirections(int saved_stdin,
|
||||
int saved_stdout);
|
||||
int saved_stdout, int saved_stderr);
|
||||
extern bool executor_prepare_heredocs(t_list *command_list,
|
||||
t_minishell *minishell, uint8_t *exit_status);
|
||||
extern char *executor_expand_heredoc_line(const char *line,
|
||||
t_minishell *minishell);
|
||||
|
||||
#endif /* EXECUTOR_H */
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2025/10/20 16:35:10 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/13 20:12:25 by sede-san ### ########.fr */
|
||||
/* Updated: 2026/02/14 15:17:38 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
# include <term.h> // tgetent(3), tgetflag(3), tgetnum(3),
|
||||
// tgetstr(3), tgoto(3), tputs(3)
|
||||
|
||||
void handle_sigint_status(t_minishell *minishell);
|
||||
bool handle_eof(char *line, t_minishell *minishell);
|
||||
# define MINISHELL_VERSION "1.0.0"
|
||||
|
||||
#endif /* MINISHELL_H */
|
||||
|
||||
@@ -41,8 +41,10 @@ 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);
|
||||
bool parser_token_is_fd_prefix(t_list *token_node,
|
||||
int *io_number);
|
||||
void redirection_add_with_fd(t_list **tokens, t_token *token,
|
||||
t_command **command, int io_number);
|
||||
void words_add(t_list **tokens, t_command **command);
|
||||
|
||||
void expand(t_list **commands, t_minishell *minishell);
|
||||
|
||||
43
include/variables.h
Normal file
43
include/variables.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* 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 */
|
||||
5
src/builtins/env/env.c
vendored
5
src/builtins/env/env.c
vendored
@@ -22,8 +22,9 @@ uint8_t builtin_env(
|
||||
{
|
||||
if (cmd.argc > 1)
|
||||
{
|
||||
ft_eputendl("minishell: env: too many arguments");
|
||||
return (EXIT_FAILURE);
|
||||
ft_eprintf("minishell: env: %s: No such file or directory\n",
|
||||
cmd.argv[1]);
|
||||
return (127);
|
||||
}
|
||||
print_env(msh);
|
||||
return (EXIT_SUCCESS);
|
||||
|
||||
@@ -16,6 +16,8 @@ static bool exit_arg_is_invalid(const char *arg)
|
||||
{
|
||||
if (arg == NULL)
|
||||
return (true);
|
||||
if (arg[0] == '\0')
|
||||
return (true);
|
||||
if ((*arg == '+' || *arg == '-') && arg[1] == '\0')
|
||||
return (true);
|
||||
if (!ft_strisnum(arg))
|
||||
|
||||
@@ -6,14 +6,24 @@
|
||||
/* 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 */
|
||||
/* 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);
|
||||
static uint8_t print_exported(t_minishell *msh);
|
||||
static void **entries_to_array(t_list *lst, size_t *count,
|
||||
bool add_oldpwd, t_map_entry *oldpwd_entry);
|
||||
static bool print_sorted_entries(t_map_entry **entries, size_t count);
|
||||
bool export_is_valid_identifier(const char *arg, size_t name_len);
|
||||
void export_parse_assignment(char *arg, char **eq_pos,
|
||||
size_t *name_len, bool *append);
|
||||
uint8_t export_set_assigned_value(const char *name, char *eq_pos,
|
||||
bool append, t_minishell *msh);
|
||||
bool export_print_declaration(const char *name, const char *value);
|
||||
void export_sort_entries(t_map_entry **entries, size_t count);
|
||||
|
||||
uint8_t builtin_export(
|
||||
t_command cmd,
|
||||
@@ -21,36 +31,24 @@ uint8_t builtin_export(
|
||||
)
|
||||
{
|
||||
uint8_t status;
|
||||
uint8_t result;
|
||||
size_t i;
|
||||
|
||||
if (cmd.argc == 1)
|
||||
return (builtin_env(cmd, msh));
|
||||
return (print_exported(msh));
|
||||
status = EXIT_SUCCESS;
|
||||
i = 0;
|
||||
while (cmd.argv[++i] != NULL)
|
||||
if (export_one(cmd.argv[i], msh) != EXIT_SUCCESS)
|
||||
{
|
||||
result = export_one(cmd.argv[i], msh);
|
||||
if (result == 2)
|
||||
return (2);
|
||||
if (result != 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
|
||||
@@ -59,21 +57,95 @@ static uint8_t export_one(
|
||||
char *eq_pos;
|
||||
char *name;
|
||||
size_t name_len;
|
||||
bool append;
|
||||
|
||||
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);
|
||||
if (arg[0] == '-' && arg[1] != '\0')
|
||||
return (ft_eprintf("minishell: export: %s: invalid option\n", arg),
|
||||
ft_eputendl("export: usage: export [name[=value] ...]"), 2);
|
||||
export_parse_assignment(arg, &eq_pos, &name_len, &append);
|
||||
if (!export_is_valid_identifier(arg, name_len))
|
||||
return (ft_eprintf(
|
||||
"minishell: export: `%s': not a valid identifier\n", arg),
|
||||
EXIT_FAILURE);
|
||||
name = ft_substr(arg, 0, name_len);
|
||||
if (name == NULL)
|
||||
return (EXIT_FAILURE);
|
||||
if (eq_pos != NULL)
|
||||
set_env(name, eq_pos + 1, msh);
|
||||
else
|
||||
set_env(name, "", msh);
|
||||
if (export_set_assigned_value(name, eq_pos, append, msh) != EXIT_SUCCESS)
|
||||
return (free(name), EXIT_FAILURE);
|
||||
free(name);
|
||||
return (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static uint8_t print_exported(
|
||||
t_minishell *msh
|
||||
)
|
||||
{
|
||||
t_list *entries;
|
||||
t_map_entry **sorted_entries;
|
||||
t_map_entry oldpwd_entry;
|
||||
size_t count;
|
||||
|
||||
entries = ft_hashmap_entries(msh->variables.environment);
|
||||
if (entries == NULL)
|
||||
return (EXIT_SUCCESS);
|
||||
oldpwd_entry = (t_map_entry){"OLDPWD", NULL};
|
||||
sorted_entries = (t_map_entry **)entries_to_array(entries, &count,
|
||||
!ft_hashmap_contains_key(msh->variables.environment, "OLDPWD"),
|
||||
&oldpwd_entry);
|
||||
ft_lstclear_nodes(&entries);
|
||||
if (sorted_entries == NULL)
|
||||
return (EXIT_FAILURE);
|
||||
export_sort_entries(sorted_entries, count);
|
||||
if (!print_sorted_entries(sorted_entries, count))
|
||||
return (free(sorted_entries), EXIT_FAILURE);
|
||||
return (free(sorted_entries), EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void **entries_to_array(
|
||||
t_list *entries,
|
||||
size_t *count,
|
||||
bool add_oldpwd,
|
||||
t_map_entry *oldpwd_entry
|
||||
)
|
||||
{
|
||||
void **array;
|
||||
t_list *node;
|
||||
t_map_entry *entry;
|
||||
size_t i;
|
||||
|
||||
*count = (size_t)ft_lstsize(entries) + (size_t)add_oldpwd;
|
||||
array = (void **)malloc(sizeof(void *) * (*count));
|
||||
if (array == NULL)
|
||||
return (NULL);
|
||||
i = 0;
|
||||
node = entries;
|
||||
while (node != NULL)
|
||||
{
|
||||
entry = (t_map_entry *)node->content;
|
||||
if (entry != NULL && ft_strcmp((char *)entry->key, "_") != 0)
|
||||
array[i++] = entry;
|
||||
node = node->next;
|
||||
}
|
||||
if (add_oldpwd)
|
||||
array[i++] = oldpwd_entry;
|
||||
*count = i;
|
||||
return (array);
|
||||
}
|
||||
|
||||
static bool print_sorted_entries(
|
||||
t_map_entry **entries,
|
||||
size_t count
|
||||
)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
i = 0;
|
||||
while (i < count)
|
||||
{
|
||||
if (!export_print_declaration((char *)entries[i]->key,
|
||||
(char *)entries[i]->value))
|
||||
return (false);
|
||||
i++;
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
40
src/builtins/export/export_sort.c
Normal file
40
src/builtins/export/export_sort.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* export_sort.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 23:55:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 23:55:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "builtins.h"
|
||||
|
||||
void export_sort_entries(
|
||||
t_map_entry **entries,
|
||||
size_t count
|
||||
)
|
||||
{
|
||||
size_t i;
|
||||
size_t j;
|
||||
t_map_entry *tmp;
|
||||
|
||||
i = 0;
|
||||
while (i < count)
|
||||
{
|
||||
j = i + 1;
|
||||
while (j < count)
|
||||
{
|
||||
if (ft_strcmp((char *)entries[i]->key, (char *)entries[j]->key) > 0)
|
||||
{
|
||||
tmp = entries[i];
|
||||
entries[i] = entries[j];
|
||||
entries[j] = tmp;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
129
src/builtins/export/export_utils.c
Normal file
129
src/builtins/export/export_utils.c
Normal file
@@ -0,0 +1,129 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* export_utils.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 15:20:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 15:20:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "builtins.h"
|
||||
|
||||
bool export_is_valid_identifier(
|
||||
const char *arg,
|
||||
size_t name_len
|
||||
)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (name_len == 0 || arg == NULL)
|
||||
return (false);
|
||||
if (!ft_isalpha(arg[0]) && arg[0] != '_')
|
||||
return (false);
|
||||
i = 0;
|
||||
while (++i < name_len)
|
||||
if (!ft_isalnum(arg[i]) && arg[i] != '_')
|
||||
return (false);
|
||||
return (true);
|
||||
}
|
||||
|
||||
void export_parse_assignment(
|
||||
char *arg,
|
||||
char **eq_pos,
|
||||
size_t *name_len,
|
||||
bool *append
|
||||
)
|
||||
{
|
||||
char *plus_eq;
|
||||
|
||||
*append = false;
|
||||
*eq_pos = ft_strchr(arg, '=');
|
||||
*name_len = ft_strlen(arg);
|
||||
plus_eq = ft_strnstr(arg, "+=", *name_len);
|
||||
if (plus_eq != NULL)
|
||||
{
|
||||
*append = true;
|
||||
*name_len = (size_t)(plus_eq - arg);
|
||||
*eq_pos = plus_eq + 1;
|
||||
}
|
||||
else if (*eq_pos != NULL)
|
||||
*name_len = (size_t)(*eq_pos - arg);
|
||||
}
|
||||
|
||||
uint8_t export_set_assigned_value(
|
||||
const char *name,
|
||||
char *eq_pos,
|
||||
bool append,
|
||||
t_minishell *msh
|
||||
)
|
||||
{
|
||||
char *current;
|
||||
char *joined;
|
||||
|
||||
if (append)
|
||||
{
|
||||
current = get_var((char *)name, msh);
|
||||
if (current == NULL)
|
||||
current = "";
|
||||
joined = ft_strnjoin(2, current, eq_pos + 1);
|
||||
if (joined == NULL)
|
||||
return (EXIT_FAILURE);
|
||||
set_var(name, joined, msh);
|
||||
free(joined);
|
||||
return (EXIT_SUCCESS);
|
||||
}
|
||||
if (eq_pos != NULL)
|
||||
set_var(name, eq_pos + 1, msh);
|
||||
else
|
||||
set_var(name, "", msh);
|
||||
return (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
char *export_escape_value(
|
||||
const char *value
|
||||
)
|
||||
{
|
||||
char *escaped;
|
||||
size_t i;
|
||||
size_t j;
|
||||
|
||||
escaped = (char *)malloc((ft_strlen(value) * 2) + 1);
|
||||
if (escaped == NULL)
|
||||
return (NULL);
|
||||
i = 0;
|
||||
j = 0;
|
||||
while (value[i] != '\0')
|
||||
{
|
||||
if (value[i] == '\\' || value[i] == '\"' || value[i] == '$')
|
||||
escaped[j++] = '\\';
|
||||
escaped[j++] = value[i++];
|
||||
}
|
||||
escaped[j] = '\0';
|
||||
return (escaped);
|
||||
}
|
||||
|
||||
bool export_print_declaration(
|
||||
const char *name,
|
||||
const char *value
|
||||
)
|
||||
{
|
||||
char *escaped;
|
||||
|
||||
ft_putstr("declare -x ");
|
||||
ft_putstr((char *)name);
|
||||
if (value != NULL)
|
||||
{
|
||||
escaped = export_escape_value(value);
|
||||
if (escaped == NULL)
|
||||
return (false);
|
||||
ft_putstr("=\"");
|
||||
ft_putstr(escaped);
|
||||
ft_putstr("\"");
|
||||
free(escaped);
|
||||
}
|
||||
ft_putchar('\n');
|
||||
return (true);
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
static uint8_t is_valid_identifier(const char *arg);
|
||||
static uint8_t unset_one(char *arg, t_minishell *msh);
|
||||
static bool is_option(const char *arg);
|
||||
|
||||
uint8_t builtin_unset(
|
||||
t_command cmd,
|
||||
@@ -21,13 +22,19 @@ uint8_t builtin_unset(
|
||||
)
|
||||
{
|
||||
uint8_t status;
|
||||
uint8_t result;
|
||||
size_t i;
|
||||
|
||||
status = EXIT_SUCCESS;
|
||||
i = 0;
|
||||
while (cmd.argv[++i] != NULL)
|
||||
if (unset_one(cmd.argv[i], msh) != EXIT_SUCCESS)
|
||||
{
|
||||
result = unset_one(cmd.argv[i], msh);
|
||||
if (result == 2)
|
||||
return (2);
|
||||
if (result != EXIT_SUCCESS)
|
||||
status = EXIT_FAILURE;
|
||||
}
|
||||
return (status);
|
||||
}
|
||||
|
||||
@@ -53,9 +60,21 @@ static uint8_t unset_one(
|
||||
t_minishell *msh
|
||||
)
|
||||
{
|
||||
if (is_option(arg))
|
||||
{
|
||||
ft_eprintf("minishell: unset: %s: invalid option\n", arg);
|
||||
ft_eputendl("unset: usage: unset [name ...]");
|
||||
return (2);
|
||||
}
|
||||
if (!is_valid_identifier(arg))
|
||||
return (ft_eprintf("minishell: unset: `%s': not a valid identifier\n",
|
||||
arg), EXIT_FAILURE);
|
||||
return (EXIT_SUCCESS);
|
||||
unset_env(arg, msh);
|
||||
return (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static bool is_option(
|
||||
const char *arg
|
||||
)
|
||||
{
|
||||
return (arg != NULL && arg[0] == '-' && arg[1] != '\0');
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "core.h"
|
||||
#include "signals_internal.h"
|
||||
|
||||
static int g_signal = 0;
|
||||
int g_signal = 0;
|
||||
|
||||
static void sigint_handler(int signal)
|
||||
static void sigint_handler_interactive(int signal)
|
||||
{
|
||||
g_signal = signal;
|
||||
write(STDOUT_FILENO, "\n", 1);
|
||||
@@ -28,7 +29,7 @@ void minishell_set_interactive_signals(void)
|
||||
struct sigaction action;
|
||||
|
||||
ft_bzero(&action, sizeof(action));
|
||||
action.sa_handler = sigint_handler;
|
||||
action.sa_handler = sigint_handler_interactive;
|
||||
sigemptyset(&action.sa_mask);
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
action.sa_handler = SIG_IGN;
|
||||
@@ -46,17 +47,6 @@ void minishell_set_execution_signals(void)
|
||||
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)
|
||||
|
||||
24
src/core/signals_child.c
Normal file
24
src/core/signals_child.c
Normal file
@@ -0,0 +1,24 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* signals_child.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 18:20:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 18:20:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "core.h"
|
||||
|
||||
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);
|
||||
}
|
||||
32
src/core/signals_heredoc.c
Normal file
32
src/core/signals_heredoc.c
Normal file
@@ -0,0 +1,32 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* signals_heredoc.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 23:40:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 23:40:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "core.h"
|
||||
#include "signals_internal.h"
|
||||
|
||||
static void sigint_handler_heredoc(int signal)
|
||||
{
|
||||
g_signal = signal;
|
||||
write(STDOUT_FILENO, "\n", 1);
|
||||
}
|
||||
|
||||
void minishell_set_heredoc_signals(void)
|
||||
{
|
||||
struct sigaction action;
|
||||
|
||||
ft_bzero(&action, sizeof(action));
|
||||
action.sa_handler = sigint_handler_heredoc;
|
||||
sigemptyset(&action.sa_mask);
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
action.sa_handler = SIG_IGN;
|
||||
sigaction(SIGQUIT, &action, NULL);
|
||||
}
|
||||
18
src/core/signals_internal.h
Normal file
18
src/core/signals_internal.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* signals_internal.h :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 23:40:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 23:40:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#ifndef SIGNALS_INTERNAL_H
|
||||
# define SIGNALS_INTERNAL_H
|
||||
|
||||
extern int g_signal;
|
||||
|
||||
#endif
|
||||
@@ -6,7 +6,7 @@
|
||||
/* 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 */
|
||||
/* Updated: 2026/02/14 06:58:05 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -29,3 +29,10 @@ void malloc_error(void)
|
||||
{
|
||||
ft_eprintf("minishell: %s\n", strerror(ENOMEM));
|
||||
}
|
||||
|
||||
void command_not_found_error(
|
||||
const char *command
|
||||
)
|
||||
{
|
||||
ft_eprintf("minishell: %s: command not found\n", command);
|
||||
}
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
/* 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 */
|
||||
/* Updated: 2026/02/14 13:12:58 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "executor.h"
|
||||
#include "builtins.h"
|
||||
#include "errors.h"
|
||||
#include <errno.h>
|
||||
|
||||
static uint8_t resolve_execve_status(void)
|
||||
@@ -40,9 +41,13 @@ static void handle_execve_error(
|
||||
)
|
||||
{
|
||||
uint8_t exit_status;
|
||||
size_t i;
|
||||
|
||||
exit_status = resolve_execve_status();
|
||||
free_envp(envp);
|
||||
i = 0;
|
||||
while (envp[i] != NULL)
|
||||
free(envp[i++]);
|
||||
free(envp);
|
||||
perror(command->path);
|
||||
exit(exit_status);
|
||||
}
|
||||
@@ -56,10 +61,7 @@ static void execute_external_command(
|
||||
|
||||
envp = get_envp(minishell);
|
||||
if (envp == NULL)
|
||||
{
|
||||
perror("get_envp");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return (malloc_error(), exit(EXIT_FAILURE));
|
||||
execve(command->path, command->argv, envp);
|
||||
handle_execve_error(command, envp);
|
||||
}
|
||||
@@ -77,10 +79,7 @@ uint8_t executor_execute_command(
|
||||
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);
|
||||
}
|
||||
return (command_not_found_error(command->argv[0]), 127);
|
||||
execute_external_command(command, minishell);
|
||||
return (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ static void cmdfree_redirection(
|
||||
{
|
||||
if (redirection == NULL)
|
||||
return ;
|
||||
if (redirection->type == TOKEN_HEREDOC && redirection->heredoc_ready
|
||||
&& redirection->target != NULL)
|
||||
unlink(redirection->target);
|
||||
free(redirection->target);
|
||||
free(redirection);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/08 19:10:47 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/11 00:00:00 by sede-san ### ########.fr */
|
||||
/* Updated: 2026/02/14 13:10:43 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -48,13 +48,15 @@ static void run_command_in_parent(
|
||||
t_command *command;
|
||||
int saved_stdin;
|
||||
int saved_stdout;
|
||||
int saved_stderr;
|
||||
|
||||
command = state->current_command->content;
|
||||
if (!executor_apply_redirections(command, &saved_stdin, &saved_stdout))
|
||||
if (!executor_apply_redirections(command, &saved_stdin, &saved_stdout,
|
||||
&saved_stderr))
|
||||
state->exit_status = EXIT_FAILURE;
|
||||
else
|
||||
state->exit_status = executor_execute_command(command, minishell);
|
||||
executor_restore_redirections(saved_stdin, saved_stdout);
|
||||
executor_restore_redirections(saved_stdin, saved_stdout, saved_stderr);
|
||||
}
|
||||
|
||||
static bool run_current_command(
|
||||
@@ -89,6 +91,12 @@ uint8_t execute(
|
||||
t_exec_state state;
|
||||
|
||||
init_exec_state(&state, command_list);
|
||||
if (!executor_prepare_heredocs(command_list, minishell, &state.exit_status))
|
||||
{
|
||||
minishell->exit_status = state.exit_status;
|
||||
ft_lstclear(&command_list, (void (*)(void *))executor_cmdfree);
|
||||
return (state.exit_status);
|
||||
}
|
||||
minishell_set_execution_signals();
|
||||
while (state.current_command)
|
||||
{
|
||||
|
||||
65
src/executor/heredoc.c
Normal file
65
src/executor/heredoc.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* heredoc.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 17:50:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 17:50:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "heredoc_internal.h"
|
||||
#include "errors.h"
|
||||
|
||||
static bool prepare_heredoc_redirection(t_redirection *rd,
|
||||
t_minishell *minishell, uint8_t *exit_status)
|
||||
{
|
||||
char *path;
|
||||
int fd;
|
||||
|
||||
path = NULL;
|
||||
fd = executor_heredoc_open_tmp(&path);
|
||||
if (fd == -1)
|
||||
return (perror("open"), *exit_status = EXIT_FAILURE, false);
|
||||
if (!executor_heredoc_fill_tmp(fd, rd, minishell, exit_status))
|
||||
return (executor_heredoc_discard_tmp(fd, path), false);
|
||||
return (executor_heredoc_finalize_tmp(rd, fd, path, exit_status));
|
||||
}
|
||||
|
||||
static bool process_command_heredocs(t_command *command, t_minishell *minishell,
|
||||
uint8_t *exit_status)
|
||||
{
|
||||
t_list *redir_node;
|
||||
t_redirection *rd;
|
||||
|
||||
redir_node = command->redirections;
|
||||
while (redir_node != NULL)
|
||||
{
|
||||
rd = (t_redirection *)redir_node->content;
|
||||
if (rd->type == TOKEN_HEREDOC
|
||||
&& !prepare_heredoc_redirection(rd, minishell, exit_status))
|
||||
return (false);
|
||||
redir_node = redir_node->next;
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool executor_prepare_heredocs(t_list *command_list, t_minishell *minishell,
|
||||
uint8_t *exit_status)
|
||||
{
|
||||
t_command *command;
|
||||
|
||||
*exit_status = EXIT_SUCCESS;
|
||||
minishell_set_heredoc_signals();
|
||||
while (command_list != NULL)
|
||||
{
|
||||
command = (t_command *)command_list->content;
|
||||
if (!process_command_heredocs(command, minishell, exit_status))
|
||||
return (minishell_set_interactive_signals(), false);
|
||||
command_list = command_list->next;
|
||||
}
|
||||
minishell_set_interactive_signals();
|
||||
return (true);
|
||||
}
|
||||
107
src/executor/heredoc_expand.c
Normal file
107
src/executor/heredoc_expand.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* heredoc_expand.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 17:30:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 17:30:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "executor.h"
|
||||
#include "variables.h"
|
||||
#include "errors.h"
|
||||
|
||||
static bool is_var_char(char c)
|
||||
{
|
||||
return (ft_isalnum(c) || c == '_');
|
||||
}
|
||||
|
||||
static bool append_text(char **result, const char *value)
|
||||
{
|
||||
char *joined;
|
||||
|
||||
joined = ft_strnjoin(2, *result, (char *)value);
|
||||
if (joined == NULL)
|
||||
{
|
||||
free(*result);
|
||||
*result = NULL;
|
||||
return (false);
|
||||
}
|
||||
free(*result);
|
||||
*result = joined;
|
||||
return (true);
|
||||
}
|
||||
|
||||
static char *expand_variable(const char *line, size_t *i, t_minishell *msh)
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
size_t start;
|
||||
|
||||
(*i)++;
|
||||
if (line[*i] == '?')
|
||||
return ((*i)++, ft_itoa(msh->exit_status));
|
||||
if (line[*i] == '\0' || !is_var_char(line[*i]))
|
||||
return (ft_strdup("$"));
|
||||
start = *i;
|
||||
while (line[*i] != '\0' && is_var_char(line[*i]))
|
||||
(*i)++;
|
||||
name = ft_substr(line, start, *i - start);
|
||||
if (name == NULL)
|
||||
return (NULL);
|
||||
value = get_var(name, msh);
|
||||
free(name);
|
||||
if (value == NULL)
|
||||
value = "";
|
||||
return (ft_strdup(value));
|
||||
}
|
||||
|
||||
static char *expand_literal_char(const char *line, size_t *i,
|
||||
bool *in_single, bool *in_double)
|
||||
{
|
||||
char value[2];
|
||||
|
||||
if (line[*i] == '\'' && !*in_double)
|
||||
*in_single = !*in_single;
|
||||
else if (line[*i] == '\"' && !*in_single)
|
||||
*in_double = !*in_double;
|
||||
value[0] = line[*i];
|
||||
value[1] = '\0';
|
||||
(*i)++;
|
||||
return (ft_strdup(value));
|
||||
}
|
||||
|
||||
char *executor_expand_heredoc_line(
|
||||
const char *line,
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
char *result;
|
||||
char *expanded;
|
||||
size_t i;
|
||||
bool in_single;
|
||||
bool in_double;
|
||||
|
||||
result = ft_strdup("");
|
||||
if (result == NULL)
|
||||
return (malloc_error(), NULL);
|
||||
i = 0;
|
||||
in_single = false;
|
||||
in_double = false;
|
||||
while (line[i] != '\0')
|
||||
{
|
||||
if (line[i] == '$' && !in_single)
|
||||
expanded = expand_variable(line, &i, minishell);
|
||||
else
|
||||
expanded = expand_literal_char(line, &i, &in_single, &in_double);
|
||||
if (expanded == NULL)
|
||||
return (free(result), malloc_error(), NULL);
|
||||
if (!append_text(&result, expanded))
|
||||
return (free(expanded), malloc_error(), NULL);
|
||||
free(expanded);
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
77
src/executor/heredoc_file.c
Normal file
77
src/executor/heredoc_file.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* heredoc_file.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 17:50:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 17:50:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "heredoc_internal.h"
|
||||
#include <errno.h>
|
||||
#include "variables.h"
|
||||
|
||||
static char *build_tmp_path(unsigned int id)
|
||||
{
|
||||
char *suffix;
|
||||
char *path;
|
||||
|
||||
suffix = ft_uitoa(id);
|
||||
if (suffix == NULL)
|
||||
return (errno = ENOMEM, NULL);
|
||||
path = ft_strnjoin(2, "/tmp/minishell_heredoc_", suffix);
|
||||
free(suffix);
|
||||
if (path == NULL)
|
||||
return (errno = ENOMEM, NULL);
|
||||
return (path);
|
||||
}
|
||||
|
||||
int executor_heredoc_open_tmp(char **path_out)
|
||||
{
|
||||
static unsigned int counter;
|
||||
char *path;
|
||||
unsigned int attempts;
|
||||
int fd;
|
||||
|
||||
attempts = 0;
|
||||
while (attempts++ < 10000)
|
||||
{
|
||||
path = build_tmp_path(counter++);
|
||||
if (path == NULL)
|
||||
return (-1);
|
||||
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600);
|
||||
if (fd != -1)
|
||||
return (*path_out = path, fd);
|
||||
free(path);
|
||||
if (errno != EEXIST)
|
||||
return (-1);
|
||||
}
|
||||
errno = EEXIST;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
void executor_heredoc_discard_tmp(int fd, char *path)
|
||||
{
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (path != NULL)
|
||||
{
|
||||
unlink(path);
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
|
||||
bool executor_heredoc_finalize_tmp(t_redirection *rd, int fd, char *path,
|
||||
uint8_t *exit_status)
|
||||
{
|
||||
if (close(fd) == -1)
|
||||
return (executor_heredoc_discard_tmp(-1, path), perror("close"),
|
||||
*exit_status = EXIT_FAILURE, false);
|
||||
free(rd->target);
|
||||
rd->target = path;
|
||||
rd->heredoc_ready = true;
|
||||
return (true);
|
||||
}
|
||||
80
src/executor/heredoc_input.c
Normal file
80
src/executor/heredoc_input.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* heredoc_input.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 17:50:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 17:50:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "heredoc_internal.h"
|
||||
#include "errors.h"
|
||||
|
||||
static bool write_all(int fd, const char *buffer, size_t len)
|
||||
{
|
||||
size_t total;
|
||||
ssize_t written;
|
||||
|
||||
total = 0;
|
||||
while (total < len)
|
||||
{
|
||||
written = write(fd, buffer + total, len - total);
|
||||
if (written <= 0)
|
||||
return (false);
|
||||
total += written;
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
static bool write_heredoc_line(int fd, const char *line)
|
||||
{
|
||||
if (!write_all(fd, line, ft_strlen(line)))
|
||||
return (false);
|
||||
return (write_all(fd, "\n", 1));
|
||||
}
|
||||
|
||||
static bool handle_heredoc_line(int fd, char *line, t_redirection *rd,
|
||||
t_minishell *minishell)
|
||||
{
|
||||
char *expanded;
|
||||
|
||||
expanded = line;
|
||||
if (rd->heredoc_expand)
|
||||
{
|
||||
expanded = executor_expand_heredoc_line(line, minishell);
|
||||
free(line);
|
||||
if (expanded == NULL)
|
||||
return (false);
|
||||
}
|
||||
if (!write_heredoc_line(fd, expanded))
|
||||
return (free(expanded), perror("write"), false);
|
||||
free(expanded);
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool executor_heredoc_fill_tmp(int fd, t_redirection *rd,
|
||||
t_minishell *minishell, uint8_t *exit_status)
|
||||
{
|
||||
char *line;
|
||||
|
||||
while (1)
|
||||
{
|
||||
line = executor_heredoc_read_line(minishell);
|
||||
if (minishell_consume_sigint())
|
||||
return (free(line), *exit_status = 130, false);
|
||||
if (line == NULL)
|
||||
{
|
||||
if (!isatty(STDIN_FILENO))
|
||||
minishell->exit = true;
|
||||
return (ft_eprintf("minishell: warning: here-document delimited by "
|
||||
"end-of-file (wanted `%s')\n", rd->target), true);
|
||||
}
|
||||
if (ft_strcmp(line, rd->target) == 0)
|
||||
return (free(line), true);
|
||||
if (!handle_heredoc_line(fd, line, rd, minishell))
|
||||
return (*exit_status = EXIT_FAILURE, false);
|
||||
}
|
||||
}
|
||||
26
src/executor/heredoc_internal.h
Normal file
26
src/executor/heredoc_internal.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* heredoc_internal.h :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 17:50:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 17:50:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#ifndef HEREDOC_INTERNAL_H
|
||||
# define HEREDOC_INTERNAL_H
|
||||
|
||||
# include "executor.h"
|
||||
|
||||
char *executor_heredoc_read_line(t_minishell *minishell);
|
||||
int executor_heredoc_open_tmp(char **path_out);
|
||||
void executor_heredoc_discard_tmp(int fd, char *path);
|
||||
bool executor_heredoc_finalize_tmp(t_redirection *rd, int fd, char *path,
|
||||
uint8_t *exit_status);
|
||||
bool executor_heredoc_fill_tmp(int fd, t_redirection *rd,
|
||||
t_minishell *minishell, uint8_t *exit_status);
|
||||
|
||||
#endif
|
||||
89
src/executor/heredoc_readline.c
Normal file
89
src/executor/heredoc_readline.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* heredoc_readline.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/15 00:20:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/15 00:20:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "heredoc_internal.h"
|
||||
#include "variables.h"
|
||||
#include <errno.h>
|
||||
|
||||
static bool append_char(char **line, size_t *len, char c)
|
||||
{
|
||||
char *new_line;
|
||||
size_t i;
|
||||
|
||||
new_line = (char *)malloc(*len + 2);
|
||||
if (new_line == NULL)
|
||||
return (free(*line), *line = NULL, false);
|
||||
i = 0;
|
||||
while (i < *len)
|
||||
{
|
||||
new_line[i] = (*line)[i];
|
||||
i++;
|
||||
}
|
||||
new_line[*len] = c;
|
||||
new_line[*len + 1] = '\0';
|
||||
free(*line);
|
||||
*line = new_line;
|
||||
(*len)++;
|
||||
return (true);
|
||||
}
|
||||
|
||||
static char *read_line_stdin(void)
|
||||
{
|
||||
char *line;
|
||||
char c;
|
||||
size_t len;
|
||||
ssize_t nread;
|
||||
|
||||
line = ft_strdup("");
|
||||
if (line == NULL)
|
||||
return (NULL);
|
||||
len = 0;
|
||||
errno = 0;
|
||||
nread = read(STDIN_FILENO, &c, 1);
|
||||
while (nread > 0)
|
||||
{
|
||||
if (c == '\n')
|
||||
return (line);
|
||||
if (!append_char(&line, &len, c))
|
||||
return (NULL);
|
||||
nread = read(STDIN_FILENO, &c, 1);
|
||||
}
|
||||
if (nread == -1 && errno == EINTR)
|
||||
return (line);
|
||||
if (len > 0)
|
||||
return (line);
|
||||
return (free(line), NULL);
|
||||
}
|
||||
|
||||
char *executor_heredoc_read_line(t_minishell *minishell)
|
||||
{
|
||||
char *prompt;
|
||||
char *line;
|
||||
size_t len;
|
||||
|
||||
if (!isatty(STDIN_FILENO))
|
||||
{
|
||||
line = get_next_line(STDIN_FILENO);
|
||||
if (line == NULL)
|
||||
return (NULL);
|
||||
len = ft_strlen(line);
|
||||
if (len > 0 && line[len - 1] == '\n')
|
||||
line[len - 1] = '\0';
|
||||
return (line);
|
||||
}
|
||||
prompt = get_var("PS2", minishell);
|
||||
if (prompt == NULL)
|
||||
prompt = DEFAULT_PS2;
|
||||
if (write(STDOUT_FILENO, prompt, ft_strlen(prompt)) == -1)
|
||||
return (NULL);
|
||||
return (read_line_stdin());
|
||||
}
|
||||
@@ -6,11 +6,12 @@
|
||||
/* 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 */
|
||||
/* Updated: 2026/02/14 13:16:15 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "executor.h"
|
||||
#include "variables.h"
|
||||
|
||||
static bool is_path_explicit(
|
||||
const char *command_name
|
||||
@@ -37,7 +38,7 @@ static char *resolve_path_from_env(
|
||||
return (NULL);
|
||||
command_path = NULL;
|
||||
i = -1;
|
||||
while (!command_path && path_env[++i] != NULL)
|
||||
while (command_name[0] != 0 && !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)
|
||||
|
||||
@@ -53,7 +53,7 @@ void executor_child_process(
|
||||
minishell_set_child_signals();
|
||||
executor_setup_child_input(pipeline);
|
||||
executor_setup_child_output(current_command, pipeline);
|
||||
if (!executor_apply_redirections(command, NULL, NULL))
|
||||
if (!executor_apply_redirections(command, NULL, NULL, NULL))
|
||||
exit(EXIT_FAILURE);
|
||||
exit_status = executor_execute_command(command, minishell);
|
||||
exit(exit_status);
|
||||
|
||||
@@ -19,6 +19,8 @@ static int open_redirection_target(
|
||||
{
|
||||
if (redirection->type == TOKEN_REDIRECT_IN)
|
||||
return (open(redirection->target, O_RDONLY));
|
||||
if (redirection->type == TOKEN_HEREDOC)
|
||||
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)
|
||||
@@ -27,73 +29,74 @@ static int open_redirection_target(
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static bool backup_stream_if_needed(
|
||||
const t_redirection *redirection,
|
||||
int *saved_stdin,
|
||||
int *saved_stdout
|
||||
static bool backup_fd_if_needed(
|
||||
int io_number,
|
||||
int *saved_fd
|
||||
)
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (saved_fd == NULL || *saved_fd != -1)
|
||||
return (true);
|
||||
*saved_fd = dup(io_number);
|
||||
if (*saved_fd == -1)
|
||||
return (perror("dup"), false);
|
||||
return (true);
|
||||
}
|
||||
|
||||
static bool apply_redirection(
|
||||
const t_redirection *redirection,
|
||||
int *saved_stdin,
|
||||
int *saved_stdout,
|
||||
int *saved_stderr
|
||||
)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (redirection == NULL || redirection->target == NULL)
|
||||
return (false);
|
||||
if ((redirection->io_number == STDIN_FILENO
|
||||
&& !backup_fd_if_needed(STDIN_FILENO, saved_stdin))
|
||||
|| (redirection->io_number == STDOUT_FILENO
|
||||
&& !backup_fd_if_needed(STDOUT_FILENO, saved_stdout))
|
||||
|| (redirection->io_number == STDERR_FILENO
|
||||
&& !backup_fd_if_needed(STDERR_FILENO, saved_stderr)))
|
||||
return (false);
|
||||
fd = open_redirection_target(redirection);
|
||||
if (fd == -1)
|
||||
return (perror(redirection->target), false);
|
||||
if (dup2(fd, redirection->io_number) == -1)
|
||||
return (close(fd), perror("dup2"), false);
|
||||
return (close(fd), true);
|
||||
}
|
||||
|
||||
bool executor_apply_redirections(
|
||||
const t_command *command,
|
||||
int *saved_stdin,
|
||||
int *saved_stdout
|
||||
int *saved_stdout,
|
||||
int *saved_stderr
|
||||
)
|
||||
{
|
||||
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)
|
||||
if (saved_stderr != NULL)
|
||||
*saved_stderr = -1;
|
||||
if (command == 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);
|
||||
while (node != NULL
|
||||
&& apply_redirection((t_redirection *)node->content,
|
||||
saved_stdin, saved_stdout, saved_stderr))
|
||||
node = node->next;
|
||||
}
|
||||
return (true);
|
||||
return (node == NULL);
|
||||
}
|
||||
|
||||
void executor_restore_redirections(
|
||||
int saved_stdin,
|
||||
int saved_stdout
|
||||
int saved_stdout,
|
||||
int saved_stderr
|
||||
)
|
||||
{
|
||||
if (saved_stdin != -1)
|
||||
@@ -108,4 +111,10 @@ void executor_restore_redirections(
|
||||
perror("dup2");
|
||||
close(saved_stdout);
|
||||
}
|
||||
if (saved_stderr != -1)
|
||||
{
|
||||
if (dup2(saved_stderr, STDERR_FILENO) == -1)
|
||||
perror("dup2");
|
||||
close(saved_stderr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2025/10/20 20:51:33 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/13 20:12:07 by sede-san ### ########.fr */
|
||||
/* Updated: 2026/02/14 12:57:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -15,29 +15,22 @@
|
||||
#include "parser.h"
|
||||
#include "executor.h"
|
||||
|
||||
static void set_prompts(
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
minishell->prompt.ps1 = DEFAULT_PS1;
|
||||
minishell->prompt.ps2 = DEFAULT_PS2;
|
||||
}
|
||||
|
||||
void minishell_init(
|
||||
t_minishell *minishell,
|
||||
char **envp
|
||||
){
|
||||
ft_bzero(minishell, sizeof(t_minishell));
|
||||
set_intp(minishell);
|
||||
set_envp(envp, minishell);
|
||||
set_builtins(minishell);
|
||||
set_prompts(minishell);
|
||||
if (minishell->variables.environment == NULL || minishell->builtins == NULL)
|
||||
minishell_clear(minishell);
|
||||
}
|
||||
|
||||
void minishell_run(
|
||||
t_minishell *minishell
|
||||
){
|
||||
)
|
||||
{
|
||||
char *line;
|
||||
t_list *commands;
|
||||
|
||||
@@ -47,7 +40,7 @@ void minishell_run(
|
||||
while (!minishell->exit)
|
||||
{
|
||||
if (isatty(STDIN_FILENO))
|
||||
line = readline(minishell->prompt.ps1);
|
||||
line = readline(get_var("PS1", minishell));
|
||||
else
|
||||
line = get_next_line(STDIN_FILENO);
|
||||
handle_sigint_status(minishell);
|
||||
@@ -67,6 +60,8 @@ void minishell_clear(
|
||||
t_minishell *minishell
|
||||
){
|
||||
rl_clear_history();
|
||||
if (minishell->variables.internal != NULL)
|
||||
ft_hashmap_clear(&minishell->variables.internal, free);
|
||||
if (minishell->variables.environment != NULL)
|
||||
ft_hashmap_clear(&minishell->variables.environment, free);
|
||||
if (minishell->builtins != NULL)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* 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 */
|
||||
/* Updated: 2026/02/14 13:03:15 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -31,6 +31,7 @@ bool handle_eof(
|
||||
return (false);
|
||||
if (isatty(STDIN_FILENO))
|
||||
ft_putendl("exit");
|
||||
free(line);
|
||||
minishell->exit = true;
|
||||
return (true);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "errors.h"
|
||||
|
||||
static t_list *parse_tokens(t_list *tokens);
|
||||
static bool parse_add_command(t_list **commands, t_list **current_token);
|
||||
static bool parse_advance_token(t_list **commands, t_list **current_token);
|
||||
|
||||
/**
|
||||
* @brief Converts a command line string into a list of commands.
|
||||
@@ -52,9 +54,7 @@ static t_list *parse_tokens(
|
||||
)
|
||||
{
|
||||
t_list *commands;
|
||||
t_command *command;
|
||||
t_list *current_token;
|
||||
t_list *new_command;
|
||||
|
||||
if (tokens == NULL)
|
||||
return (NULL);
|
||||
@@ -63,32 +63,49 @@ static t_list *parse_tokens(
|
||||
if (((t_token *)current_token->content)->type == TOKEN_PIPE)
|
||||
return (syntax_error_unexpected_token(
|
||||
(t_token *)current_token->content), NULL);
|
||||
while (current_token != NULL)
|
||||
{
|
||||
command = command_new(¤t_token);
|
||||
if (command == NULL)
|
||||
{
|
||||
ft_lstclear(&commands, (void (*)(void *))command_clear);
|
||||
while (current_token != NULL && parse_add_command(&commands, ¤t_token)
|
||||
&& parse_advance_token(&commands, ¤t_token))
|
||||
continue ;
|
||||
if (current_token != NULL)
|
||||
return (NULL);
|
||||
}
|
||||
return (commands);
|
||||
}
|
||||
|
||||
static bool parse_add_command(
|
||||
t_list **commands,
|
||||
t_list **current_token
|
||||
)
|
||||
{
|
||||
t_command *command;
|
||||
t_list *new_command;
|
||||
|
||||
command = command_new(current_token);
|
||||
if (command == NULL)
|
||||
return (ft_lstclear(commands, (void (*)(void *))command_clear), false);
|
||||
new_command = ft_lstnew(command);
|
||||
if (new_command == NULL)
|
||||
{
|
||||
command_clear(command);
|
||||
ft_lstclear(&commands, (void (*)(void *))command_clear);
|
||||
return (malloc_error(), NULL);
|
||||
return (ft_lstclear(commands, (void (*)(void *))command_clear),
|
||||
malloc_error(), false);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
return (commands);
|
||||
ft_lstadd_back(commands, new_command);
|
||||
return (true);
|
||||
}
|
||||
|
||||
static bool parse_advance_token(
|
||||
t_list **commands,
|
||||
t_list **current_token
|
||||
)
|
||||
{
|
||||
if (*current_token == NULL)
|
||||
return (true);
|
||||
if ((*current_token)->next == NULL)
|
||||
{
|
||||
ft_lstclear(commands, (void (*)(void *))command_clear);
|
||||
return (syntax_error_unexpected_token(
|
||||
(t_token *)(*current_token)->content), false);
|
||||
}
|
||||
*current_token = (*current_token)->next;
|
||||
return (true);
|
||||
}
|
||||
|
||||
46
src/parser/parser_clear.c
Normal file
46
src/parser/parser_clear.c
Normal file
@@ -0,0 +1,46 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* parser_clear.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 16:10:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 16:10:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "parser.h"
|
||||
|
||||
static 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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -58,10 +58,18 @@ void command_add_tokens(
|
||||
)
|
||||
{
|
||||
t_token *token;
|
||||
int io_number;
|
||||
|
||||
token = (t_token *)(*tokens)->content;
|
||||
if (parser_token_is_fd_prefix(*tokens, &io_number))
|
||||
{
|
||||
*tokens = (*tokens)->next;
|
||||
token = (t_token *)(*tokens)->content;
|
||||
redirection_add_with_fd(tokens, token, command, io_number);
|
||||
return ;
|
||||
}
|
||||
if (is_redirection(token))
|
||||
redirection_add(tokens, token, command);
|
||||
redirection_add_with_fd(tokens, token, command, -1);
|
||||
else
|
||||
words_add(tokens, command);
|
||||
}
|
||||
|
||||
54
src/parser/parser_command_fd.c
Normal file
54
src/parser/parser_command_fd.c
Normal file
@@ -0,0 +1,54 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* parser_command_fd.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 16:40:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 16:40:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "parser.h"
|
||||
#include <limits.h>
|
||||
|
||||
static bool parse_io_number(const char *value, int *io_number)
|
||||
{
|
||||
long number;
|
||||
size_t i;
|
||||
|
||||
if (value == NULL || value[0] == '\0')
|
||||
return (false);
|
||||
number = 0;
|
||||
i = 0;
|
||||
while (value[i] != '\0')
|
||||
{
|
||||
if (!ft_isdigit(value[i]))
|
||||
return (false);
|
||||
if (number > (INT_MAX - (value[i] - '0')) / 10)
|
||||
return (false);
|
||||
number = (number * 10) + (value[i] - '0');
|
||||
i++;
|
||||
}
|
||||
*io_number = (int)number;
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool parser_token_is_fd_prefix(
|
||||
t_list *token_node,
|
||||
int *io_number
|
||||
)
|
||||
{
|
||||
t_token *token;
|
||||
t_token *next_token;
|
||||
|
||||
if (token_node == NULL || token_node->next == NULL)
|
||||
return (false);
|
||||
token = (t_token *)token_node->content;
|
||||
next_token = (t_token *)token_node->next->content;
|
||||
if (token == NULL || next_token == NULL || token->type != TOKEN_WORD
|
||||
|| !is_redirection(next_token))
|
||||
return (false);
|
||||
return (parse_io_number(token->value, io_number));
|
||||
}
|
||||
@@ -6,92 +6,109 @@
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/13 21:24:45 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/13 21:24:45 by sede-san ### ########.fr */
|
||||
/* Updated: 2026/02/14 06:56:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "parser.h"
|
||||
#include "errors.h"
|
||||
#include "parser_expand_internal.h"
|
||||
|
||||
static char *replace_value(char *original, char *value, int start, int end);
|
||||
static void expand_variable(char **argument, int *i, t_minishell *minishell);
|
||||
static void expand_argument(char **argument, t_minishell *minishell);
|
||||
|
||||
static char *replace_value(
|
||||
char *original,
|
||||
char *value,
|
||||
int start,
|
||||
int end
|
||||
static void command_clear_argv_expand(
|
||||
t_command *command
|
||||
)
|
||||
{
|
||||
const char *before = ft_substr(original, 0, start);
|
||||
const char *after = ft_substr(original, end, ft_strlen(original) - end);
|
||||
const char *expanded = ft_strnjoin(3, before, value, after);
|
||||
|
||||
free((char *)before);
|
||||
free((char *)after);
|
||||
return ((char *)expanded);
|
||||
}
|
||||
|
||||
static void expand_variable(
|
||||
char **argument,
|
||||
int *i,
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
char *expanded;
|
||||
char *variable_name;
|
||||
char *variable_value;
|
||||
const int start = *i + 1;
|
||||
int end;
|
||||
|
||||
end = start;
|
||||
while (ft_isalnum((*argument)[end]) || (*argument)[end] == '_')
|
||||
end++;
|
||||
variable_name = ft_substr(*argument, start, end - start);
|
||||
if (variable_name == NULL)
|
||||
return (minishell->exit = true, malloc_error());
|
||||
variable_value = get_env(variable_name, minishell);
|
||||
free(variable_name);
|
||||
if (variable_value == NULL)
|
||||
variable_value = "";
|
||||
expanded = replace_value(*argument, variable_value, start - 1, end);
|
||||
if (expanded == NULL)
|
||||
return (minishell->exit = true, malloc_error());
|
||||
*i += ft_strlen(variable_value);
|
||||
free(*argument);
|
||||
*argument = expanded;
|
||||
}
|
||||
|
||||
static void expand_argument(
|
||||
char **argument,
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
bool in_single_quote;
|
||||
bool in_double_quote;
|
||||
int i;
|
||||
|
||||
in_single_quote = false;
|
||||
in_double_quote = false;
|
||||
i = 0;
|
||||
while ((*argument)[i] != '\0')
|
||||
while (i < command->argc)
|
||||
{
|
||||
if ((*argument)[i] == '$' && !in_single_quote)
|
||||
{
|
||||
expand_variable(argument, &i, minishell);
|
||||
if (*argument == NULL)
|
||||
return (minishell->exit = true, malloc_error());
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((*argument)[i] == '\'' && !in_double_quote)
|
||||
in_single_quote = !in_single_quote;
|
||||
else if ((*argument)[i] == '"' && !in_single_quote)
|
||||
in_double_quote = !in_double_quote;
|
||||
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
|
||||
)
|
||||
{
|
||||
t_redirection *redirection;
|
||||
char *expanded;
|
||||
|
||||
while (redirections != NULL)
|
||||
{
|
||||
redirection = (t_redirection *)redirections->content;
|
||||
expanded = parser_expand_word(redirection->target, minishell,
|
||||
true);
|
||||
if (expanded == NULL)
|
||||
return (false);
|
||||
free(redirection->target);
|
||||
redirection->target = expanded;
|
||||
redirections = redirections->next;
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
void expand(
|
||||
@@ -99,24 +116,23 @@ void expand(
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
t_list *current_command;
|
||||
t_list *current;
|
||||
t_command *command;
|
||||
int i;
|
||||
|
||||
current_command = *commands;
|
||||
while (current_command != NULL)
|
||||
if (commands == NULL || *commands == NULL)
|
||||
return ;
|
||||
current = *commands;
|
||||
while (current != NULL)
|
||||
{
|
||||
command = (t_command *)current_command->content;
|
||||
i = 0;
|
||||
while (i < command->argc)
|
||||
command = (t_command *)current->content;
|
||||
if (!expand_argv(command, minishell)
|
||||
|| !expand_redirections(command->redirections, minishell)
|
||||
|| !expand_redirections(command->heredocs, minishell))
|
||||
{
|
||||
expand_argument(&command->argv[i], minishell);
|
||||
if (command->argv[i] == NULL)
|
||||
ft_lstclear(commands, (void (*)(void *))command_clear);
|
||||
i++;
|
||||
*commands = NULL;
|
||||
return ;
|
||||
}
|
||||
if (command == NULL)
|
||||
return (ft_lstclear(commands, (void (*)(void *))command_clear));
|
||||
current_command = current_command->next;
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
106
src/parser/parser_expand_fields.c
Normal file
106
src/parser/parser_expand_fields.c
Normal file
@@ -0,0 +1,106 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* 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);
|
||||
}
|
||||
|
||||
static bool set_empty_word_field(
|
||||
t_list **fields,
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
t_list *node;
|
||||
|
||||
node = ft_lstnew(ft_strdup(""));
|
||||
if (node == NULL || node->content == NULL)
|
||||
return (free(node), parser_expand_malloc_error(minishell), false);
|
||||
*fields = node;
|
||||
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;
|
||||
if (word[0] == '\0')
|
||||
return (set_empty_word_field(fields, minishell));
|
||||
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));
|
||||
}
|
||||
85
src/parser/parser_expand_fields_escape.c
Normal file
85
src/parser/parser_expand_fields_escape.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* parser_expand_fields_escape.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 16:40:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 16:40:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "parser_expand_internal.h"
|
||||
|
||||
static bool is_backslash_escaped_in_double_quote(
|
||||
char c
|
||||
)
|
||||
{
|
||||
return (c == '$' || c == '\"' || c == '\\');
|
||||
}
|
||||
|
||||
static char get_escaped_char(
|
||||
const char *word,
|
||||
size_t *i
|
||||
)
|
||||
{
|
||||
char value;
|
||||
|
||||
if (word[*i + 1] == '\0')
|
||||
value = word[(*i)++];
|
||||
else
|
||||
{
|
||||
value = word[*i + 1];
|
||||
*i += 2;
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
bool parser_fields_handle_backslash(
|
||||
const char *word,
|
||||
size_t *i,
|
||||
t_fields_ctx ctx,
|
||||
bool *handled
|
||||
)
|
||||
{
|
||||
char value[2];
|
||||
|
||||
*handled = false;
|
||||
if (word[*i] != '\\' || *ctx.in_single_quote)
|
||||
return (true);
|
||||
if (*ctx.in_double_quote
|
||||
&& !is_backslash_escaped_in_double_quote(word[*i + 1]))
|
||||
return (true);
|
||||
*handled = true;
|
||||
value[0] = get_escaped_char(word, i);
|
||||
value[1] = '\0';
|
||||
if (!parser_fields_append_text(ctx.current, value, ctx.minishell))
|
||||
return (false);
|
||||
*ctx.touched = true;
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool parser_fields_expand_tilde(
|
||||
const char *word,
|
||||
size_t *i,
|
||||
t_fields_ctx ctx,
|
||||
bool *handled
|
||||
)
|
||||
{
|
||||
char *home;
|
||||
|
||||
*handled = false;
|
||||
if (word[*i] != '~' || *ctx.in_single_quote || *ctx.in_double_quote
|
||||
|| *i != 0 || (word[*i + 1] != '\0' && word[*i + 1] != '/'))
|
||||
return (true);
|
||||
home = get_var("HOME", ctx.minishell);
|
||||
if (home == NULL)
|
||||
return (true);
|
||||
*handled = true;
|
||||
(*i)++;
|
||||
if (!parser_fields_append_text(ctx.current, home, ctx.minishell))
|
||||
return (false);
|
||||
*ctx.touched = true;
|
||||
return (true);
|
||||
}
|
||||
113
src/parser/parser_expand_fields_step.c
Normal file
113
src/parser/parser_expand_fields_step.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* 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 (!parser_fields_handle_backslash(word, i, ctx, &handled))
|
||||
return (false);
|
||||
if (handled)
|
||||
return (true);
|
||||
if (!parser_fields_expand_tilde(word, i, ctx, &handled))
|
||||
return (false);
|
||||
if (handled)
|
||||
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);
|
||||
}
|
||||
100
src/parser/parser_expand_fields_utils.c
Normal file
100
src/parser/parser_expand_fields_utils.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* 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);
|
||||
}
|
||||
67
src/parser/parser_expand_internal.h
Normal file
67
src/parser/parser_expand_internal.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* 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);
|
||||
bool parser_fields_handle_backslash(const char *word, size_t *i,
|
||||
t_fields_ctx ctx, bool *handled);
|
||||
bool parser_fields_expand_tilde(const char *word, size_t *i,
|
||||
t_fields_ctx ctx, bool *handled);
|
||||
|
||||
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
|
||||
82
src/parser/parser_expand_variable.c
Normal file
82
src/parser/parser_expand_variable.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* 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));
|
||||
}
|
||||
104
src/parser/parser_expand_word.c
Normal file
104
src/parser/parser_expand_word.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* 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);
|
||||
}
|
||||
@@ -13,7 +13,9 @@
|
||||
#include "parser.h"
|
||||
#include "errors.h"
|
||||
|
||||
static t_redirection *redirection_new(t_list **tokens);
|
||||
static t_redirection *redirection_new(t_list **tokens, int io_number);
|
||||
static bool redirection_read_target(t_redirection *rd, t_list **tk);
|
||||
static bool has_single_quote(const char *value);
|
||||
|
||||
/**
|
||||
* @brief Creates a new redirection from a list of tokens.
|
||||
@@ -23,7 +25,8 @@ static t_redirection *redirection_new(t_list **tokens);
|
||||
* @return A new redirection or `NULL` on error.
|
||||
*/
|
||||
static t_redirection *redirection_new(
|
||||
t_list **tokens
|
||||
t_list **tokens,
|
||||
int io_number
|
||||
)
|
||||
{
|
||||
t_redirection *redirection;
|
||||
@@ -34,28 +37,62 @@ static t_redirection *redirection_new(
|
||||
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;
|
||||
redirection->heredoc_expand = true;
|
||||
redirection->heredoc_ready = false;
|
||||
if (io_number < 0)
|
||||
redirection->io_number = STDOUT_FILENO;
|
||||
else
|
||||
redirection->io_number = io_number;
|
||||
if (io_number < 0 && (token->type == TOKEN_REDIRECT_IN
|
||||
|| token->type == TOKEN_HEREDOC))
|
||||
redirection->io_number = STDIN_FILENO;
|
||||
if (!redirection_read_target(redirection, tokens))
|
||||
return (free(redirection), NULL);
|
||||
return (redirection);
|
||||
}
|
||||
|
||||
static bool redirection_read_target(
|
||||
t_redirection *rd,
|
||||
t_list **tk
|
||||
)
|
||||
{
|
||||
t_token *token;
|
||||
|
||||
*tk = (*tk)->next;
|
||||
if (*tk == NULL)
|
||||
return (syntax_error_unexpected_token(NULL), false);
|
||||
token = (t_token *)(*tk)->content;
|
||||
if (token->type != TOKEN_WORD)
|
||||
{
|
||||
free(redirection);
|
||||
while (*tokens != NULL)
|
||||
*tokens = (*tokens)->next;
|
||||
return (syntax_error_unexpected_token(token), NULL);
|
||||
while (*tk != NULL)
|
||||
*tk = (*tk)->next;
|
||||
return (syntax_error_unexpected_token(token), false);
|
||||
}
|
||||
redirection->target = ft_strdup(token->value);
|
||||
if (redirection->target == NULL)
|
||||
if (rd->type == TOKEN_HEREDOC && has_single_quote(token->value))
|
||||
rd->heredoc_expand = false;
|
||||
rd->target = ft_strdup(token->value);
|
||||
if (rd->target == NULL)
|
||||
return (malloc_error(), false);
|
||||
*tk = (*tk)->next;
|
||||
return (true);
|
||||
}
|
||||
|
||||
static bool has_single_quote(
|
||||
const char *value
|
||||
)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (value == NULL)
|
||||
return (false);
|
||||
i = 0;
|
||||
while (value[i] != '\0')
|
||||
{
|
||||
free(redirection);
|
||||
return (malloc_error(), NULL);
|
||||
if (value[i] == '\'')
|
||||
return (true);
|
||||
i++;
|
||||
}
|
||||
*tokens = (*tokens)->next;
|
||||
return (redirection);
|
||||
return (false);
|
||||
}
|
||||
|
||||
void redirection_clear(
|
||||
@@ -69,16 +106,17 @@ void redirection_clear(
|
||||
}
|
||||
}
|
||||
|
||||
void redirection_add(
|
||||
void redirection_add_with_fd(
|
||||
t_list **tokens,
|
||||
t_token *token,
|
||||
t_command **command
|
||||
t_command **command,
|
||||
int io_number
|
||||
)
|
||||
{
|
||||
t_redirection *redirection;
|
||||
t_list *redirection_tokens;
|
||||
|
||||
redirection = redirection_new(tokens);
|
||||
redirection = redirection_new(tokens, io_number);
|
||||
if (redirection == NULL)
|
||||
{
|
||||
command_clear(*command);
|
||||
@@ -91,8 +129,6 @@ void redirection_add(
|
||||
free(redirection);
|
||||
return (malloc_error());
|
||||
}
|
||||
if (token->type == TOKEN_HEREDOC)
|
||||
ft_lstadd_back(&(*command)->heredocs, redirection_tokens);
|
||||
else
|
||||
(void)token;
|
||||
ft_lstadd_back(&(*command)->redirections, redirection_tokens);
|
||||
}
|
||||
|
||||
@@ -6,48 +6,86 @@
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/13 21:29:44 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/13 21:29:44 by sede-san ### ########.fr */
|
||||
/* 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(
|
||||
static bool args_add_word(
|
||||
t_list **args,
|
||||
size_t argc
|
||||
const char *value
|
||||
)
|
||||
{
|
||||
t_list *node;
|
||||
char *dup;
|
||||
|
||||
dup = ft_strdup(value);
|
||||
if (dup == NULL)
|
||||
return (false);
|
||||
node = ft_lstnew(dup);
|
||||
if (node == NULL)
|
||||
return (free(dup), false);
|
||||
ft_lstadd_back(args, node);
|
||||
return (true);
|
||||
}
|
||||
|
||||
static bool words_collect(
|
||||
t_list *arg,
|
||||
t_list **args,
|
||||
int *new_argc,
|
||||
t_list **end
|
||||
)
|
||||
{
|
||||
t_token *token;
|
||||
int io_number;
|
||||
|
||||
while (arg != NULL)
|
||||
{
|
||||
token = (t_token *)arg->content;
|
||||
if (token->type != TOKEN_WORD || parser_token_is_fd_prefix(arg,
|
||||
&io_number))
|
||||
break ;
|
||||
if (!args_add_word(args, token->value))
|
||||
return (false);
|
||||
(*new_argc)++;
|
||||
arg = arg->next;
|
||||
}
|
||||
*end = arg;
|
||||
return (true);
|
||||
}
|
||||
|
||||
static bool command_append_words(
|
||||
t_command *command,
|
||||
t_list **args,
|
||||
int new_argc
|
||||
)
|
||||
{
|
||||
char **argv;
|
||||
t_list *arg;
|
||||
size_t i;
|
||||
t_list *current;
|
||||
int i;
|
||||
|
||||
argv = (char **)malloc(sizeof(char *) * (argc + 1));
|
||||
argv = (char **)malloc(sizeof(char *) * (command->argc + new_argc + 1));
|
||||
if (argv == NULL)
|
||||
return (NULL);
|
||||
return (false);
|
||||
i = 0;
|
||||
arg = *args;
|
||||
while (arg != NULL)
|
||||
while (i < command->argc)
|
||||
{
|
||||
argv[i] = (char *)arg->content;
|
||||
arg = arg->next;
|
||||
argv[i] = command->argv[i];
|
||||
i++;
|
||||
}
|
||||
current = *args;
|
||||
while (current != NULL)
|
||||
{
|
||||
argv[i++] = (char *)current->content;
|
||||
current = current->next;
|
||||
}
|
||||
argv[i] = NULL;
|
||||
free(command->argv);
|
||||
ft_lstclear_nodes(args);
|
||||
return (argv);
|
||||
command->argv = argv;
|
||||
command->argc += new_argc;
|
||||
return (true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,59 +101,12 @@ void words_add(
|
||||
)
|
||||
{
|
||||
t_list *args;
|
||||
t_list *arg;
|
||||
t_token *token;
|
||||
int new_argc;
|
||||
|
||||
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);
|
||||
}
|
||||
new_argc = 0;
|
||||
if (!words_collect(*tokens, &args, &new_argc, tokens)
|
||||
|| !command_append_words(*command, &args, new_argc))
|
||||
return (ft_lstclear(&args, free), command_clear(*command),
|
||||
*command = NULL, (void)0);
|
||||
}
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* environment.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2025/12/01 09:12:39 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/08 19:44:15 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "minishell.h"
|
||||
#include "core.h"
|
||||
|
||||
/**
|
||||
* @brief Parses and stores environment variables from envp array into a hashmap
|
||||
*
|
||||
* This function iterates through the environment variables array (envp) and
|
||||
* 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 envp Array of environment variable strings in "NAME=value" format
|
||||
* @param msh Pointer to the minishell structure containing the environment
|
||||
* hashmap
|
||||
*
|
||||
* @note The function assumes envp strings are in the standard format
|
||||
* "NAME=value"
|
||||
*/
|
||||
void set_envp(
|
||||
char **envp,
|
||||
t_minishell *msh
|
||||
) {
|
||||
char *equal_sign;
|
||||
char *key;
|
||||
char *value;
|
||||
|
||||
if (msh == NULL || envp == NULL)
|
||||
return ;
|
||||
msh->variables.environment
|
||||
= ft_hashmap_new(32, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
||||
if (msh->variables.environment == NULL)
|
||||
return ;
|
||||
while (*envp != NULL)
|
||||
{
|
||||
equal_sign = ft_strchr(*envp, '=');
|
||||
if (equal_sign == NULL)
|
||||
{
|
||||
key = ft_strdup(*envp);
|
||||
value = ft_strdup("");
|
||||
}
|
||||
else
|
||||
{
|
||||
key = ft_substr(*envp, 0, equal_sign - *envp);
|
||||
value = ft_strdup(equal_sign + 1);
|
||||
}
|
||||
if (key == NULL || value == NULL)
|
||||
{
|
||||
free(key);
|
||||
free(value);
|
||||
ft_hashmap_clear(&msh->variables.environment, free);
|
||||
return ;
|
||||
}
|
||||
ft_hashmap_put(msh->variables.environment, key, value);
|
||||
envp++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets an environment variable in the minishell's environment hashmap.
|
||||
*
|
||||
* This function adds a new environment variable or updates an existing one
|
||||
* in the minishell's environment hashmap. If the variable already exists,
|
||||
* the old value is freed to prevent memory leaks. If the variable is new,
|
||||
* a duplicate of the key name is created for storage.
|
||||
*
|
||||
* @param env_name The name of the environment variable to set
|
||||
* @param env_value The value to assign to the environment variable
|
||||
* @param msh Pointer to the minishell structure containing the
|
||||
* environment hashmap
|
||||
*/
|
||||
void set_env(
|
||||
const char *env_name,
|
||||
char *env_value,
|
||||
t_minishell *msh
|
||||
) {
|
||||
t_hashmap *environment;
|
||||
const char *key;
|
||||
char *old_value;
|
||||
|
||||
environment = msh->variables.environment;
|
||||
key = env_name;
|
||||
if (!ft_hashmap_contains_key(environment, key))
|
||||
key = ft_strdup(env_name);
|
||||
old_value = ft_hashmap_put(environment, key, ft_strdup(env_value));
|
||||
if (old_value != NULL)
|
||||
free(old_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts the environment variables hashmap to an envp array format.
|
||||
*
|
||||
* This function extracts all environment variables from the minishell's
|
||||
* environment hashmap and converts them into a NULL-terminated array of
|
||||
* strings in the format "KEY=VALUE".
|
||||
*
|
||||
* @param msh Pointer to the minishell structure containing the environment
|
||||
* variables hashmap.
|
||||
*
|
||||
* @return A dynamically allocated array of strings representing environment
|
||||
* variables in "KEY=VALUE" format, terminated by NULL. Returns NULL
|
||||
* if memory allocation fails. The caller is responsible for freeing
|
||||
* the returned array and its individual string elements using
|
||||
* the `free_envp()` function.
|
||||
*
|
||||
* @note The function allocates memory for both the array and individual
|
||||
* strings using malloc and ft_strnjoin respectively.
|
||||
* @note The returned array size is environment->size + 1 to accommodate
|
||||
* the NULL terminator.
|
||||
*/
|
||||
char **get_envp(
|
||||
t_minishell *msh
|
||||
) {
|
||||
char **envp;
|
||||
t_list *env_list;
|
||||
t_list *env;
|
||||
t_map_entry *entry;
|
||||
size_t i;
|
||||
|
||||
env_list = ft_hashmap_entries(msh->variables.environment);
|
||||
envp = (char **)malloc(
|
||||
(msh->variables.environment->size + 1) * sizeof(char *)
|
||||
);
|
||||
if (envp != NULL)
|
||||
{
|
||||
i = 0;
|
||||
env = env_list;
|
||||
while (env != NULL)
|
||||
{
|
||||
entry = env->content;
|
||||
envp[i++] = ft_strnjoin(3, entry->key, "=", entry->value);
|
||||
env = env->next;
|
||||
}
|
||||
envp[i] = NULL;
|
||||
}
|
||||
ft_lstclear_nodes(&env_list);
|
||||
return (envp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Frees a dynamically allocated environment variables array
|
||||
*
|
||||
* This function deallocates memory for an array of strings that was previously
|
||||
* allocated by `get_envp()`. It iterates through each string in the array,
|
||||
* frees the memory for individual strings, and then frees the array itself.
|
||||
*
|
||||
* @param envp Pointer to the array of environment variable strings to be freed.
|
||||
* Each string in the array should be dynamically allocated.
|
||||
* The array must be NULL-terminated.
|
||||
*/
|
||||
void free_envp(
|
||||
char **envp
|
||||
) {
|
||||
size_t i;
|
||||
|
||||
i = -1;
|
||||
while (envp[++i] != NULL)
|
||||
free(envp[i]);
|
||||
free(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));
|
||||
}
|
||||
149
src/variables/environment/environment.c
Normal file
149
src/variables/environment/environment.c
Normal file
@@ -0,0 +1,149 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* environment.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2025/12/01 09:12:39 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 13:30:34 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "minishell.h"
|
||||
#include "core.h"
|
||||
#include "variables.h"
|
||||
#include "errors.h"
|
||||
|
||||
static char *resolve_key(
|
||||
const char *name,
|
||||
t_hashmap *environment,
|
||||
t_minishell *minishell,
|
||||
bool *owns_key
|
||||
);
|
||||
static char *resolve_value(
|
||||
char *value,
|
||||
t_minishell *minishell
|
||||
);
|
||||
|
||||
/**
|
||||
* @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 name The name of the environment variable to retrieve.
|
||||
* @param minishell Pointer to the minishell object.
|
||||
*
|
||||
* @return The value of the environment variable if found, NULL if not found
|
||||
*/
|
||||
char *get_env(
|
||||
const char *name,
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
return (ft_hashmap_get(minishell->variables.environment, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets an environment variable in the minishell's environment hashmap.
|
||||
*
|
||||
* This function adds a new environment variable or updates an existing one
|
||||
* in the minishell's environment hashmap. If the variable already exists,
|
||||
* the old value is freed to prevent memory leaks. If the variable is new,
|
||||
* a duplicate of the key name is created for storage.
|
||||
*
|
||||
* @param name The name of the environment variable to set
|
||||
* @param value The value to assign to the environment variable
|
||||
* @param minishell Pointer to the minishell object.
|
||||
*/
|
||||
void set_env(
|
||||
const char *name,
|
||||
char *value,
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
t_hashmap *environment;
|
||||
char *key;
|
||||
char *val;
|
||||
char *old_value;
|
||||
bool owns_key;
|
||||
|
||||
environment = minishell->variables.environment;
|
||||
key = resolve_key(name, environment, minishell, &owns_key);
|
||||
if (key == NULL)
|
||||
return ;
|
||||
val = resolve_value(value, minishell);
|
||||
if (val == NULL)
|
||||
{
|
||||
if (owns_key)
|
||||
free(key);
|
||||
return ;
|
||||
}
|
||||
old_value = ft_hashmap_put(environment, key, val);
|
||||
if (old_value != NULL)
|
||||
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);
|
||||
}
|
||||
|
||||
static char *resolve_key(
|
||||
const char *name,
|
||||
t_hashmap *environment,
|
||||
t_minishell *minishell,
|
||||
bool *owns_key
|
||||
)
|
||||
{
|
||||
char *key;
|
||||
|
||||
key = (char *)name;
|
||||
*owns_key = false;
|
||||
if (name == NULL)
|
||||
return (NULL);
|
||||
if (!ft_hashmap_contains_key(environment, name))
|
||||
{
|
||||
key = ft_strdup(name);
|
||||
if (key == NULL)
|
||||
return (minishell->exit = true, malloc_error(), NULL);
|
||||
*owns_key = true;
|
||||
}
|
||||
return (key);
|
||||
}
|
||||
|
||||
static char *resolve_value(
|
||||
char *value,
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
char *val;
|
||||
|
||||
if (value == NULL)
|
||||
val = ft_strdup("");
|
||||
else
|
||||
val = ft_strdup(value);
|
||||
if (val == NULL)
|
||||
return (minishell->exit = true, malloc_error(), NULL);
|
||||
return (val);
|
||||
}
|
||||
121
src/variables/environment/environment_envp.c
Normal file
121
src/variables/environment/environment_envp.c
Normal file
@@ -0,0 +1,121 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* environment_envp.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/14 16:10:00 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 16:10:00 by sede-san ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "minishell.h"
|
||||
#include "core.h"
|
||||
#include "variables.h"
|
||||
#include "errors.h"
|
||||
|
||||
static void fill_envp(
|
||||
char **envp,
|
||||
t_list *env_list
|
||||
)
|
||||
{
|
||||
t_list *env;
|
||||
t_map_entry *entry;
|
||||
size_t i;
|
||||
|
||||
i = 0;
|
||||
env = env_list;
|
||||
while (env != NULL)
|
||||
{
|
||||
entry = env->content;
|
||||
envp[i++] = ft_strnjoin(3, entry->key, "=", entry->value);
|
||||
env = env->next;
|
||||
}
|
||||
envp[i] = NULL;
|
||||
}
|
||||
|
||||
char **get_envp(
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
char **envp;
|
||||
t_list *env_list;
|
||||
|
||||
env_list = ft_hashmap_entries(minishell->variables.environment);
|
||||
envp = (char **)malloc(
|
||||
(minishell->variables.environment->size + 1) * sizeof(char *)
|
||||
);
|
||||
if (envp != NULL)
|
||||
fill_envp(envp, env_list);
|
||||
ft_lstclear_nodes(&env_list);
|
||||
return (envp);
|
||||
}
|
||||
|
||||
static bool import_env_entry(
|
||||
char *entry_text,
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
char *equal;
|
||||
char *key;
|
||||
char *value;
|
||||
size_t key_len;
|
||||
|
||||
equal = ft_strchr(entry_text, '=');
|
||||
if (equal == NULL)
|
||||
return (true);
|
||||
key_len = (size_t)(equal - entry_text);
|
||||
key = ft_substr(entry_text, 0, key_len);
|
||||
value = ft_strdup(equal + 1);
|
||||
if (key == NULL || value == NULL)
|
||||
return (free(key), free(value), minishell->exit = true,
|
||||
malloc_error(), false);
|
||||
set_env(key, value, minishell);
|
||||
free(key);
|
||||
free(value);
|
||||
return (!minishell->exit);
|
||||
}
|
||||
|
||||
static void update_shlvl(
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
char *value;
|
||||
char *new_shlvl;
|
||||
int shlvl;
|
||||
|
||||
value = get_env("SHLVL", minishell);
|
||||
shlvl = 0;
|
||||
if (value != NULL)
|
||||
shlvl = ft_atoi(value);
|
||||
if (shlvl < 0)
|
||||
shlvl = 0;
|
||||
new_shlvl = ft_itoa(shlvl + 1);
|
||||
if (new_shlvl == NULL)
|
||||
return ((void)(minishell->exit = true), malloc_error());
|
||||
set_env("SHLVL", new_shlvl, minishell);
|
||||
free(new_shlvl);
|
||||
}
|
||||
|
||||
void set_envp(
|
||||
char **envp,
|
||||
t_minishell *minishell
|
||||
)
|
||||
{
|
||||
t_hashmap *environment;
|
||||
|
||||
if (minishell == NULL || envp == NULL)
|
||||
return ;
|
||||
environment = ft_hashmap_new(32, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
||||
if (environment == NULL)
|
||||
return ;
|
||||
minishell->variables.environment = environment;
|
||||
while (*envp != NULL)
|
||||
{
|
||||
if (!import_env_entry(*envp, minishell))
|
||||
return ;
|
||||
envp++;
|
||||
}
|
||||
update_shlvl(minishell);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* environment_unset.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/09 22:16:00 by codex #+# #+# */
|
||||
/* Updated: 2026/02/09 22:16:00 by codex ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "minishell.h"
|
||||
#include "core.h"
|
||||
|
||||
void unset_env(
|
||||
const char *env_name,
|
||||
t_minishell *msh
|
||||
){
|
||||
t_hashmap *new_env;
|
||||
t_list *entries;
|
||||
t_list *current;
|
||||
t_map_entry *entry;
|
||||
|
||||
new_env = ft_hashmap_new(32, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
||||
if (new_env == NULL)
|
||||
return ;
|
||||
entries = ft_hashmap_entries(msh->variables.environment);
|
||||
current = entries;
|
||||
while (current != NULL)
|
||||
{
|
||||
entry = current->content;
|
||||
if (ft_strcmp(entry->key, env_name) != 0)
|
||||
ft_hashmap_put(new_env,
|
||||
ft_strdup(entry->key), ft_strdup(entry->value));
|
||||
current = current->next;
|
||||
}
|
||||
ft_lstclear_nodes(&entries);
|
||||
ft_hashmap_clear(&msh->variables.environment, free);
|
||||
msh->variables.environment = new_env;
|
||||
}
|
||||
123
src/variables/internal/internal.c
Normal file
123
src/variables/internal/internal.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* internal.c :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/02/13 21:29:43 by sede-san #+# #+# */
|
||||
/* Updated: 2026/02/14 15:17:17 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("PS0", "", minishell);
|
||||
set_int("PS1", DEFAULT_PS1, minishell);
|
||||
set_int("PS2", DEFAULT_PS2, minishell);
|
||||
set_int("MINISHELL", "minishell", minishell);
|
||||
set_int("MINISHELL_VERSION", MINISHELL_VERSION, minishell);
|
||||
}
|
||||
50
src/variables/variables.c
Normal file
50
src/variables/variables.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* 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);
|
||||
}
|
||||
Reference in New Issue
Block a user