Compare commits
4 Commits
e983f7fe64
...
feature/bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d39eca2c94 | ||
|
|
084fa4759c | ||
|
|
778e0c0481 | ||
| 280fa51f94 |
48
AGENTS.md
Normal file
48
AGENTS.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# 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`.
|
||||||
|
- `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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
12
Makefile
12
Makefile
@@ -3,10 +3,10 @@
|
|||||||
# ::: :::::::: #
|
# ::: :::::::: #
|
||||||
# Makefile :+: :+: :+: #
|
# Makefile :+: :+: :+: #
|
||||||
# +:+ +:+ +:+ #
|
# +:+ +:+ +:+ #
|
||||||
# By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ #
|
# By: marcnava <marcnava@student.42madrid.com +#+ +:+ +#+ #
|
||||||
# +#+#+#+#+#+ +#+ #
|
# +#+#+#+#+#+ +#+ #
|
||||||
# Created: 2025/07/30 20:22:21 by sede-san #+# #+# #
|
# Created: 2025/07/30 20:22:21 by sede-san #+# #+# #
|
||||||
# Updated: 2025/12/01 14:05:56 by sede-san ### ########.fr #
|
# Updated: 2026/02/09 22:44:34 by marcnava ### ########.fr #
|
||||||
# #
|
# #
|
||||||
# **************************************************************************** #
|
# **************************************************************************** #
|
||||||
|
|
||||||
@@ -17,15 +17,15 @@ NAME = minishell
|
|||||||
# ************************** Compilation variables *************************** #
|
# ************************** Compilation variables *************************** #
|
||||||
|
|
||||||
CC = cc
|
CC = cc
|
||||||
CFLAGS = -Wall -Wextra -Werror
|
CFLAGS = -Wall -Wextra #-Werror
|
||||||
HEADERS = -I $(INCLUDE_PATH) $(LIBS_INCLUDE)
|
HEADERS = -I $(INCLUDE_PATH) $(LIBS_INCLUDE)
|
||||||
|
|
||||||
ifeq ($(DEBUG), lldb) # debug with LLDB
|
ifeq ($(DEBUG), lldb) # debug with LLDB
|
||||||
CFLAGS += -g3
|
CFLAGS = -g3
|
||||||
else ifeq ($(DEBUG), valgrind) # debug with valgrind
|
else ifeq ($(DEBUG), valgrind) # debug with valgrind
|
||||||
CFLAGS += -g3
|
CFLAGS = -g3
|
||||||
else ifeq ($(DEBUG), address) # use AdressSanitize
|
else ifeq ($(DEBUG), address) # use AdressSanitize
|
||||||
CFLAGS += -fsanitize=address -g3
|
CFLAGS = -fsanitize=address -g3
|
||||||
else # apply optimization flags if no debugging is being done
|
else # apply optimization flags if no debugging is being done
|
||||||
CFLAGS += -O3
|
CFLAGS += -O3
|
||||||
endif
|
endif
|
||||||
|
|||||||
177
allowed.txt
Normal file
177
allowed.txt
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
[minishell_allowed]
|
||||||
|
readline
|
||||||
|
rl_clear_history
|
||||||
|
rl_on_new_line
|
||||||
|
rl_replace_line
|
||||||
|
rl_redisplay
|
||||||
|
add_history
|
||||||
|
printf
|
||||||
|
malloc
|
||||||
|
free
|
||||||
|
write
|
||||||
|
access
|
||||||
|
open
|
||||||
|
read
|
||||||
|
close
|
||||||
|
fork
|
||||||
|
wait
|
||||||
|
waitpid
|
||||||
|
wait3
|
||||||
|
wait4
|
||||||
|
signal
|
||||||
|
sigaction
|
||||||
|
sigemptyset
|
||||||
|
sigaddset
|
||||||
|
kill
|
||||||
|
exit
|
||||||
|
getcwd
|
||||||
|
chdir
|
||||||
|
stat
|
||||||
|
lstat
|
||||||
|
fstat
|
||||||
|
unlink
|
||||||
|
execve
|
||||||
|
dup
|
||||||
|
dup2
|
||||||
|
pipe
|
||||||
|
opendir
|
||||||
|
readdir
|
||||||
|
closedir
|
||||||
|
strerror
|
||||||
|
perror
|
||||||
|
isatty
|
||||||
|
ttyname
|
||||||
|
ttyslot
|
||||||
|
ioctl
|
||||||
|
getenv
|
||||||
|
tcsetattr
|
||||||
|
tcgetattr
|
||||||
|
tgetent
|
||||||
|
tgetflag
|
||||||
|
tgetnum
|
||||||
|
tgetstr
|
||||||
|
tgoto
|
||||||
|
tputs
|
||||||
|
|
||||||
|
[libft]
|
||||||
|
ft_atoi
|
||||||
|
ft_atoi_base
|
||||||
|
ft_atol
|
||||||
|
ft_bzero
|
||||||
|
ft_calloc
|
||||||
|
ft_cdlstadd_back
|
||||||
|
ft_cdlstadd_front
|
||||||
|
ft_cdlstclear
|
||||||
|
ft_cdlstdelone
|
||||||
|
ft_cdlstiter
|
||||||
|
ft_cdlstlast
|
||||||
|
ft_cdlstmap
|
||||||
|
ft_cdlstnew
|
||||||
|
ft_cdlstsize
|
||||||
|
ft_clstadd_back
|
||||||
|
ft_clstadd_front
|
||||||
|
ft_clstclear
|
||||||
|
ft_clstdelone
|
||||||
|
ft_clstiter
|
||||||
|
ft_clstlast
|
||||||
|
ft_clstmap
|
||||||
|
ft_clstnew
|
||||||
|
ft_clstsize
|
||||||
|
ft_dlstadd_back
|
||||||
|
ft_dlstadd_front
|
||||||
|
ft_dlstclear
|
||||||
|
ft_dlstdelone
|
||||||
|
ft_dlstiter
|
||||||
|
ft_dlstlast
|
||||||
|
ft_dlstmap
|
||||||
|
ft_dlstnew
|
||||||
|
ft_dlstsize
|
||||||
|
ft_eputchar
|
||||||
|
ft_eputendl
|
||||||
|
ft_eputnbr
|
||||||
|
ft_eputstr
|
||||||
|
ft_free
|
||||||
|
ft_free_split
|
||||||
|
ft_hashmap_clear
|
||||||
|
ft_hashmap_clear_keys
|
||||||
|
ft_hashmap_contains_key
|
||||||
|
ft_hashmap_entries
|
||||||
|
ft_hashmap_get
|
||||||
|
ft_hashmap_hashstr
|
||||||
|
ft_hashmap_new
|
||||||
|
ft_hashmap_put
|
||||||
|
ft_hashmap_remove
|
||||||
|
ft_hashmap_strcmp
|
||||||
|
ft_hashstr
|
||||||
|
ft_iabs
|
||||||
|
ft_imin
|
||||||
|
ft_isalnum
|
||||||
|
ft_isalpha
|
||||||
|
ft_isascii
|
||||||
|
ft_iscntrl
|
||||||
|
ft_isdigit
|
||||||
|
ft_islower
|
||||||
|
ft_isprint
|
||||||
|
ft_isspace
|
||||||
|
ft_isupper
|
||||||
|
ft_itoa
|
||||||
|
ft_itoa_base
|
||||||
|
ft_lstadd_back
|
||||||
|
ft_lstadd_front
|
||||||
|
ft_lstclear
|
||||||
|
ft_lstclear_nodes
|
||||||
|
ft_lstdelone
|
||||||
|
ft_lstiter
|
||||||
|
ft_lstlast
|
||||||
|
ft_lstmap
|
||||||
|
ft_lstnew
|
||||||
|
ft_lstsize
|
||||||
|
ft_ltoa
|
||||||
|
ft_memchr
|
||||||
|
ft_memcmp
|
||||||
|
ft_memcpy
|
||||||
|
ft_memmove
|
||||||
|
ft_memset
|
||||||
|
ft_nsplit
|
||||||
|
ft_pow
|
||||||
|
ft_putchar
|
||||||
|
ft_putchar_fd
|
||||||
|
ft_putendl
|
||||||
|
ft_putendl_fd
|
||||||
|
ft_putnbr
|
||||||
|
ft_putnbr_fd
|
||||||
|
ft_putstr
|
||||||
|
ft_putstr_fd
|
||||||
|
ft_realloc
|
||||||
|
ft_split
|
||||||
|
ft_strchr
|
||||||
|
ft_strcmp
|
||||||
|
ft_strdup
|
||||||
|
ft_strisnum
|
||||||
|
ft_striteri
|
||||||
|
ft_strjoin
|
||||||
|
ft_strlcat
|
||||||
|
ft_strlcpy
|
||||||
|
ft_strlen
|
||||||
|
ft_strmapi
|
||||||
|
ft_strncmp
|
||||||
|
ft_strncpy
|
||||||
|
ft_strnjoin
|
||||||
|
ft_strnstr
|
||||||
|
ft_strrchr
|
||||||
|
ft_strtrim
|
||||||
|
ft_substr
|
||||||
|
ft_swap
|
||||||
|
ft_tolower
|
||||||
|
ft_toupper
|
||||||
|
ft_uitoa
|
||||||
|
ft_uitoa_base
|
||||||
|
ft_ultoa_base
|
||||||
|
|
||||||
|
[get_next_line]
|
||||||
|
get_next_line
|
||||||
|
|
||||||
|
[ft_printf]
|
||||||
|
ft_eprintf
|
||||||
|
ft_fprintf
|
||||||
|
ft_printf
|
||||||
151
docs/builtins_fixes.md
Normal file
151
docs/builtins_fixes.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# Correcciones en Builtins
|
||||||
|
|
||||||
|
Este documento resume los fallos detectados en los builtins actuales y la
|
||||||
|
solucion aplicada en codigo.
|
||||||
|
|
||||||
|
## 1) Infraestructura de builtins (`include/builtins.h`, `src/builtins/builtins.c`)
|
||||||
|
|
||||||
|
### Fallo
|
||||||
|
- Uso inconsistente de tipos (`u_int8_t`/`unsigned char` frente a `uint8_t`).
|
||||||
|
- `set_builtins` no comprobaba fallos de registro por builtin (duplicado de
|
||||||
|
clave o insercion en hashmap), pudiendo dejar estado parcial.
|
||||||
|
- `is_builtin` podia dereferenciar punteros nulos.
|
||||||
|
|
||||||
|
### Por que fallaba
|
||||||
|
- `u_int8_t` no es el tipo estandar C99 y depende de plataforma/headers.
|
||||||
|
- Si falla una insercion, la tabla quedaba inicializada parcialmente sin
|
||||||
|
rollback.
|
||||||
|
- En errores de inicializacion, consultar `is_builtin` podia romper.
|
||||||
|
|
||||||
|
### Solucion
|
||||||
|
- Unificacion de firmas a `uint8_t`.
|
||||||
|
- Nuevo helper `register_builtin()` con validacion tras `ft_hashmap_put`.
|
||||||
|
- Si falla cualquier alta: limpieza de `minishell->builtins` y retorno de
|
||||||
|
error.
|
||||||
|
- Guardas nulas en `is_builtin`.
|
||||||
|
|
||||||
|
## 2) `cd` (`src/builtins/cd/cd.c`)
|
||||||
|
|
||||||
|
### Fallo
|
||||||
|
- `cd` con demasiados argumentos devolvia `2` (bash devuelve `1`).
|
||||||
|
- `cd` sin `HOME` acababa llamando a `chdir(NULL)`.
|
||||||
|
- El manejo de error usaba comprobaciones invertidas (`access`) y codigos
|
||||||
|
incorrectos.
|
||||||
|
- No se actualizaban `PWD` y `OLDPWD` tras `chdir` exitoso.
|
||||||
|
- `cd -` (usar `OLDPWD`) no estaba soportado.
|
||||||
|
|
||||||
|
### Por que fallaba
|
||||||
|
- Codigos de salida incompatibles con el comportamiento esperado del shell.
|
||||||
|
- `HOME` no definido no se controlaba antes del `chdir`.
|
||||||
|
- La logica de `access` estaba al reves y mezclaba condiciones.
|
||||||
|
- Variables de entorno del directorio quedaban desincronizadas.
|
||||||
|
- Faltaba resolver el caso especial de `-` hacia `OLDPWD`.
|
||||||
|
|
||||||
|
### Solucion
|
||||||
|
- Refactor en `resolve_cd_path()` para validar argumentos y `HOME`.
|
||||||
|
- Retorno `EXIT_FAILURE` en `too many arguments` y `HOME not set`.
|
||||||
|
- Error de `chdir` simplificado a `perror("minishell: cd")` + retorno `1`.
|
||||||
|
- Actualizacion de `OLDPWD` y `PWD` mediante `getcwd(NULL, 0)` + `set_env()`.
|
||||||
|
- Soporte de `cd -`: usa `OLDPWD`, valida `OLDPWD not set` e imprime el nuevo
|
||||||
|
directorio tras el cambio.
|
||||||
|
|
||||||
|
## 3) `exit` (`src/builtins/exit/exit.c`)
|
||||||
|
|
||||||
|
### Fallo
|
||||||
|
- Habia un `printf` de debug en ejecucion real.
|
||||||
|
- `exit <no_numerico>` devolvia `2` pero no cerraba el shell.
|
||||||
|
- `exit n m` devolvia `2`; en bash es `1` y no sale del shell.
|
||||||
|
- Validacion numerica basada en `ft_strisnum` sin control de overflow.
|
||||||
|
- Se mostraba `exit` incluso en contexto no interactivo.
|
||||||
|
|
||||||
|
### Por que fallaba
|
||||||
|
- Debug residual contamina salida.
|
||||||
|
- Semantica de `exit` incompleta respecto a bash.
|
||||||
|
- Valores fuera de rango podian tratarse como validos por conversion directa.
|
||||||
|
- Mensaje `exit` debe mostrarse solo en shell interactivo.
|
||||||
|
|
||||||
|
### Solucion
|
||||||
|
- Eliminado debug print.
|
||||||
|
- Nuevo flujo `resolve_exit_status()`:
|
||||||
|
- Sin argumentos: usa `msh->exit_status`.
|
||||||
|
- Argumento no numerico o fuera de `long`: mensaje
|
||||||
|
`numeric argument required`, `msh->exit = true`, estado `2`.
|
||||||
|
- Demasiados argumentos: mensaje de error y estado `1`, sin salir.
|
||||||
|
- Parser numerico propio (`get_uint8_from_num` + `has_overflow`) con soporte
|
||||||
|
de signo y control de overflow.
|
||||||
|
- `ft_eputendl("exit")` solo si `isatty(STDIN_FILENO)`.
|
||||||
|
|
||||||
|
## 4) `pwd` (`src/builtins/pwd/pwd.c`)
|
||||||
|
|
||||||
|
### Fallo
|
||||||
|
- Si `getcwd` fallaba, el builtin devolvia `EXIT_SUCCESS`.
|
||||||
|
- Uso de buffer fijo (`PATH_MAX`) menos robusto para rutas largas.
|
||||||
|
|
||||||
|
### Por que fallaba
|
||||||
|
- El shell reportaba exito aunque no pudiera obtener el directorio.
|
||||||
|
- Un buffer fijo puede truncar o fallar en escenarios de rutas profundas.
|
||||||
|
|
||||||
|
### Solucion
|
||||||
|
- Cambio a `getcwd(NULL, 0)` con memoria dinamica.
|
||||||
|
- Si falla, `perror("minishell: pwd")` y retorno `EXIT_FAILURE`.
|
||||||
|
- `free()` del buffer dinamico tras imprimir.
|
||||||
|
|
||||||
|
## 5) `echo` (`src/builtins/echo/echo.c`, `src/builtins/echo/echo_def.c`)
|
||||||
|
|
||||||
|
### Cambio aplicado
|
||||||
|
- Ajuste de tipos de retorno auxiliares a `uint8_t` para mantener consistencia
|
||||||
|
con `builtins.h`.
|
||||||
|
|
||||||
|
### Nota
|
||||||
|
- No se detectaron fallos funcionales criticos adicionales en la logica actual
|
||||||
|
de `echo` durante esta revision.
|
||||||
|
|
||||||
|
## 6) Builtins faltantes: `env`, `export`, `unset`
|
||||||
|
|
||||||
|
### Fallo
|
||||||
|
- Los builtins `env`, `export` y `unset` no estaban implementados ni
|
||||||
|
registrados en `set_builtins`.
|
||||||
|
|
||||||
|
### Por que fallaba
|
||||||
|
- Comandos basicos de shell no existian en la tabla de builtins.
|
||||||
|
- Cualquier prueba/flujo que dependiera de gestion de variables exportadas
|
||||||
|
fallaba (listar, crear y eliminar variables de entorno).
|
||||||
|
|
||||||
|
### Solucion
|
||||||
|
- Nuevos builtins implementados:
|
||||||
|
- `src/builtins/env/env.c`
|
||||||
|
- `src/builtins/export/export.c`
|
||||||
|
- `src/builtins/unset/unset.c`
|
||||||
|
- Registro en `src/builtins/builtins.c` (tabla ampliada de 4 a 7 entradas).
|
||||||
|
- Nuevos prototipos en `include/builtins.h`.
|
||||||
|
- Soporte de borrado real de entorno mediante `unset_env`:
|
||||||
|
- Declaracion en `include/core.h`
|
||||||
|
- Implementacion en `src/variables/environment_unset.c`
|
||||||
|
|
||||||
|
## 7) Comportamiento aplicado en los nuevos builtins
|
||||||
|
|
||||||
|
### `env`
|
||||||
|
- Si recibe argumentos, devuelve error (`minishell: env: too many arguments`)
|
||||||
|
y estado `1`.
|
||||||
|
- Sin argumentos, lista `KEY=VALUE` de las variables de entorno actuales.
|
||||||
|
|
||||||
|
### `export`
|
||||||
|
- `export NAME=VALUE`: crea/actualiza la variable.
|
||||||
|
- `export NAME`: crea/actualiza con valor vacio (`NAME=`) para que aparezca
|
||||||
|
en `env`.
|
||||||
|
- Identificadores invalidos (`1A=2`, etc.) devuelven error
|
||||||
|
`not a valid identifier` y estado `1`.
|
||||||
|
- Sin argumentos, reutiliza el listado de `env`.
|
||||||
|
|
||||||
|
### `unset`
|
||||||
|
- Elimina variables validas del entorno en memoria.
|
||||||
|
- Identificadores invalidos devuelven error
|
||||||
|
`not a valid identifier` y estado `1`.
|
||||||
|
- Si el identificador es valido y no existe, no falla (comportamiento shell).
|
||||||
|
|
||||||
|
## Validacion realizada
|
||||||
|
|
||||||
|
- `norminette` ejecutado sobre los archivos modificados: `OK`.
|
||||||
|
- Build completa no ejecutable en este entorno por falta de acceso de red al
|
||||||
|
clonado de librerias (`github.com`), por lo que no se pudo validar runtime
|
||||||
|
del binario en esta sesion.
|
||||||
77
docs/defensa_resumen_es.md
Normal file
77
docs/defensa_resumen_es.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# 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".
|
||||||
|
|
||||||
292
docs/guia_obligatoria_es.md
Normal file
292
docs/guia_obligatoria_es.md
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
# 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-\.
|
||||||
|
|
||||||
341
executor_new_previous.c
Normal file
341
executor_new_previous.c
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* executor_new.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/10/28 13:03:44 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2026/02/08 19:05:37 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "executor.h"
|
||||||
|
#include "builtins.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
static void handle_execve_error();
|
||||||
|
static void handle_parent_process(
|
||||||
|
int *prev_read_fd,
|
||||||
|
t_list *current_command,
|
||||||
|
int pipefd[2]
|
||||||
|
);
|
||||||
|
static int handle_pipeline(
|
||||||
|
t_list *current_command,
|
||||||
|
int pipefd[2],
|
||||||
|
int *exit_status
|
||||||
|
);
|
||||||
|
static inline uint8_t execute_builtin(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
);
|
||||||
|
static void execute_external_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
);
|
||||||
|
static pid_t handle_fork(
|
||||||
|
t_list *current_command,
|
||||||
|
t_minishell *minishell
|
||||||
|
);
|
||||||
|
static uint8_t execute_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
);
|
||||||
|
|
||||||
|
static inline bool can_execute_command(t_command *command, t_minishell *minishell);
|
||||||
|
static inline bool command_exists(t_command *command, t_minishell *minishell);
|
||||||
|
static void cmdfree(t_command *command);
|
||||||
|
static void cmdfree_argv(char **argv);
|
||||||
|
|
||||||
|
# define WRITE_PIPE 1
|
||||||
|
# define READ_PIPE 0
|
||||||
|
# define CHILD_PID 0
|
||||||
|
|
||||||
|
u_int8_t execute(
|
||||||
|
t_list *command_list,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
t_list *current_command;
|
||||||
|
int prev_read_fd;
|
||||||
|
int exit_status;
|
||||||
|
int pipefd[2];
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
|
||||||
|
prev_read_fd = -1;
|
||||||
|
exit_status = EXIT_SUCCESS;
|
||||||
|
current_command = command_list;
|
||||||
|
while (current_command != NULL)
|
||||||
|
{
|
||||||
|
if (handle_pipeline(current_command, pipefd, &exit_status) == EXIT_FAILURE)
|
||||||
|
break ;
|
||||||
|
pid = handle_fork(current_command, minishell);
|
||||||
|
if (pid == -1)
|
||||||
|
{
|
||||||
|
perror("fork");
|
||||||
|
exit_status = EXIT_FAILURE;
|
||||||
|
break ;
|
||||||
|
}
|
||||||
|
if (pid == CHILD_PID)
|
||||||
|
handle_child_process(prev_read_fd, current_command, pipefd, minishell);
|
||||||
|
handle_parent_process(&prev_read_fd, current_command, pipefd);
|
||||||
|
current_command = current_command->next;
|
||||||
|
}
|
||||||
|
ft_lstclear(&command_list, (void (*)(void *))cmdfree);
|
||||||
|
while (wait(&exit_status) > 0)
|
||||||
|
{
|
||||||
|
if (WIFEXITED(exit_status))
|
||||||
|
exit_status = WEXITSTATUS(exit_status);
|
||||||
|
}
|
||||||
|
return (exit_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_pipeline(
|
||||||
|
t_list *current_command,
|
||||||
|
int pipefd[2],
|
||||||
|
int *exit_status
|
||||||
|
) {
|
||||||
|
if (current_command->next) // create pipe if needed
|
||||||
|
{
|
||||||
|
if (pipe(pipefd) == -1)
|
||||||
|
{
|
||||||
|
perror("pipe");
|
||||||
|
*exit_status = EXIT_FAILURE;
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_parent_process(
|
||||||
|
int *prev_read_fd,
|
||||||
|
t_list *current_command,
|
||||||
|
int pipefd[2]
|
||||||
|
) {
|
||||||
|
if (*prev_read_fd != -1)
|
||||||
|
close(*prev_read_fd);
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
close(pipefd[WRITE_PIPE]); // parent does not write
|
||||||
|
*prev_read_fd = pipefd[READ_PIPE]; // pass read end forward
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*prev_read_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pid_t handle_fork(
|
||||||
|
t_list *current_command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
pid_t pid;
|
||||||
|
const t_command *command = (t_command *)current_command->content;
|
||||||
|
|
||||||
|
pid = 0;
|
||||||
|
if (current_command->next != NULL
|
||||||
|
|| !is_builtin(command->path, minishell))
|
||||||
|
pid = fork();
|
||||||
|
return (pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_child_process(int prev_read_fd, t_list *current_command, int pipefd[2], t_minishell *minishell)
|
||||||
|
{
|
||||||
|
redirect_pipes(prev_read_fd, current_command, pipefd);
|
||||||
|
execute_command((t_command *)current_command->content, minishell);
|
||||||
|
}
|
||||||
|
|
||||||
|
void redirect_pipes(int prev_read_fd, t_list *current_command, int pipefd[2])
|
||||||
|
{
|
||||||
|
redirect_input_pipe(prev_read_fd);
|
||||||
|
redirect_output_pipe(current_command, pipefd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void redirect_output_pipe(t_list * current_command, int pipefd[2])
|
||||||
|
{
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
dup2(pipefd[WRITE_PIPE], STDOUT_FILENO);
|
||||||
|
close(pipefd[READ_PIPE]);
|
||||||
|
close(pipefd[WRITE_PIPE]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void redirect_input_pipe(int prev_read_fd)
|
||||||
|
{
|
||||||
|
if (prev_read_fd != -1)
|
||||||
|
{
|
||||||
|
dup2(prev_read_fd, STDIN_FILENO);
|
||||||
|
close(prev_read_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool can_execute_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
if (!is_builtin(command->path, minishell)
|
||||||
|
&& access(command->path, X_OK) != EXIT_SUCCESS)
|
||||||
|
return (false);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool command_exists(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
if (!is_builtin(command->path, minishell)
|
||||||
|
&& access(command->path, F_OK) != EXIT_SUCCESS)
|
||||||
|
return (false);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t execute_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
if (is_builtin(command->path, minishell))
|
||||||
|
return (execute_builtin(command, minishell));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
execute_external_command(command, minishell);
|
||||||
|
return (EXIT_FAILURE); //! should never reach here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t execute_builtin(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
const t_builtin_func builtin
|
||||||
|
= ft_hashmap_get(minishell->builtins, command->path);
|
||||||
|
|
||||||
|
return (builtin(*command, minishell));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void execute_external_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
char **envp;
|
||||||
|
|
||||||
|
envp = get_envp(minishell);
|
||||||
|
if (envp == NULL)
|
||||||
|
{
|
||||||
|
perror("get_envp");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
execve(command->path, command->argv, envp);
|
||||||
|
handle_execve_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_execve_error() {
|
||||||
|
perror("execve");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static inline bool is_piped(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// return (command->piped_to != NULL || command->piped_from != NULL);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static u_int8_t handle_child_process(
|
||||||
|
// t_command *command,
|
||||||
|
// t_minishell *minishell
|
||||||
|
// ) {
|
||||||
|
// if (!redirect_pipes(command))
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// execute_command(command, minishell);
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static int redirect_pipe(
|
||||||
|
// int from,
|
||||||
|
// int to
|
||||||
|
// ) {
|
||||||
|
// if (dup2(from, to) == -1)
|
||||||
|
// return (-1);
|
||||||
|
// close(from);
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static int redirect_pipes(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// if (command->piped_from &&
|
||||||
|
// redirect_pipe(command->piped_from->outfile, STDIN_FILENO) == -1)
|
||||||
|
// {
|
||||||
|
// perror("dup2");
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
// if (command->piped_to &&
|
||||||
|
// redirect_pipe(command->outfile, STDOUT_FILENO) == -1)
|
||||||
|
// {
|
||||||
|
// perror("dup2");
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
// return (1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static void show_error(
|
||||||
|
// t_command *command,
|
||||||
|
// t_minishell *minishell
|
||||||
|
// ) {
|
||||||
|
// if (!command_exists(command, minishell))
|
||||||
|
// {
|
||||||
|
// ft_eprintf("minishell: %s: command not found\n", command->argv[0]);
|
||||||
|
// minishell->exit_status = 127;
|
||||||
|
// }
|
||||||
|
// else if (!can_execute_command(command, minishell))
|
||||||
|
// {
|
||||||
|
// ft_eprintf("minishell: %s: %s\n", command->path, strerror(errno));
|
||||||
|
// minishell->exit_status = errno;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
static void cmdfree(
|
||||||
|
t_command *command
|
||||||
|
) {
|
||||||
|
if (command == NULL)
|
||||||
|
return ;
|
||||||
|
cmdfree_argv(command->argv);
|
||||||
|
free(command->path);
|
||||||
|
free(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmdfree_argv(
|
||||||
|
char **argv
|
||||||
|
) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (argv == NULL)
|
||||||
|
return ;
|
||||||
|
i = 0;
|
||||||
|
while (argv[i] != NULL)
|
||||||
|
{
|
||||||
|
free(argv[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
free(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static void debug_print_command_info(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// size_t i;
|
||||||
|
|
||||||
|
// if (command == NULL)
|
||||||
|
// {
|
||||||
|
// printf("Command is NULL\n");
|
||||||
|
// return ;
|
||||||
|
// }
|
||||||
|
// printf("Command info:\n");
|
||||||
|
// printf(" Path: %s\n", command->path);
|
||||||
|
// printf(" Argc: %d\n", command->argc);
|
||||||
|
// printf(" Argv:\n");
|
||||||
|
// for (i = 0; i < (size_t)command->argc; i++)
|
||||||
|
// printf(" arg[%zu]: %s\n", i, command->argv[i]);
|
||||||
|
// printf(" Infile FD: %d\n", command->infile);
|
||||||
|
// printf(" Outfile FD: %d\n", command->outfile);
|
||||||
|
// printf("--------------------------\n");
|
||||||
|
// }
|
||||||
256
executor_old.c
Normal file
256
executor_old.c
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* executor.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/10/28 13:03:44 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2026/02/08 03:42:11 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "executor.h"
|
||||||
|
#include "builtins.h"
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
static inline bool can_execute_command(t_command *command, t_minishell *minishell);
|
||||||
|
static inline bool command_exists(t_command *command, t_minishell *minishell);
|
||||||
|
static u_int8_t execute_command(t_command *command, t_minishell *minishell);
|
||||||
|
static u_int8_t execute_command(t_command *command, t_minishell *minishell);
|
||||||
|
// static inline bool is_piped(t_command *command);
|
||||||
|
static void cmdfree(t_command *command);
|
||||||
|
static void cmdfree_argv(char **argv);
|
||||||
|
|
||||||
|
# define WRITE_PIPE 1
|
||||||
|
# define READ_PIPE 0
|
||||||
|
# define CHILD_PID 0
|
||||||
|
|
||||||
|
u_int8_t execute(
|
||||||
|
t_list *command_list,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
t_list *current_command;
|
||||||
|
int prev_read_fd;
|
||||||
|
pid_t pid;
|
||||||
|
u_int8_t exit_status;
|
||||||
|
int child_exit_status;
|
||||||
|
|
||||||
|
prev_read_fd = -1;
|
||||||
|
exit_status = EXIT_SUCCESS;
|
||||||
|
current_command = command_list;
|
||||||
|
while (current_command != NULL)
|
||||||
|
{
|
||||||
|
t_command *command = (t_command *)current_command->content;
|
||||||
|
int pipefd[2];
|
||||||
|
|
||||||
|
/* Create pipe ONLY if there is a next command */
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
if (pipe(pipefd) == -1)
|
||||||
|
{
|
||||||
|
perror("pipe");
|
||||||
|
exit_status = EXIT_FAILURE;
|
||||||
|
break ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create fork
|
||||||
|
pid = 0;
|
||||||
|
if (current_command->next != NULL
|
||||||
|
|| !is_builtin(command->path, minishell))
|
||||||
|
pid = fork();
|
||||||
|
// handle fork error
|
||||||
|
if (pid == -1)
|
||||||
|
{
|
||||||
|
perror("fork");
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle child process
|
||||||
|
if (pid == CHILD_PID)
|
||||||
|
{
|
||||||
|
/* If we have input from previous pipe */
|
||||||
|
if (prev_read_fd != -1)
|
||||||
|
{
|
||||||
|
dup2(prev_read_fd, STDIN_FILENO);
|
||||||
|
close(prev_read_fd);
|
||||||
|
}
|
||||||
|
/* If we pipe output to next command */
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
dup2(pipefd[WRITE_PIPE], STDOUT_FILENO);
|
||||||
|
close(pipefd[READ_PIPE]);
|
||||||
|
close(pipefd[WRITE_PIPE]);
|
||||||
|
}
|
||||||
|
execute_command(command, minishell); // child process exits here
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle parent process
|
||||||
|
waitpid(pid, &child_exit_status, 0); // wait for child to finish
|
||||||
|
if (prev_read_fd != -1)
|
||||||
|
close(prev_read_fd);
|
||||||
|
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
close(pipefd[WRITE_PIPE]); /* parent does not write */
|
||||||
|
prev_read_fd = pipefd[READ_PIPE]; /* pass read end forward */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
prev_read_fd = -1;
|
||||||
|
|
||||||
|
// continue executing
|
||||||
|
current_command = current_command->next;
|
||||||
|
}
|
||||||
|
ft_lstclear(&command_list, (void (*)(void *))cmdfree);
|
||||||
|
exit_status = child_exit_status;
|
||||||
|
return (exit_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool can_execute_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
if (!is_builtin(command->path, minishell)
|
||||||
|
&& access(command->path, X_OK) != EXIT_SUCCESS)
|
||||||
|
return (false);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool command_exists(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
if (!is_builtin(command->path, minishell)
|
||||||
|
&& access(command->path, F_OK) != EXIT_SUCCESS)
|
||||||
|
return (false);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u_int8_t execute_command(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
char **envp;
|
||||||
|
t_builtin_func builtin;
|
||||||
|
|
||||||
|
if (is_builtin(command->path, minishell))
|
||||||
|
{
|
||||||
|
builtin = ft_hashmap_get(minishell->builtins, command->path);
|
||||||
|
return (builtin(*command, minishell));
|
||||||
|
}
|
||||||
|
envp = get_envp(minishell);
|
||||||
|
execve(command->path, command->argv, envp);
|
||||||
|
// handle error if execve fails
|
||||||
|
perror("execve");
|
||||||
|
free_envp(envp);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static inline bool is_piped(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// return (command->piped_to != NULL || command->piped_from != NULL);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static u_int8_t handle_child_process(
|
||||||
|
// t_command *command,
|
||||||
|
// t_minishell *minishell
|
||||||
|
// ) {
|
||||||
|
// if (!redirect_pipes(command))
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// execute_command(command, minishell);
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static int redirect_pipe(
|
||||||
|
// int from,
|
||||||
|
// int to
|
||||||
|
// ) {
|
||||||
|
// if (dup2(from, to) == -1)
|
||||||
|
// return (-1);
|
||||||
|
// close(from);
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static int redirect_pipes(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// if (command->piped_from &&
|
||||||
|
// redirect_pipe(command->piped_from->outfile, STDIN_FILENO) == -1)
|
||||||
|
// {
|
||||||
|
// perror("dup2");
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
// if (command->piped_to &&
|
||||||
|
// redirect_pipe(command->outfile, STDOUT_FILENO) == -1)
|
||||||
|
// {
|
||||||
|
// perror("dup2");
|
||||||
|
// return (0);
|
||||||
|
// }
|
||||||
|
// return (1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static void show_error(
|
||||||
|
// t_command *command,
|
||||||
|
// t_minishell *minishell
|
||||||
|
// ) {
|
||||||
|
// if (!command_exists(command, minishell))
|
||||||
|
// {
|
||||||
|
// ft_eprintf("minishell: %s: command not found\n", command->argv[0]);
|
||||||
|
// minishell->exit_status = 127;
|
||||||
|
// }
|
||||||
|
// else if (!can_execute_command(command, minishell))
|
||||||
|
// {
|
||||||
|
// ft_eprintf("minishell: %s: %s\n", command->path, strerror(errno));
|
||||||
|
// minishell->exit_status = errno;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
static void cmdfree(
|
||||||
|
t_command *command
|
||||||
|
) {
|
||||||
|
if (command == NULL)
|
||||||
|
return ;
|
||||||
|
cmdfree_argv(command->argv);
|
||||||
|
free(command->path);
|
||||||
|
free(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmdfree_argv(
|
||||||
|
char **argv
|
||||||
|
) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (argv == NULL)
|
||||||
|
return ;
|
||||||
|
i = 0;
|
||||||
|
while (argv[i] != NULL)
|
||||||
|
{
|
||||||
|
free(argv[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
free(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static void debug_print_command_info(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// size_t i;
|
||||||
|
|
||||||
|
// if (command == NULL)
|
||||||
|
// {
|
||||||
|
// printf("Command is NULL\n");
|
||||||
|
// return ;
|
||||||
|
// }
|
||||||
|
// printf("Command info:\n");
|
||||||
|
// printf(" Path: %s\n", command->path);
|
||||||
|
// printf(" Argc: %d\n", command->argc);
|
||||||
|
// printf(" Argv:\n");
|
||||||
|
// for (i = 0; i < (size_t)command->argc; i++)
|
||||||
|
// printf(" arg[%zu]: %s\n", i, command->argv[i]);
|
||||||
|
// printf(" Infile FD: %d\n", command->infile);
|
||||||
|
// printf(" Outfile FD: %d\n", command->outfile);
|
||||||
|
// printf("--------------------------\n");
|
||||||
|
// }
|
||||||
@@ -6,19 +6,18 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/29 22:09:51 by sede-san #+# #+# */
|
/* Created: 2025/10/29 22:09:51 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/12/01 17:53:57 by sede-san ### ########.fr */
|
/* Updated: 2026/02/08 19:42:50 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#ifndef BUILTINS_H
|
#ifndef BUILTINS_H
|
||||||
# define BUILTINS_H
|
# define BUILTINS_H
|
||||||
|
|
||||||
# include "libft.h"
|
|
||||||
# include "minishell.h"
|
|
||||||
# include "ft_args.h"
|
# include "ft_args.h"
|
||||||
|
# include "minishell.h"
|
||||||
|
# include "core.h"
|
||||||
|
|
||||||
typedef unsigned char (*t_builtin_func)(t_command cmd, t_minishell *msh);
|
typedef uint8_t (*t_builtin_func)(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* Functions */
|
/* Functions */
|
||||||
@@ -26,24 +25,36 @@ typedef unsigned char (*t_builtin_func)(t_command cmd, t_minishell *msh);
|
|||||||
|
|
||||||
/* builtins.c */
|
/* builtins.c */
|
||||||
|
|
||||||
extern u_int8_t set_builtins(t_minishell *msh);
|
extern uint8_t set_builtins(t_minishell *minishell);
|
||||||
|
|
||||||
extern u_int8_t is_builtin(const char *command_name, t_minishell *msh);
|
extern uint8_t is_builtin(const char *command_name, t_minishell *minishell);
|
||||||
|
|
||||||
/* cd.c */
|
/* cd.c */
|
||||||
|
|
||||||
extern u_int8_t builtin_cd(t_command cmd, t_minishell *msh);
|
extern uint8_t builtin_cd(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
/* echo.c */
|
/* echo.c */
|
||||||
|
|
||||||
extern u_int8_t builtin_echo(t_command cmd, t_minishell *msh);
|
extern uint8_t builtin_echo(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
/* exit.c */
|
/* exit.c */
|
||||||
|
|
||||||
extern u_int8_t builtin_exit(t_command cmd, t_minishell *msh);
|
extern uint8_t builtin_exit(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
/* pwd.c */
|
/* pwd.c */
|
||||||
|
|
||||||
extern u_int8_t builtin_pwd(t_command cmd, t_minishell *msh);
|
extern uint8_t builtin_pwd(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
|
/* env.c */
|
||||||
|
|
||||||
|
extern uint8_t builtin_env(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
|
/* export.c */
|
||||||
|
|
||||||
|
extern uint8_t builtin_export(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
|
/* unset.c */
|
||||||
|
|
||||||
|
extern uint8_t builtin_unset(t_command cmd, t_minishell *minishell);
|
||||||
|
|
||||||
#endif /* BUILTINS_H */
|
#endif /* BUILTINS_H */
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/22 19:10:13 by sede-san #+# #+# */
|
/* Created: 2025/10/22 19:10:13 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/12/01 17:02:10 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 18:45:41 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -50,8 +50,8 @@ typedef struct s_minishell
|
|||||||
{
|
{
|
||||||
t_variables variables;
|
t_variables variables;
|
||||||
t_hashmap *builtins;
|
t_hashmap *builtins;
|
||||||
u_int8_t exit_status;
|
uint8_t exit_status;
|
||||||
u_int8_t exit;
|
bool exit;
|
||||||
} t_minishell;
|
} t_minishell;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,17 +69,28 @@ typedef struct s_command
|
|||||||
int argc;
|
int argc;
|
||||||
char **argv;
|
char **argv;
|
||||||
char *path;
|
char *path;
|
||||||
|
t_list *redirections;
|
||||||
} t_command;
|
} t_command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default shell prompt string
|
||||||
|
*/
|
||||||
|
# define DEFAULT_PS1 "minishell > "
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default secondary prompt string for multiline input
|
||||||
|
*/
|
||||||
|
# define DEFAULT_PS2 "> "
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* Functions */
|
/* Functions */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
/* minishell.c */
|
/* minishell.c */
|
||||||
|
|
||||||
extern int minishell_init(t_minishell *minishell, char **envp);
|
extern void minishell_init(t_minishell *minishell, char **envp);
|
||||||
|
|
||||||
extern u_int8_t minishell_run(t_minishell *minishell);
|
extern void minishell_run(t_minishell *minishell);
|
||||||
|
|
||||||
extern void minishell_clear(t_minishell *minishell);
|
extern void minishell_clear(t_minishell *minishell);
|
||||||
|
|
||||||
@@ -96,4 +107,6 @@ extern void free_envp(char **envp);
|
|||||||
|
|
||||||
extern char *get_env(const char *env_name, t_minishell *msh);
|
extern char *get_env(const char *env_name, t_minishell *msh);
|
||||||
|
|
||||||
|
extern void unset_env(const char *env_name, t_minishell *msh);
|
||||||
|
|
||||||
#endif /* CORE_H */
|
#endif /* CORE_H */
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/28 13:08:33 by sede-san #+# #+# */
|
/* Created: 2025/10/28 13:08:33 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/10/28 14:20:23 by sede-san ### ########.fr */
|
/* Updated: 2026/02/08 21:32:56 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -14,6 +14,18 @@
|
|||||||
# define EXECUTOR_H
|
# define EXECUTOR_H
|
||||||
|
|
||||||
# include "minishell.h"
|
# include "minishell.h"
|
||||||
|
# include "core.h"
|
||||||
|
# include <stdint.h>
|
||||||
|
|
||||||
|
# define READ_PIPE 0
|
||||||
|
# define WRITE_PIPE 1
|
||||||
|
|
||||||
|
typedef struct s_pipeline
|
||||||
|
{
|
||||||
|
int prev_read_fd;
|
||||||
|
int pipefd[2];
|
||||||
|
} t_pipeline;
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* Functions */
|
/* Functions */
|
||||||
@@ -21,6 +33,9 @@
|
|||||||
|
|
||||||
// executor.c
|
// executor.c
|
||||||
|
|
||||||
extern u_int8_t execute(t_command command, t_minishell *minishell);
|
# define PIPE_ERROR -1
|
||||||
|
# define FORK_ERROR -1
|
||||||
|
|
||||||
|
extern uint8_t execute(t_list *command, t_minishell *minishell);
|
||||||
|
|
||||||
#endif /* EXECUTOR_H */
|
#endif /* EXECUTOR_H */
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/20 16:35:10 by sede-san #+# #+# */
|
/* Created: 2025/10/20 16:35:10 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/10/30 12:20:08 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 18:38:16 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -16,10 +16,8 @@
|
|||||||
# include "libft.h"
|
# include "libft.h"
|
||||||
# include "ft_printf.h"
|
# include "ft_printf.h"
|
||||||
# include "chardefs.h"
|
# include "chardefs.h"
|
||||||
# include "core.h"
|
# include <stdbool.h>
|
||||||
# include "parser.h"
|
# include <stdint.h>
|
||||||
# include "executor.h"
|
|
||||||
# include "builtins.h"
|
|
||||||
# include <readline/readline.h> // readline(3), rl_clear_history(),
|
# include <readline/readline.h> // readline(3), rl_clear_history(),
|
||||||
// rl_on_new_line(), rl_replace_line(),
|
// rl_on_new_line(), rl_replace_line(),
|
||||||
// rl_redisplay()
|
// rl_redisplay()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/22 19:03:51 by sede-san #+# #+# */
|
/* Created: 2025/10/22 19:03:51 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/10/30 12:44:51 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 20:36:19 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -14,6 +14,46 @@
|
|||||||
# define PARSER_H
|
# define PARSER_H
|
||||||
|
|
||||||
# include "minishell.h"
|
# include "minishell.h"
|
||||||
|
# include "core.h"
|
||||||
|
# include "builtins.h"
|
||||||
|
|
||||||
|
# define PIPE_STR "|"
|
||||||
|
# define REDIRECT_IN_STR "<"
|
||||||
|
# define REDIRECT_OUT_STR ">"
|
||||||
|
# define APPEND_STR ">>"
|
||||||
|
# define HEREDOC_STR "<<"
|
||||||
|
|
||||||
|
# define TOKENS_COUNT 5
|
||||||
|
|
||||||
|
typedef enum e_token_type
|
||||||
|
{
|
||||||
|
TOKEN_WORD,
|
||||||
|
TOKEN_PIPE,
|
||||||
|
TOKEN_REDIRECT_IN,
|
||||||
|
TOKEN_REDIRECT_OUT,
|
||||||
|
TOKEN_APPEND,
|
||||||
|
TOKEN_HEREDOC
|
||||||
|
} t_token_type;
|
||||||
|
|
||||||
|
typedef struct s_token
|
||||||
|
{
|
||||||
|
t_token_type type;
|
||||||
|
char *value;
|
||||||
|
} t_token;
|
||||||
|
|
||||||
|
typedef enum e_redirection_type
|
||||||
|
{
|
||||||
|
REDIRECT_IN,
|
||||||
|
REDIRECT_OUT,
|
||||||
|
APPEND,
|
||||||
|
HEREDOC
|
||||||
|
} t_redirection_type;
|
||||||
|
|
||||||
|
typedef struct s_redirection
|
||||||
|
{
|
||||||
|
t_redirection_type type;
|
||||||
|
char *target;
|
||||||
|
} t_redirection;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* Functions */
|
/* Functions */
|
||||||
@@ -21,6 +61,6 @@
|
|||||||
|
|
||||||
// parser.c
|
// parser.c
|
||||||
|
|
||||||
extern t_command parse(char *line, t_minishell *minishell);
|
extern t_list *parse(char *line, t_minishell *minishell);
|
||||||
|
|
||||||
#endif /* PARSER_H */
|
#endif /* PARSER_H */
|
||||||
|
|||||||
27
minishell-codex/Makefile
Normal file
27
minishell-codex/Makefile
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
NAME := minishell
|
||||||
|
CC := cc
|
||||||
|
CFLAGS := -Wall -Wextra -Werror -g
|
||||||
|
INCLUDES := -Iinclude
|
||||||
|
READLINE_LIBS := -lreadline -lncurses
|
||||||
|
|
||||||
|
SRCS := $(shell find src -name '*.c')
|
||||||
|
OBJS := $(SRCS:src/%.c=build/%.o)
|
||||||
|
|
||||||
|
all: $(NAME)
|
||||||
|
|
||||||
|
$(NAME): $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(OBJS) $(READLINE_LIBS) -o $(NAME)
|
||||||
|
|
||||||
|
build/%.o: src/%.c
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build
|
||||||
|
|
||||||
|
fclean: clean
|
||||||
|
rm -f $(NAME)
|
||||||
|
|
||||||
|
re: fclean all
|
||||||
|
|
||||||
|
.PHONY: all clean fclean re
|
||||||
72
minishell-codex/docs/defensa.md
Normal file
72
minishell-codex/docs/defensa.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# Minishell - Guion de defensa (version codex)
|
||||||
|
|
||||||
|
Este guion esta alineado con la estructura real en `minishell-codex/`.
|
||||||
|
|
||||||
|
## 1. Explicacion corta del proyecto
|
||||||
|
- Minishell es un interprete de comandos interactivo.
|
||||||
|
- Implementa pipes, redirecciones, variables y builtins basicos.
|
||||||
|
- Se basa en el flujo: lectura -> lexer -> parser -> expansion -> ejecucion.
|
||||||
|
|
||||||
|
## 2. Flujo completo (paso a paso)
|
||||||
|
1. `readline()` muestra el prompt y devuelve la linea.
|
||||||
|
2. `lex_line()` divide la linea en tokens (`TOK_WORD`, `TOK_PIPE`, redirecciones).
|
||||||
|
3. `parse_tokens()` construye la pipeline con comandos y redirecciones.
|
||||||
|
4. `expand_pipeline()` aplica expansion de `$VAR` y `$?` respetando comillas.
|
||||||
|
5. `execute_pipeline()` resuelve `PATH`, prepara heredocs y ejecuta.
|
||||||
|
|
||||||
|
## 3. Estructuras clave
|
||||||
|
- `t_token`: tipo y texto de tokens (`minishell-codex/include/minishell.h`).
|
||||||
|
- `t_command`: argv, redirecciones, path.
|
||||||
|
- `t_pipeline`: lista de comandos.
|
||||||
|
- `t_redir`: tipo, target y fd.
|
||||||
|
- `t_shell`: estado global (env, exit_status, flags).
|
||||||
|
|
||||||
|
## 4. Lexer (por que esta separado)
|
||||||
|
- Maneja comillas y metacaracteres sin mezclar con ejecucion.
|
||||||
|
- Detecta errores de comillas sin cerrar.
|
||||||
|
- Facilita el parseo posterior.
|
||||||
|
|
||||||
|
## 5. Parser
|
||||||
|
- Convierte tokens en comandos reales.
|
||||||
|
- Cada `TOK_PIPE` crea un nuevo comando.
|
||||||
|
- Redirecciones se guardan en lista separada (`t_redir`).
|
||||||
|
- Valida errores (pipe sin comando, redireccion sin destino).
|
||||||
|
|
||||||
|
## 6. Expansion
|
||||||
|
- `expand_pipeline()` recorre argv y targets de redireccion.
|
||||||
|
- Reglas:
|
||||||
|
- En comilla simple no se expande.
|
||||||
|
- En comilla doble si se expande.
|
||||||
|
- `$?` es el exit status anterior.
|
||||||
|
|
||||||
|
## 7. Redirecciones y heredoc
|
||||||
|
- `apply_redirections()` abre y hace `dup2()`.
|
||||||
|
- `prepare_heredocs()` genera un pipe con el contenido.
|
||||||
|
- Heredoc no se mete en el historial.
|
||||||
|
|
||||||
|
## 8. Ejecucion y pipes
|
||||||
|
- Si hay un solo builtin, se ejecuta en el padre.
|
||||||
|
- Si hay pipeline, todos se forkean.
|
||||||
|
- Se conectan con `pipe()` y `dup2()`.
|
||||||
|
- Se espera a todos, y el exit status es el del ultimo comando.
|
||||||
|
|
||||||
|
## 9. Builtins
|
||||||
|
- Implementados en `src/builtins/builtins.c`.
|
||||||
|
- `echo`, `cd`, `pwd`, `env`, `export`, `unset`, `exit`.
|
||||||
|
- `export` valida identificadores y permite `KEY=VALUE`.
|
||||||
|
|
||||||
|
## 10. Señales
|
||||||
|
- Una sola global: `g_signal`.
|
||||||
|
- `ctrl-C`: limpia linea y muestra prompt.
|
||||||
|
- `ctrl-\`: se ignora en interactivo.
|
||||||
|
- En child se restauran señales por defecto.
|
||||||
|
|
||||||
|
## 11. Ejemplos rapidos para demostrar
|
||||||
|
- Pipes: `ls | wc -l`
|
||||||
|
- Redireccion: `echo hola > out.txt`
|
||||||
|
- Heredoc: `cat << EOF` -> texto -> `EOF`
|
||||||
|
- Expansion: `echo $HOME`, `echo $?`
|
||||||
|
|
||||||
|
## 12. Mensaje final recomendado
|
||||||
|
"Separar lexer, parser, expansion y ejecucion me permitio mantener el codigo claro
|
||||||
|
y replicar el comportamiento de bash para el minimo requerido por el subject."
|
||||||
59
minishell-codex/docs/tests_manual.md
Normal file
59
minishell-codex/docs/tests_manual.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# Minishell - Checklist de pruebas manuales
|
||||||
|
|
||||||
|
Ejecuta en `minishell-codex/`:
|
||||||
|
- `make`
|
||||||
|
- `./minishell`
|
||||||
|
|
||||||
|
## 1. Prompt y salida
|
||||||
|
- Iniciar y salir con `ctrl-D`.
|
||||||
|
- `exit` debe cerrar el shell con el ultimo status.
|
||||||
|
|
||||||
|
## 2. Comandos simples
|
||||||
|
- `ls`
|
||||||
|
- `pwd`
|
||||||
|
- `echo hola`
|
||||||
|
|
||||||
|
## 3. Builtins
|
||||||
|
- `echo -n hola` (sin salto de linea)
|
||||||
|
- `cd /` luego `pwd`
|
||||||
|
- `export TEST=42` luego `env | grep TEST`
|
||||||
|
- `unset TEST` luego `env | grep TEST` (no debe aparecer)
|
||||||
|
- `env` sin argumentos
|
||||||
|
- `exit 2`
|
||||||
|
|
||||||
|
## 4. Expansion
|
||||||
|
- `echo $HOME`
|
||||||
|
- `echo $?` despues de un comando que falle (ej: `ls noexiste`)
|
||||||
|
- `echo '$HOME'` (no expande)
|
||||||
|
- `echo "$HOME"` (si expande)
|
||||||
|
|
||||||
|
## 5. Pipes
|
||||||
|
- `ls | wc -l`
|
||||||
|
- `echo hola | cat`
|
||||||
|
- `cat /etc/passwd | grep root | wc -l`
|
||||||
|
|
||||||
|
## 6. Redirecciones
|
||||||
|
- `echo hola > out.txt` y luego `cat out.txt`
|
||||||
|
- `echo 1 >> out.txt` y luego `cat out.txt`
|
||||||
|
- `cat < out.txt`
|
||||||
|
|
||||||
|
## 7. Heredoc
|
||||||
|
- `cat << EOF`
|
||||||
|
- escribir varias lineas
|
||||||
|
- `EOF`
|
||||||
|
- Ver que se imprime todo lo escrito.
|
||||||
|
|
||||||
|
## 8. Comillas
|
||||||
|
- `echo "a b c"` (una sola palabra)
|
||||||
|
- `echo 'a b c'` (una sola palabra)
|
||||||
|
- `echo "a 'b' c"`
|
||||||
|
|
||||||
|
## 9. Errores de parseo
|
||||||
|
- `| ls` (no debe ejecutar)
|
||||||
|
- `echo hola >` (error)
|
||||||
|
- `echo "hola` (comillas sin cerrar)
|
||||||
|
|
||||||
|
## 10. Senales
|
||||||
|
- `ctrl-C` en prompt: debe limpiar linea y mostrar prompt nuevo.
|
||||||
|
- `sleep 5` y `ctrl-C`: debe interrumpir el proceso.
|
||||||
|
- `ctrl-\` no debe imprimir nada en prompt interactivo.
|
||||||
136
minishell-codex/include/minishell.h
Normal file
136
minishell-codex/include/minishell.h
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
#ifndef MINISHELL_H
|
||||||
|
#define MINISHELL_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#include <readline/history.h>
|
||||||
|
|
||||||
|
#define MS_PROMPT "minishell> "
|
||||||
|
|
||||||
|
extern int g_signal;
|
||||||
|
|
||||||
|
typedef enum e_tokentype
|
||||||
|
{
|
||||||
|
TOK_WORD,
|
||||||
|
TOK_PIPE,
|
||||||
|
TOK_REDIR_IN,
|
||||||
|
TOK_REDIR_OUT,
|
||||||
|
TOK_REDIR_APPEND,
|
||||||
|
TOK_HEREDOC
|
||||||
|
} t_tokentype;
|
||||||
|
|
||||||
|
typedef struct s_token
|
||||||
|
{
|
||||||
|
char *text;
|
||||||
|
t_tokentype type;
|
||||||
|
struct s_token *next;
|
||||||
|
} t_token;
|
||||||
|
|
||||||
|
typedef enum e_redirtype
|
||||||
|
{
|
||||||
|
REDIR_IN,
|
||||||
|
REDIR_OUT,
|
||||||
|
REDIR_APPEND,
|
||||||
|
REDIR_HEREDOC
|
||||||
|
} t_redirtype;
|
||||||
|
|
||||||
|
typedef struct s_redir
|
||||||
|
{
|
||||||
|
t_redirtype type;
|
||||||
|
char *target;
|
||||||
|
int fd;
|
||||||
|
struct s_redir *next;
|
||||||
|
} t_redir;
|
||||||
|
|
||||||
|
typedef struct s_command
|
||||||
|
{
|
||||||
|
char **argv;
|
||||||
|
int argc;
|
||||||
|
char *path;
|
||||||
|
t_redir *redirs;
|
||||||
|
} t_command;
|
||||||
|
|
||||||
|
typedef struct s_pipeline
|
||||||
|
{
|
||||||
|
t_command **cmds;
|
||||||
|
size_t count;
|
||||||
|
} t_pipeline;
|
||||||
|
|
||||||
|
typedef struct s_env
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
char *value;
|
||||||
|
struct s_env *next;
|
||||||
|
} t_env;
|
||||||
|
|
||||||
|
typedef struct s_shell
|
||||||
|
{
|
||||||
|
t_env *env;
|
||||||
|
int exit_status;
|
||||||
|
int last_status;
|
||||||
|
int exit_requested;
|
||||||
|
int interactive;
|
||||||
|
} t_shell;
|
||||||
|
|
||||||
|
/* core */
|
||||||
|
void ms_init(t_shell *sh, char **envp);
|
||||||
|
void ms_loop(t_shell *sh);
|
||||||
|
void ms_cleanup(t_shell *sh);
|
||||||
|
void ms_setup_signals(t_shell *sh);
|
||||||
|
void ms_set_child_signals(void);
|
||||||
|
void ms_set_heredoc_signals(void);
|
||||||
|
|
||||||
|
/* parser */
|
||||||
|
t_token *lex_line(const char *line, int *error);
|
||||||
|
void free_tokens(t_token *toks);
|
||||||
|
|
||||||
|
t_pipeline *parse_tokens(t_token *toks, int *error);
|
||||||
|
void free_pipeline(t_pipeline *p);
|
||||||
|
|
||||||
|
int expand_pipeline(t_pipeline *p, t_shell *sh);
|
||||||
|
|
||||||
|
/* executor */
|
||||||
|
int execute_pipeline(t_pipeline *p, t_shell *sh);
|
||||||
|
int prepare_heredocs(t_pipeline *p, t_shell *sh);
|
||||||
|
int apply_redirections(t_command *cmd, int *saved_stdin, int *saved_stdout);
|
||||||
|
void restore_redirections(int saved_stdin, int saved_stdout);
|
||||||
|
char *resolve_path(const char *cmd, t_shell *sh);
|
||||||
|
|
||||||
|
/* env */
|
||||||
|
void env_init(t_shell *sh, char **envp);
|
||||||
|
void env_clear(t_shell *sh);
|
||||||
|
char *env_get(t_shell *sh, const char *key);
|
||||||
|
int env_set(t_shell *sh, const char *key, const char *value);
|
||||||
|
int env_unset(t_shell *sh, const char *key);
|
||||||
|
char **env_to_envp(t_shell *sh);
|
||||||
|
void env_free_envp(char **envp);
|
||||||
|
void env_print(t_shell *sh);
|
||||||
|
|
||||||
|
/* builtins */
|
||||||
|
int is_builtin(const char *name);
|
||||||
|
int exec_builtin(t_command *cmd, t_shell *sh);
|
||||||
|
|
||||||
|
/* utils */
|
||||||
|
int ms_is_space(int c);
|
||||||
|
int ms_is_alpha(int c);
|
||||||
|
int ms_is_alnum(int c);
|
||||||
|
int ms_is_digit(int c);
|
||||||
|
char *ms_strdup(const char *s);
|
||||||
|
char *ms_strndup(const char *s, size_t n);
|
||||||
|
char *ms_strjoin(const char *a, const char *b);
|
||||||
|
char *ms_strjoin3(const char *a, const char *b, const char *c);
|
||||||
|
char *ms_substr(const char *s, size_t start, size_t len);
|
||||||
|
char **ms_split(const char *s, char delim);
|
||||||
|
void ms_free_split(char **sp);
|
||||||
|
char *ms_itoa(int n);
|
||||||
|
|
||||||
|
#endif
|
||||||
221
minishell-codex/src/builtins/builtins.c
Normal file
221
minishell-codex/src/builtins/builtins.c
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static int builtin_echo(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_cd(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_pwd(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_env(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_export(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_unset(t_command *cmd, t_shell *sh);
|
||||||
|
static int builtin_exit(t_command *cmd, t_shell *sh);
|
||||||
|
|
||||||
|
int is_builtin(const char *name)
|
||||||
|
{
|
||||||
|
if (!name)
|
||||||
|
return 0;
|
||||||
|
return (strcmp(name, "echo") == 0 || strcmp(name, "cd") == 0
|
||||||
|
|| strcmp(name, "pwd") == 0 || strcmp(name, "env") == 0
|
||||||
|
|| strcmp(name, "export") == 0 || strcmp(name, "unset") == 0
|
||||||
|
|| strcmp(name, "exit") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int exec_builtin(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
if (strcmp(cmd->argv[0], "echo") == 0)
|
||||||
|
return builtin_echo(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "cd") == 0)
|
||||||
|
return builtin_cd(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "pwd") == 0)
|
||||||
|
return builtin_pwd(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "env") == 0)
|
||||||
|
return builtin_env(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "export") == 0)
|
||||||
|
return builtin_export(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "unset") == 0)
|
||||||
|
return builtin_unset(cmd, sh);
|
||||||
|
if (strcmp(cmd->argv[0], "exit") == 0)
|
||||||
|
return builtin_exit(cmd, sh);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_echo(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
int newline = 1;
|
||||||
|
|
||||||
|
(void)sh;
|
||||||
|
while (cmd->argv[i] && cmd->argv[i][0] == '-' && cmd->argv[i][1] == 'n')
|
||||||
|
{
|
||||||
|
int j = 2;
|
||||||
|
while (cmd->argv[i][j] == 'n')
|
||||||
|
j++;
|
||||||
|
if (cmd->argv[i][j] != '\0')
|
||||||
|
break;
|
||||||
|
newline = 0;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
while (cmd->argv[i])
|
||||||
|
{
|
||||||
|
printf("%s", cmd->argv[i]);
|
||||||
|
if (cmd->argv[i + 1])
|
||||||
|
printf(" ");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (newline)
|
||||||
|
printf("\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_pwd(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
char buf[4096];
|
||||||
|
(void)cmd;
|
||||||
|
(void)sh;
|
||||||
|
if (getcwd(buf, sizeof(buf)))
|
||||||
|
printf("%s\n", buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_cd(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
char cwd[4096];
|
||||||
|
|
||||||
|
if (cmd->argc > 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: cd: too many arguments\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (cmd->argc == 1)
|
||||||
|
path = env_get(sh, "HOME");
|
||||||
|
else
|
||||||
|
path = cmd->argv[1];
|
||||||
|
if (!path)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: cd: HOME not set\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (getcwd(cwd, sizeof(cwd)))
|
||||||
|
env_set(sh, "OLDPWD", cwd);
|
||||||
|
if (chdir(path) != 0)
|
||||||
|
{
|
||||||
|
perror("minishell: cd");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (getcwd(cwd, sizeof(cwd)))
|
||||||
|
env_set(sh, "PWD", cwd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_env(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
if (cmd->argc > 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: env: too many arguments\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
env_print(sh);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int valid_identifier(const char *s)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
if (!s || !ms_is_alpha((unsigned char)s[0]))
|
||||||
|
return 0;
|
||||||
|
while (s[i])
|
||||||
|
{
|
||||||
|
if (!ms_is_alnum((unsigned char)s[i]))
|
||||||
|
return 0;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_export(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
if (cmd->argc == 1)
|
||||||
|
{
|
||||||
|
env_print(sh);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
while (cmd->argv[i])
|
||||||
|
{
|
||||||
|
char *eq = strchr(cmd->argv[i], '=');
|
||||||
|
if (eq)
|
||||||
|
{
|
||||||
|
char *key = ms_strndup(cmd->argv[i], (size_t)(eq - cmd->argv[i]));
|
||||||
|
char *val = ms_strdup(eq + 1);
|
||||||
|
if (!valid_identifier(key))
|
||||||
|
fprintf(stderr, "minishell: export: `%s': not a valid identifier\n", cmd->argv[i]);
|
||||||
|
else
|
||||||
|
env_set(sh, key, val);
|
||||||
|
free(key);
|
||||||
|
free(val);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!valid_identifier(cmd->argv[i]))
|
||||||
|
fprintf(stderr, "minishell: export: `%s': not a valid identifier\n", cmd->argv[i]);
|
||||||
|
else
|
||||||
|
env_set(sh, cmd->argv[i], "");
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_unset(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
while (cmd->argv[i])
|
||||||
|
{
|
||||||
|
env_unset(sh, cmd->argv[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int builtin_exit(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
long code = sh->exit_status;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (sh->interactive)
|
||||||
|
printf("exit\n");
|
||||||
|
if (cmd->argc == 1)
|
||||||
|
{
|
||||||
|
sh->exit_requested = 1;
|
||||||
|
sh->exit_status = (int)(code & 0xFF);
|
||||||
|
return sh->exit_status;
|
||||||
|
}
|
||||||
|
if (cmd->argc > 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: exit: too many arguments\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (cmd->argv[1][i] == '+' || cmd->argv[1][i] == '-')
|
||||||
|
i++;
|
||||||
|
if (cmd->argv[1][i] == '\0')
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: exit: %s: numeric argument required\n", cmd->argv[1]);
|
||||||
|
sh->exit_requested = 1;
|
||||||
|
sh->exit_status = 2;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
while (cmd->argv[1][i])
|
||||||
|
{
|
||||||
|
if (!ms_is_digit((unsigned char)cmd->argv[1][i]))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: exit: %s: numeric argument required\n", cmd->argv[1]);
|
||||||
|
sh->exit_requested = 1;
|
||||||
|
sh->exit_status = 2;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
code = strtol(cmd->argv[1], NULL, 10);
|
||||||
|
sh->exit_requested = 1;
|
||||||
|
sh->exit_status = (int)(code & 0xFF);
|
||||||
|
return sh->exit_status;
|
||||||
|
}
|
||||||
18
minishell-codex/src/core/init.c
Normal file
18
minishell-codex/src/core/init.c
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
void ms_init(t_shell *sh, char **envp)
|
||||||
|
{
|
||||||
|
memset(sh, 0, sizeof(*sh));
|
||||||
|
sh->interactive = isatty(STDIN_FILENO);
|
||||||
|
sh->exit_status = 0;
|
||||||
|
sh->last_status = 0;
|
||||||
|
sh->exit_requested = 0;
|
||||||
|
env_init(sh, envp);
|
||||||
|
ms_setup_signals(sh);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_cleanup(t_shell *sh)
|
||||||
|
{
|
||||||
|
env_clear(sh);
|
||||||
|
rl_clear_history();
|
||||||
|
}
|
||||||
46
minishell-codex/src/core/loop.c
Normal file
46
minishell-codex/src/core/loop.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static void handle_line(t_shell *sh, char *line)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
t_token *toks = NULL;
|
||||||
|
t_pipeline *p = NULL;
|
||||||
|
|
||||||
|
toks = lex_line(line, &error);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
free_tokens(toks);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p = parse_tokens(toks, &error);
|
||||||
|
free_tokens(toks);
|
||||||
|
if (error || !p)
|
||||||
|
{
|
||||||
|
free_pipeline(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (expand_pipeline(p, sh) != 0)
|
||||||
|
{
|
||||||
|
free_pipeline(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sh->exit_status = execute_pipeline(p, sh);
|
||||||
|
sh->last_status = sh->exit_status;
|
||||||
|
free_pipeline(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_loop(t_shell *sh)
|
||||||
|
{
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
while (!sh->exit_requested)
|
||||||
|
{
|
||||||
|
line = readline(MS_PROMPT);
|
||||||
|
if (!line)
|
||||||
|
break;
|
||||||
|
if (line[0] != '\0')
|
||||||
|
add_history(line);
|
||||||
|
handle_line(sh, line);
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
minishell-codex/src/core/signals.c
Normal file
46
minishell-codex/src/core/signals.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
int g_signal = 0;
|
||||||
|
|
||||||
|
static void sigint_handler(int sig)
|
||||||
|
{
|
||||||
|
g_signal = sig;
|
||||||
|
write(STDOUT_FILENO, "\n", 1);
|
||||||
|
rl_on_new_line();
|
||||||
|
rl_replace_line("", 0);
|
||||||
|
rl_redisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sigquit_handler(int sig)
|
||||||
|
{
|
||||||
|
g_signal = sig;
|
||||||
|
(void)sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_setup_signals(t_shell *sh)
|
||||||
|
{
|
||||||
|
struct sigaction sa_int;
|
||||||
|
struct sigaction sa_quit;
|
||||||
|
|
||||||
|
(void)sh;
|
||||||
|
memset(&sa_int, 0, sizeof(sa_int));
|
||||||
|
memset(&sa_quit, 0, sizeof(sa_quit));
|
||||||
|
sa_int.sa_handler = sigint_handler;
|
||||||
|
sa_quit.sa_handler = sigquit_handler;
|
||||||
|
sigemptyset(&sa_int.sa_mask);
|
||||||
|
sigemptyset(&sa_quit.sa_mask);
|
||||||
|
sigaction(SIGINT, &sa_int, NULL);
|
||||||
|
sigaction(SIGQUIT, &sa_quit, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_set_child_signals(void)
|
||||||
|
{
|
||||||
|
signal(SIGINT, SIG_DFL);
|
||||||
|
signal(SIGQUIT, SIG_DFL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_set_heredoc_signals(void)
|
||||||
|
{
|
||||||
|
signal(SIGINT, SIG_DFL);
|
||||||
|
signal(SIGQUIT, SIG_IGN);
|
||||||
|
}
|
||||||
171
minishell-codex/src/env/env.c
vendored
Normal file
171
minishell-codex/src/env/env.c
vendored
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static t_env *env_new(const char *key, const char *value)
|
||||||
|
{
|
||||||
|
t_env *n = (t_env *)calloc(1, sizeof(t_env));
|
||||||
|
if (!n)
|
||||||
|
return NULL;
|
||||||
|
n->key = ms_strdup(key);
|
||||||
|
n->value = value ? ms_strdup(value) : ms_strdup("");
|
||||||
|
if (!n->key || !n->value)
|
||||||
|
{
|
||||||
|
free(n->key);
|
||||||
|
free(n->value);
|
||||||
|
free(n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void env_init(t_shell *sh, char **envp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *eq;
|
||||||
|
|
||||||
|
sh->env = NULL;
|
||||||
|
if (!envp)
|
||||||
|
return;
|
||||||
|
i = 0;
|
||||||
|
while (envp[i])
|
||||||
|
{
|
||||||
|
eq = strchr(envp[i], '=');
|
||||||
|
if (eq)
|
||||||
|
{
|
||||||
|
char *key = ms_strndup(envp[i], (size_t)(eq - envp[i]));
|
||||||
|
char *val = ms_strdup(eq + 1);
|
||||||
|
env_set(sh, key, val);
|
||||||
|
free(key);
|
||||||
|
free(val);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void env_clear(t_shell *sh)
|
||||||
|
{
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
t_env *next;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
next = cur->next;
|
||||||
|
free(cur->key);
|
||||||
|
free(cur->value);
|
||||||
|
free(cur);
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
sh->env = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *env_get(t_shell *sh, const char *key)
|
||||||
|
{
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (strcmp(cur->key, key) == 0)
|
||||||
|
return cur->value;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int env_set(t_shell *sh, const char *key, const char *value)
|
||||||
|
{
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
t_env *prev = NULL;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (strcmp(cur->key, key) == 0)
|
||||||
|
{
|
||||||
|
char *dup = ms_strdup(value ? value : "");
|
||||||
|
if (!dup)
|
||||||
|
return 1;
|
||||||
|
free(cur->value);
|
||||||
|
cur->value = dup;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
cur = env_new(key, value);
|
||||||
|
if (!cur)
|
||||||
|
return 1;
|
||||||
|
if (prev)
|
||||||
|
prev->next = cur;
|
||||||
|
else
|
||||||
|
sh->env = cur;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int env_unset(t_shell *sh, const char *key)
|
||||||
|
{
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
t_env *prev = NULL;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (strcmp(cur->key, key) == 0)
|
||||||
|
{
|
||||||
|
if (prev)
|
||||||
|
prev->next = cur->next;
|
||||||
|
else
|
||||||
|
sh->env = cur->next;
|
||||||
|
free(cur->key);
|
||||||
|
free(cur->value);
|
||||||
|
free(cur);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **env_to_envp(t_shell *sh)
|
||||||
|
{
|
||||||
|
char **envp;
|
||||||
|
int count = 0;
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
envp = (char **)calloc((size_t)count + 1, sizeof(char *));
|
||||||
|
if (!envp)
|
||||||
|
return NULL;
|
||||||
|
cur = sh->env;
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
char *kv = ms_strjoin3(cur->key, "=", cur->value);
|
||||||
|
envp[i++] = kv;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
envp[i] = NULL;
|
||||||
|
return envp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void env_free_envp(char **envp)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
if (!envp)
|
||||||
|
return;
|
||||||
|
while (envp[i])
|
||||||
|
free(envp[i++]);
|
||||||
|
free(envp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void env_print(t_shell *sh)
|
||||||
|
{
|
||||||
|
t_env *cur = sh->env;
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (cur->value)
|
||||||
|
printf("%s=%s\n", cur->key, cur->value);
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
134
minishell-codex/src/executor/exec.c
Normal file
134
minishell-codex/src/executor/exec.c
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static int exec_external(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
char **envp = env_to_envp(sh);
|
||||||
|
if (!envp)
|
||||||
|
return 1;
|
||||||
|
execve(cmd->path, cmd->argv, envp);
|
||||||
|
perror(cmd->path);
|
||||||
|
env_free_envp(envp);
|
||||||
|
return 126;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run_command_child(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
int saved_in, saved_out;
|
||||||
|
|
||||||
|
if (apply_redirections(cmd, &saved_in, &saved_out) != 0)
|
||||||
|
return 1;
|
||||||
|
if (is_builtin(cmd->argv[0]))
|
||||||
|
return exec_builtin(cmd, sh);
|
||||||
|
return exec_external(cmd, sh);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run_command_parent_builtin(t_command *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
int saved_in, saved_out;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (apply_redirections(cmd, &saved_in, &saved_out) != 0)
|
||||||
|
return 1;
|
||||||
|
status = exec_builtin(cmd, sh);
|
||||||
|
restore_redirections(saved_in, saved_out);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setup_pipes(int idx, int count, int pipefd[2])
|
||||||
|
{
|
||||||
|
if (idx + 1 >= count)
|
||||||
|
return 0;
|
||||||
|
if (pipe(pipefd) == -1)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setup_child_fds(int idx, int count, int prev_read, int pipefd[2])
|
||||||
|
{
|
||||||
|
if (prev_read != -1)
|
||||||
|
{
|
||||||
|
dup2(prev_read, STDIN_FILENO);
|
||||||
|
close(prev_read);
|
||||||
|
}
|
||||||
|
if (idx + 1 < count)
|
||||||
|
{
|
||||||
|
dup2(pipefd[1], STDOUT_FILENO);
|
||||||
|
close(pipefd[0]);
|
||||||
|
close(pipefd[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_parent_fds(int idx, int count, int *prev_read, int pipefd[2])
|
||||||
|
{
|
||||||
|
if (*prev_read != -1)
|
||||||
|
close(*prev_read);
|
||||||
|
if (idx + 1 < count)
|
||||||
|
{
|
||||||
|
close(pipefd[1]);
|
||||||
|
*prev_read = pipefd[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*prev_read = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int execute_pipeline(t_pipeline *p, t_shell *sh)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int prev_read = -1;
|
||||||
|
pid_t *pids;
|
||||||
|
int status = 0;
|
||||||
|
|
||||||
|
if (!p || p->count == 0)
|
||||||
|
return 0;
|
||||||
|
if (prepare_heredocs(p, sh) != 0)
|
||||||
|
return sh->exit_status;
|
||||||
|
for (size_t k = 0; k < p->count; k++)
|
||||||
|
{
|
||||||
|
if (!p->cmds[k]->argv || !p->cmds[k]->argv[0])
|
||||||
|
return 1;
|
||||||
|
free(p->cmds[k]->path);
|
||||||
|
p->cmds[k]->path = resolve_path(p->cmds[k]->argv[0], sh);
|
||||||
|
}
|
||||||
|
if (p->count == 1 && is_builtin(p->cmds[0]->argv[0]))
|
||||||
|
return run_command_parent_builtin(p->cmds[0], sh);
|
||||||
|
pids = (pid_t *)calloc(p->count, sizeof(pid_t));
|
||||||
|
if (!pids)
|
||||||
|
return 1;
|
||||||
|
for (i = 0; i < (int)p->count; i++)
|
||||||
|
{
|
||||||
|
int pipefd[2] = {-1, -1};
|
||||||
|
if (setup_pipes(i, (int)p->count, pipefd))
|
||||||
|
break;
|
||||||
|
pids[i] = fork();
|
||||||
|
if (pids[i] == 0)
|
||||||
|
{
|
||||||
|
ms_set_child_signals();
|
||||||
|
setup_child_fds(i, (int)p->count, prev_read, pipefd);
|
||||||
|
if (!p->cmds[i]->path)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "minishell: %s: command not found\n", p->cmds[i]->argv[0]);
|
||||||
|
exit(127);
|
||||||
|
}
|
||||||
|
status = run_command_child(p->cmds[i], sh);
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
close_parent_fds(i, (int)p->count, &prev_read, pipefd);
|
||||||
|
}
|
||||||
|
for (i = 0; i < (int)p->count; i++)
|
||||||
|
{
|
||||||
|
int wstatus = 0;
|
||||||
|
if (pids[i] > 0)
|
||||||
|
{
|
||||||
|
waitpid(pids[i], &wstatus, 0);
|
||||||
|
if (i == (int)p->count - 1)
|
||||||
|
{
|
||||||
|
if (WIFEXITED(wstatus))
|
||||||
|
status = WEXITSTATUS(wstatus);
|
||||||
|
else if (WIFSIGNALED(wstatus))
|
||||||
|
status = 128 + WTERMSIG(wstatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(pids);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
41
minishell-codex/src/executor/path.c
Normal file
41
minishell-codex/src/executor/path.c
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static int has_slash(const char *s)
|
||||||
|
{
|
||||||
|
return (s && strchr(s, '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *resolve_path(const char *cmd, t_shell *sh)
|
||||||
|
{
|
||||||
|
char *path_env;
|
||||||
|
char **parts;
|
||||||
|
char *candidate;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!cmd)
|
||||||
|
return NULL;
|
||||||
|
if (is_builtin(cmd))
|
||||||
|
return ms_strdup(cmd);
|
||||||
|
if (has_slash(cmd))
|
||||||
|
return ms_strdup(cmd);
|
||||||
|
path_env = env_get(sh, "PATH");
|
||||||
|
if (!path_env)
|
||||||
|
return NULL;
|
||||||
|
parts = ms_split(path_env, ':');
|
||||||
|
if (!parts)
|
||||||
|
return NULL;
|
||||||
|
i = 0;
|
||||||
|
while (parts[i])
|
||||||
|
{
|
||||||
|
candidate = ms_strjoin3(parts[i], "/", cmd);
|
||||||
|
if (candidate && access(candidate, X_OK) == 0)
|
||||||
|
{
|
||||||
|
ms_free_split(parts);
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
free(candidate);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
ms_free_split(parts);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
108
minishell-codex/src/executor/redir.c
Normal file
108
minishell-codex/src/executor/redir.c
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static int open_redir(t_redir *r)
|
||||||
|
{
|
||||||
|
if (r->type == REDIR_IN)
|
||||||
|
return open(r->target, O_RDONLY);
|
||||||
|
if (r->type == REDIR_OUT)
|
||||||
|
return open(r->target, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (r->type == REDIR_APPEND)
|
||||||
|
return open(r->target, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int prepare_heredocs(t_pipeline *p, t_shell *sh)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
g_signal = 0;
|
||||||
|
for (i = 0; i < p->count; i++)
|
||||||
|
{
|
||||||
|
t_redir *r = p->cmds[i]->redirs;
|
||||||
|
while (r)
|
||||||
|
{
|
||||||
|
if (r->type == REDIR_HEREDOC)
|
||||||
|
{
|
||||||
|
int fds[2];
|
||||||
|
char *line;
|
||||||
|
ms_set_heredoc_signals();
|
||||||
|
if (pipe(fds) == -1)
|
||||||
|
return 1;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
line = readline("> ");
|
||||||
|
if (!line)
|
||||||
|
break;
|
||||||
|
if (strcmp(line, r->target) == 0)
|
||||||
|
{
|
||||||
|
free(line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write(fds[1], line, strlen(line));
|
||||||
|
write(fds[1], "\n", 1);
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
close(fds[1]);
|
||||||
|
r->fd = fds[0];
|
||||||
|
ms_setup_signals(sh);
|
||||||
|
if (g_signal == SIGINT)
|
||||||
|
{
|
||||||
|
sh->exit_status = 130;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r = r->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int apply_redirections(t_command *cmd, int *saved_stdin, int *saved_stdout)
|
||||||
|
{
|
||||||
|
t_redir *r = cmd->redirs;
|
||||||
|
*saved_stdin = -1;
|
||||||
|
*saved_stdout = -1;
|
||||||
|
while (r)
|
||||||
|
{
|
||||||
|
int fd = -1;
|
||||||
|
if (r->type == REDIR_HEREDOC)
|
||||||
|
fd = r->fd;
|
||||||
|
else
|
||||||
|
fd = open_redir(r);
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
perror(r->target);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (r->type == REDIR_IN || r->type == REDIR_HEREDOC)
|
||||||
|
{
|
||||||
|
if (*saved_stdin == -1)
|
||||||
|
*saved_stdin = dup(STDIN_FILENO);
|
||||||
|
dup2(fd, STDIN_FILENO);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (*saved_stdout == -1)
|
||||||
|
*saved_stdout = dup(STDOUT_FILENO);
|
||||||
|
dup2(fd, STDOUT_FILENO);
|
||||||
|
}
|
||||||
|
if (r->type != REDIR_HEREDOC)
|
||||||
|
close(fd);
|
||||||
|
r = r->next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void restore_redirections(int saved_stdin, int saved_stdout)
|
||||||
|
{
|
||||||
|
if (saved_stdin != -1)
|
||||||
|
{
|
||||||
|
dup2(saved_stdin, STDIN_FILENO);
|
||||||
|
close(saved_stdin);
|
||||||
|
}
|
||||||
|
if (saved_stdout != -1)
|
||||||
|
{
|
||||||
|
dup2(saved_stdout, STDOUT_FILENO);
|
||||||
|
close(saved_stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
minishell-codex/src/main.c
Normal file
17
minishell-codex/src/main.c
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv, char **envp)
|
||||||
|
{
|
||||||
|
t_shell sh;
|
||||||
|
|
||||||
|
(void)argv;
|
||||||
|
if (argc != 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: ./minishell\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ms_init(&sh, envp);
|
||||||
|
ms_loop(&sh);
|
||||||
|
ms_cleanup(&sh);
|
||||||
|
return sh.exit_status;
|
||||||
|
}
|
||||||
113
minishell-codex/src/parser/expand.c
Normal file
113
minishell-codex/src/parser/expand.c
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static void buf_append(char **buf, size_t *len, size_t *cap, const char *s)
|
||||||
|
{
|
||||||
|
size_t slen;
|
||||||
|
char *newbuf;
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
return;
|
||||||
|
slen = strlen(s);
|
||||||
|
if (*len + slen + 1 > *cap)
|
||||||
|
{
|
||||||
|
*cap = (*len + slen + 1) * 2;
|
||||||
|
newbuf = (char *)realloc(*buf, *cap);
|
||||||
|
if (!newbuf)
|
||||||
|
return;
|
||||||
|
*buf = newbuf;
|
||||||
|
}
|
||||||
|
memcpy(*buf + *len, s, slen);
|
||||||
|
*len += slen;
|
||||||
|
(*buf)[*len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void buf_append_char(char **buf, size_t *len, size_t *cap, char c)
|
||||||
|
{
|
||||||
|
char tmp[2];
|
||||||
|
tmp[0] = c;
|
||||||
|
tmp[1] = '\0';
|
||||||
|
buf_append(buf, len, cap, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *expand_word(const char *word, t_shell *sh)
|
||||||
|
{
|
||||||
|
int in_single = 0;
|
||||||
|
int in_double = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
char *out = NULL;
|
||||||
|
size_t len = 0, cap = 0;
|
||||||
|
|
||||||
|
while (word && word[i])
|
||||||
|
{
|
||||||
|
if (word[i] == '\'' && !in_double)
|
||||||
|
{
|
||||||
|
in_single = !in_single;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (word[i] == '"' && !in_single)
|
||||||
|
{
|
||||||
|
in_double = !in_double;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (word[i] == '$' && !in_single)
|
||||||
|
{
|
||||||
|
if (word[i + 1] == '?')
|
||||||
|
{
|
||||||
|
char *v = ms_itoa(sh->last_status);
|
||||||
|
buf_append(&out, &len, &cap, v);
|
||||||
|
free(v);
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ms_is_alpha((unsigned char)word[i + 1]))
|
||||||
|
{
|
||||||
|
size_t j = i + 1;
|
||||||
|
while (word[j] && ms_is_alnum((unsigned char)word[j]))
|
||||||
|
j++;
|
||||||
|
char *name = ms_strndup(word + i + 1, j - (i + 1));
|
||||||
|
char *val = env_get(sh, name);
|
||||||
|
buf_append(&out, &len, &cap, val ? val : "");
|
||||||
|
free(name);
|
||||||
|
i = j;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf_append_char(&out, &len, &cap, word[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (!out)
|
||||||
|
out = ms_strdup("");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int expand_pipeline(t_pipeline *p, t_shell *sh)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
int j;
|
||||||
|
for (i = 0; i < p->count; i++)
|
||||||
|
{
|
||||||
|
for (j = 0; p->cmds[i]->argv && p->cmds[i]->argv[j]; j++)
|
||||||
|
{
|
||||||
|
char *neww = expand_word(p->cmds[i]->argv[j], sh);
|
||||||
|
free(p->cmds[i]->argv[j]);
|
||||||
|
p->cmds[i]->argv[j] = neww;
|
||||||
|
}
|
||||||
|
if (p->cmds[i]->redirs)
|
||||||
|
{
|
||||||
|
t_redir *r = p->cmds[i]->redirs;
|
||||||
|
while (r)
|
||||||
|
{
|
||||||
|
if (r->target)
|
||||||
|
{
|
||||||
|
char *nt = expand_word(r->target, sh);
|
||||||
|
free(r->target);
|
||||||
|
r->target = nt;
|
||||||
|
}
|
||||||
|
r = r->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
131
minishell-codex/src/parser/lexer.c
Normal file
131
minishell-codex/src/parser/lexer.c
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static t_token *token_new(t_tokentype type, const char *text, size_t len)
|
||||||
|
{
|
||||||
|
t_token *t = (t_token *)calloc(1, sizeof(t_token));
|
||||||
|
if (!t)
|
||||||
|
return NULL;
|
||||||
|
t->type = type;
|
||||||
|
if (text)
|
||||||
|
t->text = ms_strndup(text, len);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void token_add(t_token **head, t_token *new)
|
||||||
|
{
|
||||||
|
t_token *cur;
|
||||||
|
if (!new)
|
||||||
|
return;
|
||||||
|
if (!*head)
|
||||||
|
{
|
||||||
|
*head = new;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cur = *head;
|
||||||
|
while (cur->next)
|
||||||
|
cur = cur->next;
|
||||||
|
cur->next = new;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_tokens(t_token *toks)
|
||||||
|
{
|
||||||
|
t_token *n;
|
||||||
|
while (toks)
|
||||||
|
{
|
||||||
|
n = toks->next;
|
||||||
|
free(toks->text);
|
||||||
|
free(toks);
|
||||||
|
toks = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_meta(char c)
|
||||||
|
{
|
||||||
|
return (c == '|' || c == '<' || c == '>');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_word(const char *line, size_t *i, t_token **out)
|
||||||
|
{
|
||||||
|
size_t start = *i;
|
||||||
|
int in_single = 0;
|
||||||
|
int in_double = 0;
|
||||||
|
|
||||||
|
while (line[*i])
|
||||||
|
{
|
||||||
|
if (line[*i] == '\'' && !in_double)
|
||||||
|
in_single = !in_single;
|
||||||
|
else if (line[*i] == '"' && !in_single)
|
||||||
|
in_double = !in_double;
|
||||||
|
else if (!in_single && !in_double)
|
||||||
|
{
|
||||||
|
if (ms_is_space(line[*i]) || is_meta(line[*i]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*i)++;
|
||||||
|
}
|
||||||
|
if (in_single || in_double)
|
||||||
|
return 1;
|
||||||
|
*out = token_new(TOK_WORD, line + start, *i - start);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_token *lex_line(const char *line, int *error)
|
||||||
|
{
|
||||||
|
t_token *toks = NULL;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
*error = 0;
|
||||||
|
while (line[i])
|
||||||
|
{
|
||||||
|
if (ms_is_space(line[i]))
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line[i] == '|')
|
||||||
|
{
|
||||||
|
token_add(&toks, token_new(TOK_PIPE, "|", 1));
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line[i] == '<')
|
||||||
|
{
|
||||||
|
if (line[i + 1] == '<')
|
||||||
|
{
|
||||||
|
token_add(&toks, token_new(TOK_HEREDOC, "<<", 2));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token_add(&toks, token_new(TOK_REDIR_IN, "<", 1));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line[i] == '>')
|
||||||
|
{
|
||||||
|
if (line[i + 1] == '>')
|
||||||
|
{
|
||||||
|
token_add(&toks, token_new(TOK_REDIR_APPEND, ">>", 2));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token_add(&toks, token_new(TOK_REDIR_OUT, ">", 1));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
t_token *w = NULL;
|
||||||
|
if (read_word(line, &i, &w))
|
||||||
|
{
|
||||||
|
*error = 1;
|
||||||
|
free_tokens(toks);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
token_add(&toks, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toks;
|
||||||
|
}
|
||||||
175
minishell-codex/src/parser/parser.c
Normal file
175
minishell-codex/src/parser/parser.c
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static t_command *command_new(void)
|
||||||
|
{
|
||||||
|
t_command *cmd = (t_command *)calloc(1, sizeof(t_command));
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void command_add_arg(t_command *cmd, const char *text)
|
||||||
|
{
|
||||||
|
char **new_argv;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!cmd || !text)
|
||||||
|
return;
|
||||||
|
i = cmd->argc;
|
||||||
|
new_argv = (char **)calloc((size_t)i + 2, sizeof(char *));
|
||||||
|
if (!new_argv)
|
||||||
|
return;
|
||||||
|
for (int j = 0; j < i; j++)
|
||||||
|
new_argv[j] = cmd->argv[j];
|
||||||
|
new_argv[i] = ms_strdup(text);
|
||||||
|
new_argv[i + 1] = NULL;
|
||||||
|
free(cmd->argv);
|
||||||
|
cmd->argv = new_argv;
|
||||||
|
cmd->argc += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void command_add_redir(t_command *cmd, t_redirtype type, const char *target)
|
||||||
|
{
|
||||||
|
t_redir *r = (t_redir *)calloc(1, sizeof(t_redir));
|
||||||
|
t_redir *cur;
|
||||||
|
if (!r)
|
||||||
|
return;
|
||||||
|
r->type = type;
|
||||||
|
r->target = ms_strdup(target);
|
||||||
|
r->fd = -1;
|
||||||
|
r->next = NULL;
|
||||||
|
if (!cmd->redirs)
|
||||||
|
{
|
||||||
|
cmd->redirs = r;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cur = cmd->redirs;
|
||||||
|
while (cur->next)
|
||||||
|
cur = cur->next;
|
||||||
|
cur->next = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_redirs(t_redir *r)
|
||||||
|
{
|
||||||
|
t_redir *n;
|
||||||
|
while (r)
|
||||||
|
{
|
||||||
|
n = r->next;
|
||||||
|
if (r->fd != -1)
|
||||||
|
close(r->fd);
|
||||||
|
free(r->target);
|
||||||
|
free(r);
|
||||||
|
r = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_pipeline(t_pipeline *p)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
if (!p)
|
||||||
|
return;
|
||||||
|
for (i = 0; i < p->count; i++)
|
||||||
|
{
|
||||||
|
if (p->cmds[i])
|
||||||
|
{
|
||||||
|
for (int j = 0; p->cmds[i]->argv && p->cmds[i]->argv[j]; j++)
|
||||||
|
free(p->cmds[i]->argv[j]);
|
||||||
|
free(p->cmds[i]->argv);
|
||||||
|
free(p->cmds[i]->path);
|
||||||
|
free_redirs(p->cmds[i]->redirs);
|
||||||
|
free(p->cmds[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(p->cmds);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pipeline_add_cmd(t_pipeline *p, t_command *cmd)
|
||||||
|
{
|
||||||
|
t_command **new_cmds;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
new_cmds = (t_command **)calloc(p->count + 1, sizeof(t_command *));
|
||||||
|
if (!new_cmds)
|
||||||
|
return 1;
|
||||||
|
for (i = 0; i < p->count; i++)
|
||||||
|
new_cmds[i] = p->cmds[i];
|
||||||
|
new_cmds[p->count] = cmd;
|
||||||
|
free(p->cmds);
|
||||||
|
p->cmds = new_cmds;
|
||||||
|
p->count += 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_pipeline *parse_tokens(t_token *toks, int *error)
|
||||||
|
{
|
||||||
|
t_pipeline *p;
|
||||||
|
t_command *cmd;
|
||||||
|
t_token *cur;
|
||||||
|
|
||||||
|
*error = 0;
|
||||||
|
p = (t_pipeline *)calloc(1, sizeof(t_pipeline));
|
||||||
|
if (!p)
|
||||||
|
return NULL;
|
||||||
|
cmd = command_new();
|
||||||
|
if (!cmd)
|
||||||
|
{
|
||||||
|
free(p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cur = toks;
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (cur->type == TOK_PIPE)
|
||||||
|
{
|
||||||
|
if (cmd->argc == 0)
|
||||||
|
{
|
||||||
|
*error = 1;
|
||||||
|
free_pipeline(p);
|
||||||
|
free(cmd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pipeline_add_cmd(p, cmd);
|
||||||
|
cmd = command_new();
|
||||||
|
if (!cmd)
|
||||||
|
{
|
||||||
|
*error = 1;
|
||||||
|
free_pipeline(p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur->type == TOK_REDIR_IN || cur->type == TOK_REDIR_OUT
|
||||||
|
|| cur->type == TOK_REDIR_APPEND || cur->type == TOK_HEREDOC)
|
||||||
|
{
|
||||||
|
if (!cur->next || cur->next->type != TOK_WORD)
|
||||||
|
{
|
||||||
|
*error = 1;
|
||||||
|
free_pipeline(p);
|
||||||
|
free(cmd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (cur->type == TOK_REDIR_IN)
|
||||||
|
command_add_redir(cmd, REDIR_IN, cur->next->text);
|
||||||
|
else if (cur->type == TOK_REDIR_OUT)
|
||||||
|
command_add_redir(cmd, REDIR_OUT, cur->next->text);
|
||||||
|
else if (cur->type == TOK_REDIR_APPEND)
|
||||||
|
command_add_redir(cmd, REDIR_APPEND, cur->next->text);
|
||||||
|
else if (cur->type == TOK_HEREDOC)
|
||||||
|
command_add_redir(cmd, REDIR_HEREDOC, cur->next->text);
|
||||||
|
cur = cur->next->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur->type == TOK_WORD)
|
||||||
|
command_add_arg(cmd, cur->text);
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
if (cmd->argc == 0)
|
||||||
|
{
|
||||||
|
*error = 1;
|
||||||
|
free_pipeline(p);
|
||||||
|
free(cmd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pipeline_add_cmd(p, cmd);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
6
minishell-codex/src/utils/char_utils.c
Normal file
6
minishell-codex/src/utils/char_utils.c
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
int ms_is_space(int c) { return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f'); }
|
||||||
|
int ms_is_alpha(int c) { return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'); }
|
||||||
|
int ms_is_digit(int c) { return (c >= '0' && c <= '9'); }
|
||||||
|
int ms_is_alnum(int c) { return (ms_is_alpha(c) || ms_is_digit(c)); }
|
||||||
67
minishell-codex/src/utils/split_utils.c
Normal file
67
minishell-codex/src/utils/split_utils.c
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static size_t count_parts(const char *s, char delim)
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
int in = 0;
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
if (*s == delim)
|
||||||
|
in = 0;
|
||||||
|
else if (!in)
|
||||||
|
{
|
||||||
|
in = 1;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **ms_split(const char *s, char delim)
|
||||||
|
{
|
||||||
|
char **out;
|
||||||
|
size_t parts;
|
||||||
|
size_t i = 0;
|
||||||
|
size_t start = 0;
|
||||||
|
size_t len = 0;
|
||||||
|
int in = 0;
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
parts = count_parts(s, delim);
|
||||||
|
out = (char **)calloc(parts + 1, sizeof(char *));
|
||||||
|
if (!out)
|
||||||
|
return NULL;
|
||||||
|
while (s[i])
|
||||||
|
{
|
||||||
|
if (s[i] == delim)
|
||||||
|
{
|
||||||
|
if (in)
|
||||||
|
{
|
||||||
|
out[len++] = ms_strndup(s + start, i - start);
|
||||||
|
in = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!in)
|
||||||
|
{
|
||||||
|
in = 1;
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (in)
|
||||||
|
out[len++] = ms_strndup(s + start, i - start);
|
||||||
|
out[len] = NULL;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_free_split(char **sp)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
if (!sp)
|
||||||
|
return;
|
||||||
|
while (sp[i])
|
||||||
|
free(sp[i++]);
|
||||||
|
free(sp);
|
||||||
|
}
|
||||||
100
minishell-codex/src/utils/str_utils.c
Normal file
100
minishell-codex/src/utils/str_utils.c
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
char *ms_strdup(const char *s)
|
||||||
|
{
|
||||||
|
char *out;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
len = strlen(s);
|
||||||
|
out = (char *)malloc(len + 1);
|
||||||
|
if (!out)
|
||||||
|
return NULL;
|
||||||
|
memcpy(out, s, len);
|
||||||
|
out[len] = '\0';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ms_strndup(const char *s, size_t n)
|
||||||
|
{
|
||||||
|
char *out;
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
out = (char *)malloc(n + 1);
|
||||||
|
if (!out)
|
||||||
|
return NULL;
|
||||||
|
memcpy(out, s, n);
|
||||||
|
out[n] = '\0';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ms_substr(const char *s, size_t start, size_t len)
|
||||||
|
{
|
||||||
|
size_t slen;
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
slen = strlen(s);
|
||||||
|
if (start >= slen)
|
||||||
|
return ms_strdup("");
|
||||||
|
if (start + len > slen)
|
||||||
|
len = slen - start;
|
||||||
|
return ms_strndup(s + start, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ms_strjoin(const char *a, const char *b)
|
||||||
|
{
|
||||||
|
size_t la;
|
||||||
|
size_t lb;
|
||||||
|
char *out;
|
||||||
|
|
||||||
|
if (!a || !b)
|
||||||
|
return NULL;
|
||||||
|
la = strlen(a);
|
||||||
|
lb = strlen(b);
|
||||||
|
out = (char *)malloc(la + lb + 1);
|
||||||
|
if (!out)
|
||||||
|
return NULL;
|
||||||
|
memcpy(out, a, la);
|
||||||
|
memcpy(out + la, b, lb);
|
||||||
|
out[la + lb] = '\0';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ms_strjoin3(const char *a, const char *b, const char *c)
|
||||||
|
{
|
||||||
|
char *ab;
|
||||||
|
char *abc;
|
||||||
|
|
||||||
|
ab = ms_strjoin(a, b);
|
||||||
|
if (!ab)
|
||||||
|
return NULL;
|
||||||
|
abc = ms_strjoin(ab, c);
|
||||||
|
free(ab);
|
||||||
|
return abc;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ms_itoa(int n)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
int i;
|
||||||
|
int neg;
|
||||||
|
long nb;
|
||||||
|
|
||||||
|
nb = n;
|
||||||
|
neg = (nb < 0);
|
||||||
|
if (neg)
|
||||||
|
nb = -nb;
|
||||||
|
i = 30;
|
||||||
|
buf[31] = '\0';
|
||||||
|
if (nb == 0)
|
||||||
|
buf[i--] = '0';
|
||||||
|
while (nb > 0)
|
||||||
|
{
|
||||||
|
buf[i--] = (char)('0' + (nb % 10));
|
||||||
|
nb /= 10;
|
||||||
|
}
|
||||||
|
if (neg)
|
||||||
|
buf[i--] = '-';
|
||||||
|
return ms_strdup(&buf[i + 1]);
|
||||||
|
}
|
||||||
@@ -6,28 +6,59 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/12/01 17:04:57 by sede-san #+# #+# */
|
/* Created: 2025/12/01 17:04:57 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/12/01 17:54:26 by sede-san ### ########.fr */
|
/* Updated: 2026/02/08 19:51:38 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "minishell.h"
|
#include "builtins.h"
|
||||||
|
|
||||||
u_int8_t set_builtins(
|
static uint8_t register_builtin(
|
||||||
t_minishell *msh
|
t_minishell *minishell,
|
||||||
) {
|
const char *name,
|
||||||
msh->builtins = ft_hashmap_new(4, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
t_builtin_func builtin
|
||||||
if (msh->builtins == NULL)
|
)
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
|
||||||
|
key = ft_strdup(name);
|
||||||
|
if (key == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
ft_hashmap_put(msh->builtins, ft_strdup("cd"), builtin_cd);
|
ft_hashmap_put(minishell->builtins, key, builtin);
|
||||||
ft_hashmap_put(msh->builtins, ft_strdup("echo"), builtin_echo);
|
if (!ft_hashmap_contains_key(minishell->builtins, name))
|
||||||
ft_hashmap_put(msh->builtins, ft_strdup("exit"), builtin_exit);
|
{
|
||||||
ft_hashmap_put(msh->builtins, ft_strdup("pwd"), builtin_pwd);
|
free(key);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
u_int8_t is_builtin(
|
uint8_t set_builtins(
|
||||||
const char *command_name,
|
t_minishell *minishell
|
||||||
t_minishell *msh
|
|
||||||
) {
|
) {
|
||||||
return (ft_hashmap_contains_key(msh->builtins, command_name));
|
minishell->builtins
|
||||||
|
= ft_hashmap_new(7, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
||||||
|
if (minishell->builtins == NULL)
|
||||||
|
return (0);
|
||||||
|
if (!register_builtin(minishell, "cd", builtin_cd)
|
||||||
|
|| !register_builtin(minishell, "echo", builtin_echo)
|
||||||
|
|| !register_builtin(minishell, "env", builtin_env)
|
||||||
|
|| !register_builtin(minishell, "exit", builtin_exit)
|
||||||
|
|| !register_builtin(minishell, "export", builtin_export)
|
||||||
|
|| !register_builtin(minishell, "pwd", builtin_pwd)
|
||||||
|
|| !register_builtin(minishell, "unset", builtin_unset))
|
||||||
|
{
|
||||||
|
ft_hashmap_clear_keys(&minishell->builtins);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t is_builtin(
|
||||||
|
const char *command_name,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
if (command_name == NULL || minishell == NULL
|
||||||
|
|| minishell->builtins == NULL)
|
||||||
|
return (0);
|
||||||
|
return (ft_hashmap_contains_key(minishell->builtins, command_name));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,43 +12,104 @@
|
|||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
|
|
||||||
static u_int8_t handle_error(t_command cmd, t_minishell *msh, char *path);
|
static uint8_t handle_error(void);
|
||||||
|
static void update_pwd_vars(t_minishell *msh, char *oldpwd);
|
||||||
|
static char *get_path_from_env(
|
||||||
|
t_minishell *msh,
|
||||||
|
const char *env_name,
|
||||||
|
const char *error,
|
||||||
|
uint8_t *status
|
||||||
|
);
|
||||||
|
static char *resolve_cd_path(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh,
|
||||||
|
uint8_t *status
|
||||||
|
);
|
||||||
|
|
||||||
u_int8_t builtin_cd(
|
uint8_t builtin_cd(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
t_minishell *msh
|
t_minishell *msh
|
||||||
){
|
){
|
||||||
char *path;
|
char *path;
|
||||||
|
char *oldpwd;
|
||||||
|
uint8_t status;
|
||||||
|
bool show_pwd;
|
||||||
|
|
||||||
if (cmd.argc > 2)
|
path = resolve_cd_path(cmd, msh, &status);
|
||||||
{
|
if (status != EXIT_SUCCESS)
|
||||||
ft_eputendl("minishell: cd: too many arguments");
|
return (status);
|
||||||
return (2);
|
show_pwd = (cmd.argc == 2 && ft_strcmp(cmd.argv[1], "-") == 0);
|
||||||
}
|
oldpwd = getcwd(NULL, 0);
|
||||||
else if (cmd.argc == 1)
|
|
||||||
path = get_env("HOME", msh);
|
|
||||||
else
|
|
||||||
path = cmd.argv[1];
|
|
||||||
if (chdir(path) == -1)
|
if (chdir(path) == -1)
|
||||||
return (handle_error(cmd, msh, path));
|
{
|
||||||
|
free(oldpwd);
|
||||||
|
return (handle_error());
|
||||||
|
}
|
||||||
|
update_pwd_vars(msh, oldpwd);
|
||||||
|
if (show_pwd && get_env("PWD", msh) != NULL)
|
||||||
|
ft_putendl(get_env("PWD", msh));
|
||||||
|
free(oldpwd);
|
||||||
return (EXIT_SUCCESS);
|
return (EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u_int8_t handle_error(
|
static uint8_t handle_error(void)
|
||||||
|
{
|
||||||
|
perror("minishell: cd");
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_pwd_vars(
|
||||||
|
t_minishell *msh,
|
||||||
|
char *oldpwd
|
||||||
|
){
|
||||||
|
char *newpwd;
|
||||||
|
|
||||||
|
if (oldpwd != NULL)
|
||||||
|
set_env("OLDPWD", oldpwd, msh);
|
||||||
|
newpwd = getcwd(NULL, 0);
|
||||||
|
if (newpwd != NULL)
|
||||||
|
{
|
||||||
|
set_env("PWD", newpwd, msh);
|
||||||
|
free(newpwd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *resolve_cd_path(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
t_minishell *msh,
|
t_minishell *msh,
|
||||||
char *path
|
uint8_t *status
|
||||||
){
|
){
|
||||||
u_int8_t exit_code;
|
if (cmd.argc > 2)
|
||||||
|
{
|
||||||
(void)msh;
|
ft_eputendl("minishell: cd: too many arguments");
|
||||||
exit_code = 0;
|
*status = EXIT_FAILURE;
|
||||||
if (access(path, F_OK) != -1)
|
return (NULL);
|
||||||
// No such file or directory
|
}
|
||||||
exit_code = 1;
|
if (cmd.argc == 2 && ft_strcmp(cmd.argv[1], "-") == 0)
|
||||||
else if (access(path, X_OK) == -1)
|
return (get_path_from_env(msh, "OLDPWD",
|
||||||
// Permission denied
|
"minishell: cd: OLDPWD not set", status));
|
||||||
exit_code = 2;
|
if (cmd.argc == 1)
|
||||||
perror(cmd.argv[0]);
|
return (get_path_from_env(msh, "HOME",
|
||||||
return (exit_code);
|
"minishell: cd: HOME not set", status));
|
||||||
|
*status = EXIT_SUCCESS;
|
||||||
|
return (cmd.argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *get_path_from_env(
|
||||||
|
t_minishell *msh,
|
||||||
|
const char *env_name,
|
||||||
|
const char *error,
|
||||||
|
uint8_t *status
|
||||||
|
){
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
path = get_env(env_name, msh);
|
||||||
|
if (path == NULL)
|
||||||
|
{
|
||||||
|
ft_eputendl((char *)error);
|
||||||
|
*status = EXIT_FAILURE;
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
*status = EXIT_SUCCESS;
|
||||||
|
return (path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
#include "echo_def.h"
|
#include "echo_def.h"
|
||||||
|
|
||||||
u_int8_t builtin_echo(
|
uint8_t builtin_echo(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
t_minishell *msh
|
t_minishell *msh
|
||||||
){
|
){
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include "echo_def.h"
|
#include "echo_def.h"
|
||||||
|
|
||||||
static void assign_flags(t_args *args, t_command cmd, size_t *i);
|
static void assign_flags(t_args *args, t_command cmd, size_t *i);
|
||||||
static u_int8_t is_valid_flag(t_args *args, char *flag);
|
static uint8_t is_valid_flag(t_args *args, char *flag);
|
||||||
|
|
||||||
static void assign_default_flags(
|
static void assign_default_flags(
|
||||||
t_args *args
|
t_args *args
|
||||||
@@ -51,7 +51,7 @@ static void assign_flags(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static u_int8_t is_valid_flag(
|
static uint8_t is_valid_flag(
|
||||||
t_args *args,
|
t_args *args,
|
||||||
char *flag
|
char *flag
|
||||||
){
|
){
|
||||||
|
|||||||
58
src/builtins/env/env.c
vendored
Normal file
58
src/builtins/env/env.c
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* env.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/09 22:05:00 by codex #+# #+# */
|
||||||
|
/* Updated: 2026/02/09 22:05:00 by codex ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "builtins.h"
|
||||||
|
|
||||||
|
static void print_entry(t_map_entry *entry);
|
||||||
|
static void print_env(t_minishell *msh);
|
||||||
|
|
||||||
|
uint8_t builtin_env(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (cmd.argc > 1)
|
||||||
|
{
|
||||||
|
ft_eputendl("minishell: env: too many arguments");
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
print_env(msh);
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_entry(
|
||||||
|
t_map_entry *entry
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ft_putstr((char *)entry->key);
|
||||||
|
ft_putchar('=');
|
||||||
|
if (entry->value != NULL)
|
||||||
|
ft_putstr((char *)entry->value);
|
||||||
|
ft_putchar('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_env(
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
t_list *entries;
|
||||||
|
t_list *current;
|
||||||
|
|
||||||
|
entries = ft_hashmap_entries(msh->variables.environment);
|
||||||
|
current = entries;
|
||||||
|
while (current != NULL)
|
||||||
|
{
|
||||||
|
print_entry((t_map_entry *)current->content);
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
ft_lstclear_nodes(&entries);
|
||||||
|
}
|
||||||
@@ -6,39 +6,103 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/30 01:20:48 by sede-san #+# #+# */
|
/* Created: 2025/10/30 01:20:48 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/12/01 19:15:08 by sede-san ### ########.fr */
|
/* Updated: 2026/02/08 21:16:18 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
u_int8_t builtin_exit(
|
static uint8_t get_uint8_from_num(const char *arg, uint8_t *status);
|
||||||
|
static uint8_t has_overflow(
|
||||||
|
uint64_t n,
|
||||||
|
char digit,
|
||||||
|
uint64_t limit
|
||||||
|
);
|
||||||
|
static uint8_t resolve_exit_status(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh,
|
||||||
|
uint8_t *exit_status
|
||||||
|
);
|
||||||
|
|
||||||
|
uint8_t builtin_exit(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
t_minishell *msh
|
t_minishell *msh
|
||||||
){
|
)
|
||||||
ft_eputendl("exit");
|
{
|
||||||
if (cmd.argc == 1)
|
uint8_t exit_status;
|
||||||
{
|
|
||||||
msh->exit = 1;
|
if (isatty(STDIN_FILENO))
|
||||||
// return the last exit_status, if none 0 is returned
|
ft_eputendl("exit");
|
||||||
|
if (!resolve_exit_status(cmd, msh, &exit_status))
|
||||||
return (msh->exit_status);
|
return (msh->exit_status);
|
||||||
}
|
msh->exit = true;
|
||||||
else if (!ft_strisnum(cmd.argv[1]))
|
msh->exit_status = exit_status;
|
||||||
|
return (exit_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t resolve_exit_status(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh,
|
||||||
|
uint8_t *exit_status
|
||||||
|
){
|
||||||
|
if (cmd.argc == 1)
|
||||||
|
*exit_status = msh->exit_status;
|
||||||
|
else if (!get_uint8_from_num(cmd.argv[1], exit_status))
|
||||||
{
|
{
|
||||||
ft_eputstr("exit: ");
|
ft_eprintf("minishell: exit: %s: numeric argument required\n",
|
||||||
ft_eputendl(cmd.argv[1]);
|
cmd.argv[1]);
|
||||||
ft_eputendl(": numeric argument required");
|
msh->exit = true;
|
||||||
return (2);
|
msh->exit_status = 2;
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
else if (cmd.argc > 2)
|
else if (cmd.argc > 2)
|
||||||
{
|
{
|
||||||
ft_eputendl("exit: too many arguments");
|
ft_eputendl("minishell: exit: too many arguments");
|
||||||
return (2);
|
msh->exit_status = EXIT_FAILURE;
|
||||||
}
|
return (0);
|
||||||
else
|
|
||||||
{
|
|
||||||
msh->exit = 1;
|
|
||||||
// cast to u_int8_t causes to return a value between 0 and 255
|
|
||||||
return ((u_int8_t)ft_atol(cmd.argv[1]));
|
|
||||||
}
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t get_uint8_from_num(
|
||||||
|
const char *arg,
|
||||||
|
uint8_t *status
|
||||||
|
){
|
||||||
|
uint64_t n;
|
||||||
|
uint64_t limit;
|
||||||
|
int sign;
|
||||||
|
|
||||||
|
if (arg == NULL || *arg == '\0')
|
||||||
|
return (0);
|
||||||
|
n = 0;
|
||||||
|
sign = 1;
|
||||||
|
if (*arg == '+' || *arg == '-')
|
||||||
|
if (*arg++ == '-')
|
||||||
|
sign = -1;
|
||||||
|
if (*arg == '\0')
|
||||||
|
return (0);
|
||||||
|
limit = LONG_MAX;
|
||||||
|
if (sign == -1)
|
||||||
|
limit = (uint64_t)LONG_MAX + 1;
|
||||||
|
while (*arg != '\0')
|
||||||
|
{
|
||||||
|
if (!ft_isdigit(*arg) || has_overflow(n, *arg, limit))
|
||||||
|
return (0);
|
||||||
|
n = (n * 10) + (*arg++ - '0');
|
||||||
|
}
|
||||||
|
*status = (uint8_t)(n * sign);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t has_overflow(
|
||||||
|
uint64_t n,
|
||||||
|
char digit,
|
||||||
|
uint64_t limit
|
||||||
|
){
|
||||||
|
if (n > (limit / 10))
|
||||||
|
return (1);
|
||||||
|
if (n == (limit / 10) && (uint64_t)(digit - '0') > (limit % 10))
|
||||||
|
return (1);
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|||||||
79
src/builtins/export/export.c
Normal file
79
src/builtins/export/export.c
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* export.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/09 22:05:00 by codex #+# #+# */
|
||||||
|
/* Updated: 2026/02/09 22:05:00 by codex ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "builtins.h"
|
||||||
|
|
||||||
|
static uint8_t is_valid_identifier(const char *arg, size_t name_len);
|
||||||
|
static uint8_t export_one(char *arg, t_minishell *msh);
|
||||||
|
|
||||||
|
uint8_t builtin_export(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint8_t status;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (cmd.argc == 1)
|
||||||
|
return (builtin_env(cmd, msh));
|
||||||
|
status = EXIT_SUCCESS;
|
||||||
|
i = 0;
|
||||||
|
while (cmd.argv[++i] != NULL)
|
||||||
|
if (export_one(cmd.argv[i], msh) != EXIT_SUCCESS)
|
||||||
|
status = EXIT_FAILURE;
|
||||||
|
return (status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t is_valid_identifier(
|
||||||
|
const char *arg,
|
||||||
|
size_t name_len
|
||||||
|
)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (name_len == 0 || arg == NULL)
|
||||||
|
return (0);
|
||||||
|
if (!ft_isalpha(arg[0]) && arg[0] != '_')
|
||||||
|
return (0);
|
||||||
|
i = 0;
|
||||||
|
while (++i < name_len)
|
||||||
|
if (!ft_isalnum(arg[i]) && arg[i] != '_')
|
||||||
|
return (0);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t export_one(
|
||||||
|
char *arg,
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
char *eq_pos;
|
||||||
|
char *name;
|
||||||
|
size_t name_len;
|
||||||
|
|
||||||
|
eq_pos = ft_strchr(arg, '=');
|
||||||
|
name_len = ft_strlen(arg);
|
||||||
|
if (eq_pos != NULL)
|
||||||
|
name_len = (size_t)(eq_pos - arg);
|
||||||
|
if (!is_valid_identifier(arg, name_len))
|
||||||
|
return (ft_eprintf("minishell: export: `%s': not a valid identifier\n",
|
||||||
|
arg), EXIT_FAILURE);
|
||||||
|
name = ft_substr(arg, 0, name_len);
|
||||||
|
if (name == NULL)
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
if (eq_pos != NULL)
|
||||||
|
set_env(name, eq_pos + 1, msh);
|
||||||
|
else
|
||||||
|
set_env(name, "", msh);
|
||||||
|
free(name);
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
@@ -12,15 +12,21 @@
|
|||||||
|
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
|
|
||||||
u_int8_t builtin_pwd(
|
uint8_t builtin_pwd(
|
||||||
t_command cmd,
|
t_command cmd,
|
||||||
t_minishell *msh
|
t_minishell *msh
|
||||||
){
|
){
|
||||||
char cwd[PATH_MAX];
|
char *cwd;
|
||||||
|
|
||||||
(void)cmd;
|
(void)cmd;
|
||||||
(void)msh;
|
(void)msh;
|
||||||
if (getcwd(cwd, PATH_MAX) != NULL)
|
cwd = getcwd(NULL, 0);
|
||||||
ft_putendl(cwd);
|
if (cwd == NULL)
|
||||||
|
{
|
||||||
|
perror("minishell: pwd");
|
||||||
|
return (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
ft_putendl(cwd);
|
||||||
|
free(cwd);
|
||||||
return (EXIT_SUCCESS);
|
return (EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|||||||
61
src/builtins/unset/unset.c
Normal file
61
src/builtins/unset/unset.c
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* unset.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/09 22:05:00 by codex #+# #+# */
|
||||||
|
/* Updated: 2026/02/09 22:05:00 by codex ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "builtins.h"
|
||||||
|
|
||||||
|
static uint8_t is_valid_identifier(const char *arg);
|
||||||
|
static uint8_t unset_one(char *arg, t_minishell *msh);
|
||||||
|
|
||||||
|
uint8_t builtin_unset(
|
||||||
|
t_command cmd,
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint8_t status;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
status = EXIT_SUCCESS;
|
||||||
|
i = 0;
|
||||||
|
while (cmd.argv[++i] != NULL)
|
||||||
|
if (unset_one(cmd.argv[i], msh) != EXIT_SUCCESS)
|
||||||
|
status = EXIT_FAILURE;
|
||||||
|
return (status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t is_valid_identifier(
|
||||||
|
const char *arg
|
||||||
|
)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (arg == NULL || *arg == '\0')
|
||||||
|
return (0);
|
||||||
|
if (!ft_isalpha(arg[0]) && arg[0] != '_')
|
||||||
|
return (0);
|
||||||
|
i = 0;
|
||||||
|
while (arg[++i] != '\0')
|
||||||
|
if (!ft_isalnum(arg[i]) && arg[i] != '_')
|
||||||
|
return (0);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t unset_one(
|
||||||
|
char *arg,
|
||||||
|
t_minishell *msh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!is_valid_identifier(arg))
|
||||||
|
return (ft_eprintf("minishell: unset: `%s': not a valid identifier\n",
|
||||||
|
arg), EXIT_FAILURE);
|
||||||
|
unset_env(arg, msh);
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
12
src/core/command/command.c
Normal file
12
src/core/command/command.c
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* command.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/09 18:40:03 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2026/02/09 18:40:04 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@@ -5,133 +5,226 @@
|
|||||||
/* +:+ +:+ +:+ */
|
/* +:+ +:+ +:+ */
|
||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/28 13:03:44 by sede-san #+# #+# */
|
/* Created: 2026/02/08 19:10:47 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/12/02 09:07:28 by sede-san ### ########.fr */
|
/* Updated: 2026/02/08 21:32:03 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "executor.h"
|
#include "executor.h"
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
|
|
||||||
static char *solve_path(char *cmd_name, t_minishell *msh);
|
static inline uint8_t execute_builtin(
|
||||||
static u_int8_t path_is_solved(char *cmd_name, t_minishell *msh);
|
const t_command *command,
|
||||||
static void handle_child(t_command *cmd, t_minishell *msh);
|
t_minishell *minishell
|
||||||
static void handle_parent(pid_t child_pid, t_command *cmd, t_minishell *msh);
|
)
|
||||||
|
{
|
||||||
|
const t_builtin_func builtin
|
||||||
|
= ft_hashmap_get(minishell->builtins, command->path);
|
||||||
|
|
||||||
u_int8_t execute(
|
return (builtin(*command, minishell));
|
||||||
t_command cmd,
|
|
||||||
t_minishell *msh
|
|
||||||
) {
|
|
||||||
pid_t child_pid;
|
|
||||||
|
|
||||||
cmd.path = solve_path(cmd.argv[0], msh);
|
|
||||||
if (!cmd.path)
|
|
||||||
{
|
|
||||||
ft_eprintf("minishell: %s: command not found\n", cmd.argv[0]);
|
|
||||||
return (msh->exit_status = 127, msh->exit_status);
|
|
||||||
}
|
|
||||||
if (!is_builtin(cmd.path, msh) && access(cmd.path, X_OK) != EXIT_SUCCESS)
|
|
||||||
{
|
|
||||||
ft_eputstr("minishell: ");
|
|
||||||
perror(cmd.path);
|
|
||||||
return (msh->exit_status = 126, msh->exit_status);
|
|
||||||
}
|
|
||||||
child_pid = 0;
|
|
||||||
if (!is_builtin(cmd.path, msh))
|
|
||||||
child_pid = fork();
|
|
||||||
if (child_pid == -1)
|
|
||||||
perror("minishell");
|
|
||||||
else if (child_pid == 0)
|
|
||||||
handle_child(&cmd, msh);
|
|
||||||
else
|
|
||||||
handle_parent(child_pid, &cmd, msh);
|
|
||||||
return (msh->exit_status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *solve_path(
|
static void handle_execve_error(
|
||||||
char *cmd_name,
|
const t_command *command,
|
||||||
t_minishell *msh
|
char **envp
|
||||||
){
|
)
|
||||||
char *cmd_path;
|
{
|
||||||
char **path;
|
free_envp(envp);
|
||||||
|
perror(command->path);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void execute_external_command(
|
||||||
|
const t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
char **envp;
|
||||||
|
|
||||||
|
envp = get_envp(minishell);
|
||||||
|
if (envp == NULL)
|
||||||
|
{
|
||||||
|
perror("get_envp");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
execve(command->path, command->argv, envp);
|
||||||
|
handle_execve_error(command, envp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t execute_command(
|
||||||
|
const t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (is_builtin(command->path, minishell))
|
||||||
|
return (execute_builtin(command, minishell));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
execute_external_command(command, minishell);
|
||||||
|
return (EXIT_FAILURE); //! should never reach here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmdfree_argv(
|
||||||
|
char **argv
|
||||||
|
)
|
||||||
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (path_is_solved(cmd_name, msh))
|
if (argv == NULL)
|
||||||
// return a copy to avoid double free on parent
|
return ;
|
||||||
return (ft_strdup(cmd_name));
|
i = 0;
|
||||||
path = ft_split(get_env("PATH", msh), COLON);
|
while (argv[i] != NULL)
|
||||||
if (!path)
|
|
||||||
return (NULL);
|
|
||||||
cmd_path = NULL;
|
|
||||||
i = -1;
|
|
||||||
while (!cmd_path && path[++i])
|
|
||||||
{
|
{
|
||||||
cmd_path = ft_strnjoin(3, path[i], "/", cmd_name);
|
free(argv[i]);
|
||||||
if (!cmd_path)
|
i++;
|
||||||
return (NULL);
|
|
||||||
/**
|
|
||||||
* If a command exists but user has no execution permission
|
|
||||||
* the command is shown as non existant instead of showing the
|
|
||||||
* last ocurrence found
|
|
||||||
*
|
|
||||||
* TLDR: bash shows 'Permission denied'
|
|
||||||
* and minishell 'command not found'
|
|
||||||
*
|
|
||||||
* TEST: execute an existing command without permission to do so
|
|
||||||
*/
|
|
||||||
if (access(cmd_path, X_OK) != EXIT_SUCCESS)
|
|
||||||
ft_free((void **)&cmd_path);
|
|
||||||
}
|
}
|
||||||
ft_free_split((char **)path);
|
free(argv);
|
||||||
if (!cmd_path)
|
|
||||||
return (NULL);
|
|
||||||
return (cmd_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static u_int8_t path_is_solved(
|
static void cmdfree(
|
||||||
char *cmd_name,
|
t_command *command
|
||||||
t_minishell *msh
|
)
|
||||||
){
|
{
|
||||||
return (ft_strncmp(cmd_name, "/", 1) == 0
|
if (command == NULL)
|
||||||
|| (cmd_name[1] && ft_strncmp(cmd_name, "./", 2) == 0)
|
return ;
|
||||||
|| (cmd_name[2] && ft_strncmp(cmd_name, "../", 3) == 0)
|
cmdfree_argv(command->argv);
|
||||||
|| is_builtin(cmd_name, msh)
|
free(command->path);
|
||||||
);
|
free(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_child(
|
static int create_pipe_if_needed(
|
||||||
t_command *cmd,
|
t_list *current_command,
|
||||||
t_minishell *msh
|
t_pipeline *pipeline
|
||||||
){
|
)
|
||||||
char **envp;
|
{
|
||||||
t_builtin_func builtin;
|
if (!current_command->next)
|
||||||
|
return (0);
|
||||||
|
if (pipe(pipeline->pipefd) == PIPE_ERROR)
|
||||||
|
return (perror("pipe"), PIPE_ERROR);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_builtin(cmd->argv[0], msh))
|
static bool is_fork_required(
|
||||||
|
t_list *current_command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
const t_command *command = current_command->content;
|
||||||
|
|
||||||
|
return (current_command->next != NULL
|
||||||
|
|| !is_builtin(command->path, minishell));
|
||||||
|
}
|
||||||
|
|
||||||
|
static pid_t fork_process(
|
||||||
|
t_list *current_command,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
pid = 0;
|
||||||
|
if (is_fork_required(current_command, minishell))
|
||||||
|
pid = fork();
|
||||||
|
if (pid == FORK_ERROR)
|
||||||
|
perror("fork");
|
||||||
|
return (pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setup_child_input(
|
||||||
|
t_pipeline *pipeline
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (pipeline->prev_read_fd != -1)
|
||||||
{
|
{
|
||||||
builtin = ft_hashmap_get(msh->builtins, cmd->argv[0]);
|
dup2(pipeline->prev_read_fd, STDIN_FILENO);
|
||||||
builtin(*cmd, msh);
|
close(pipeline->prev_read_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setup_child_output(
|
||||||
|
t_list *current_command,
|
||||||
|
t_pipeline *pipeline
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
dup2(pipeline->pipefd[WRITE_PIPE], STDOUT_FILENO);
|
||||||
|
close(pipeline->pipefd[READ_PIPE]);
|
||||||
|
close(pipeline->pipefd[WRITE_PIPE]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void child_process(
|
||||||
|
t_list *current_command,
|
||||||
|
t_pipeline *pipeline,
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint8_t exit_status;
|
||||||
|
const t_command *command = current_command->content;
|
||||||
|
|
||||||
|
setup_child_input(pipeline);
|
||||||
|
setup_child_output(current_command, pipeline);
|
||||||
|
exit_status = execute_command(command, minishell);
|
||||||
|
if (is_fork_required(current_command, minishell))
|
||||||
|
exit(exit_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parent_cleanup(
|
||||||
|
t_list *current_command,
|
||||||
|
t_pipeline *pipeline
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (pipeline->prev_read_fd != -1)
|
||||||
|
close(pipeline->prev_read_fd);
|
||||||
|
if (current_command->next)
|
||||||
|
{
|
||||||
|
close(pipeline->pipefd[WRITE_PIPE]);
|
||||||
|
pipeline->prev_read_fd = pipeline->pipefd[READ_PIPE];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
pipeline->prev_read_fd = -1;
|
||||||
envp = get_envp(msh);
|
|
||||||
execve(cmd->path, cmd->argv, envp);
|
|
||||||
free_envp(envp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_parent(
|
static uint8_t wait_for_children(void)
|
||||||
pid_t child_pid,
|
{
|
||||||
t_command *cmd,
|
uint8_t exit_status;
|
||||||
t_minishell *msh
|
int status;
|
||||||
){
|
|
||||||
if (waitpid(child_pid, (int *)&msh->exit_status, 0) == EXIT_SUCCESS)
|
exit_status = EXIT_SUCCESS;
|
||||||
|
while (wait(&status) > 0)
|
||||||
{
|
{
|
||||||
// handle success
|
if (WIFEXITED(status))
|
||||||
|
exit_status = WEXITSTATUS(status);
|
||||||
}
|
}
|
||||||
else
|
return (exit_status);
|
||||||
{
|
}
|
||||||
// handle error
|
|
||||||
}
|
uint8_t execute(
|
||||||
ft_free((void **)&cmd->path);
|
t_list *command_list,
|
||||||
ft_free_split(cmd->argv);
|
t_minishell *minishell
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint8_t exit_status;
|
||||||
|
t_pipeline pipeline;
|
||||||
|
t_list *current_command;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
pipeline.prev_read_fd = -1;
|
||||||
|
current_command = command_list;
|
||||||
|
while (current_command)
|
||||||
|
{
|
||||||
|
if (create_pipe_if_needed(current_command, &pipeline) == PIPE_ERROR)
|
||||||
|
break ;
|
||||||
|
pid = fork_process(current_command, minishell);
|
||||||
|
if (pid == FORK_ERROR)
|
||||||
|
break ;
|
||||||
|
if (pid == 0)
|
||||||
|
child_process(current_command, &pipeline, minishell);
|
||||||
|
parent_cleanup(current_command, &pipeline);
|
||||||
|
current_command = current_command->next;
|
||||||
|
}
|
||||||
|
exit_status = wait_for_children();
|
||||||
|
ft_lstclear(&command_list, (void (*)(void *))cmdfree);
|
||||||
|
return (exit_status);
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/main.c
16
src/main.c
@@ -3,14 +3,15 @@
|
|||||||
/* ::: :::::::: */
|
/* ::: :::::::: */
|
||||||
/* main.c :+: :+: :+: */
|
/* main.c :+: :+: :+: */
|
||||||
/* +:+ +:+ +:+ */
|
/* +:+ +:+ +:+ */
|
||||||
/* By: padan-pe <padan-pe@student.42.fr> +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/20 16:34:42 by sede-san #+# #+# */
|
/* Created: 2025/10/20 16:34:42 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/10/23 17:10:17 by padan-pe ### ########.fr */
|
/* Updated: 2026/02/09 18:46:21 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "minishell.h"
|
#include "minishell.h"
|
||||||
|
#include "core.h"
|
||||||
|
|
||||||
int main(
|
int main(
|
||||||
int argc,
|
int argc,
|
||||||
@@ -18,19 +19,14 @@ int main(
|
|||||||
char **envp
|
char **envp
|
||||||
){
|
){
|
||||||
t_minishell minishell;
|
t_minishell minishell;
|
||||||
u_int8_t exit_status;
|
|
||||||
|
|
||||||
if (argc != 1 || argv[1] || !envp)
|
if (argc != 1 || argv[1] || !envp)
|
||||||
{
|
{
|
||||||
printf("Usage: ./minishell\n");
|
printf("Usage: ./minishell\n");
|
||||||
return (EXIT_FAILURE);
|
return (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (!minishell_init(&minishell, envp))
|
minishell_init(&minishell, envp);
|
||||||
{
|
minishell_run(&minishell);
|
||||||
printf("Error: %s\n", "failed to initialize minishell");
|
|
||||||
return (EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
exit_status = minishell_run(&minishell);
|
|
||||||
minishell_clear(&minishell);
|
minishell_clear(&minishell);
|
||||||
return (exit_status);
|
return (minishell.exit_status);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,16 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/20 20:51:33 by sede-san #+# #+# */
|
/* Created: 2025/10/20 20:51:33 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/12/01 19:02:18 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 18:48:34 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "minishell.h"
|
#include "minishell.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "executor.h"
|
||||||
|
|
||||||
int minishell_init(
|
void minishell_init(
|
||||||
t_minishell *minishell,
|
t_minishell *minishell,
|
||||||
char **envp
|
char **envp
|
||||||
){
|
){
|
||||||
@@ -20,32 +23,34 @@ int minishell_init(
|
|||||||
set_envp(envp, minishell);
|
set_envp(envp, minishell);
|
||||||
set_builtins(minishell);
|
set_builtins(minishell);
|
||||||
if (minishell->variables.environment == NULL || minishell->builtins == NULL)
|
if (minishell->variables.environment == NULL || minishell->builtins == NULL)
|
||||||
{
|
|
||||||
minishell_clear(minishell);
|
minishell_clear(minishell);
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
return (1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u_int8_t minishell_run(
|
void minishell_run(
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
){
|
){
|
||||||
char *line;
|
char *line;
|
||||||
t_command command;
|
t_list *commands;
|
||||||
|
|
||||||
line = NULL;
|
if (minishell == NULL)
|
||||||
while (minishell->exit == 0)
|
|
||||||
{
|
{
|
||||||
line = readline("minishell > ");
|
minishell->exit_status = EXIT_FAILURE;
|
||||||
if (*line)
|
return ;
|
||||||
{
|
}
|
||||||
add_history(line);
|
while (!minishell->exit)
|
||||||
command = parse(line, minishell);
|
{
|
||||||
execute(command, minishell);
|
line = readline(DEFAULT_PS1);
|
||||||
}
|
if (line != NULL)
|
||||||
ft_free((void **)&line);
|
{
|
||||||
|
if (*line != '\0')
|
||||||
|
{
|
||||||
|
add_history(line);
|
||||||
|
commands = parse(line, minishell);
|
||||||
|
execute(commands, minishell);
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (minishell->exit_status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void minishell_clear(
|
void minishell_clear(
|
||||||
@@ -56,5 +61,4 @@ void minishell_clear(
|
|||||||
ft_hashmap_clear(&minishell->variables.environment, free);
|
ft_hashmap_clear(&minishell->variables.environment, free);
|
||||||
if (minishell->builtins != NULL)
|
if (minishell->builtins != NULL)
|
||||||
ft_hashmap_clear_keys(&minishell->builtins);
|
ft_hashmap_clear_keys(&minishell->builtins);
|
||||||
ft_bzero(minishell, sizeof(t_minishell));
|
|
||||||
}
|
}
|
||||||
|
|||||||
79
src/parser/lexer.c
Normal file
79
src/parser/lexer.c
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* lexer.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/09 18:56:41 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2026/02/09 20:42:50 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "core.h"
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
static t_token *tokenize(const char *line, size_t *start);
|
||||||
|
static t_token_type get_token_type(const char *str);
|
||||||
|
|
||||||
|
t_list *lex(
|
||||||
|
const char *line
|
||||||
|
) {
|
||||||
|
t_list *tokens;
|
||||||
|
t_token *token;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
tokens = NULL;
|
||||||
|
i = 0;
|
||||||
|
while (line[i] != '\0')
|
||||||
|
{
|
||||||
|
// ignore spaces
|
||||||
|
while (ft_isspace(line[i]))
|
||||||
|
i++;
|
||||||
|
// create token
|
||||||
|
token = tokenize(line, &i);
|
||||||
|
// add token to list
|
||||||
|
if (token != NULL)
|
||||||
|
ft_lstadd_back(&tokens, ft_lstnew(token));
|
||||||
|
}
|
||||||
|
return (tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
static t_token *tokenize(const char *line, size_t *start) {
|
||||||
|
t_token *token;
|
||||||
|
t_token_type type;
|
||||||
|
|
||||||
|
token = NULL;
|
||||||
|
if (line == NULL || line[*start] == '\0')
|
||||||
|
return (NULL);
|
||||||
|
type = get_token_type(line + *start);
|
||||||
|
(void)type;
|
||||||
|
// if (type != TOKEN_WORD)
|
||||||
|
// token = token_new(type, NULL);
|
||||||
|
// else
|
||||||
|
// token = read_word(line, start);
|
||||||
|
// if (token == NULL)
|
||||||
|
// (*start) += ft_strlen(token->value);
|
||||||
|
return (token);
|
||||||
|
}
|
||||||
|
|
||||||
|
static t_token_type get_token_type(const char *str)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
static const t_map_entry tokens[TOKENS_COUNT] = {
|
||||||
|
{PIPE_STR, (void *)TOKEN_PIPE},
|
||||||
|
{REDIRECT_IN_STR, (void *)TOKEN_REDIRECT_IN},
|
||||||
|
{REDIRECT_OUT_STR, (void *)TOKEN_REDIRECT_OUT},
|
||||||
|
{APPEND_STR, (void *)TOKEN_APPEND},
|
||||||
|
{HEREDOC_STR, (void *)TOKEN_HEREDOC}
|
||||||
|
};
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < TOKENS_COUNT)
|
||||||
|
{
|
||||||
|
if (ft_strcmp(str, tokens[i].key) == 0)
|
||||||
|
return ((t_token_type)tokens[i].value);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return (TOKEN_WORD);
|
||||||
|
}
|
||||||
@@ -6,73 +6,295 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/10/22 18:37:38 by sede-san #+# #+# */
|
/* Created: 2025/10/22 18:37:38 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/10/23 22:53:35 by sede-san ### ########.fr */
|
/* Updated: 2026/02/09 18:50:43 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "minishell.h"
|
#include "parser.h"
|
||||||
|
|
||||||
static char **expand_envs(char **argv);
|
// parse exclusive
|
||||||
static int count_argv(char **argv);
|
static char *extract_next_command(char *line, size_t *index);
|
||||||
|
static char *trim_whitespaces(char *line, size_t *start, size_t *end);
|
||||||
|
// static void set_pipes(t_list *commands);
|
||||||
|
|
||||||
char *parse(
|
// common
|
||||||
|
static void find_boundary(char *line, size_t *index, char bound_char);
|
||||||
|
|
||||||
|
// command exclusive
|
||||||
|
static t_command *cmdnew(char *line, t_minishell *minishell);
|
||||||
|
static void set_argv(t_command *command, char *line, t_minishell *minishell);
|
||||||
|
static void expand_envs(char *arg, t_minishell *minishell);
|
||||||
|
static char **lst_to_argv(t_list *argv_list);
|
||||||
|
static void set_argc(t_command *command);
|
||||||
|
// static void set_infile(t_command *command);
|
||||||
|
// static void set_outfile(t_command *command);
|
||||||
|
static void set_path(t_command *command, t_minishell *minishell);
|
||||||
|
static u_int8_t path_is_solved(char *cmd_name, t_minishell *msh);
|
||||||
|
static char *solve_path(char *command_name, t_minishell *minishell);
|
||||||
|
|
||||||
|
t_list *parse(
|
||||||
char *line,
|
char *line,
|
||||||
t_minishell *minishell
|
t_minishell *minishell
|
||||||
){
|
) {
|
||||||
t_command command;
|
t_list *commands;
|
||||||
|
// t_list *tokens;
|
||||||
|
t_command *command;
|
||||||
|
char *command_str;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
if (!line || !*line)
|
if (line == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
(void)minishell;
|
commands = NULL;
|
||||||
command.argv = expand_envs(ft_split(line, SPACE));
|
i = 0;
|
||||||
command.argc = count_argv(command.argv);
|
while (line[i] != '\0')
|
||||||
if (!command.argc)
|
{
|
||||||
return (NULL);
|
// tokens = tokenize();
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
command_str = extract_next_command(line, &i);
|
||||||
//* DEBUG
|
if (command_str != NULL)
|
||||||
// int i = -1;
|
{
|
||||||
// while (command.argv[++i])
|
command = cmdnew(command_str, minishell);
|
||||||
// printf("argv[%i]: %s\n", i, command.argv[i]);
|
free(command_str);
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
if (command != NULL)
|
||||||
|
ft_lstadd_back(&commands, ft_lstnew(command));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set_pipes(commands);
|
||||||
|
return (commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static void set_pipes(
|
||||||
|
// t_list *commands
|
||||||
|
// ) {
|
||||||
|
// t_list *current_command;
|
||||||
|
// t_list *previous_command;
|
||||||
|
// t_list *next_command;
|
||||||
|
// t_command *command;
|
||||||
|
|
||||||
|
// previous_command = NULL;
|
||||||
|
// current_command = commands;
|
||||||
|
// while (current_command != NULL)
|
||||||
|
// {
|
||||||
|
// command = (t_command *)current_command->content;
|
||||||
|
// if (previous_command != NULL)
|
||||||
|
// command->piped_from = (t_command *)previous_command->content;
|
||||||
|
// next_command = current_command->next;
|
||||||
|
// if (next_command != NULL)
|
||||||
|
// command->piped_to = (t_command *)next_command->content;
|
||||||
|
// previous_command = current_command;
|
||||||
|
// current_command = current_command->next;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
static char *extract_next_command(
|
||||||
|
char *line,
|
||||||
|
size_t *index
|
||||||
|
) {
|
||||||
|
char *command_str;
|
||||||
|
size_t start;
|
||||||
|
size_t end;
|
||||||
|
|
||||||
|
start = *index;
|
||||||
|
find_boundary(line, index, '|');
|
||||||
|
end = *index;
|
||||||
|
command_str = trim_whitespaces(line, &start, &end);
|
||||||
|
while (line[*index] == '|' || ft_isspace(line[*index]))
|
||||||
|
(*index)++;
|
||||||
|
return (command_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void find_boundary(
|
||||||
|
char *line,
|
||||||
|
size_t *index,
|
||||||
|
char bound_char
|
||||||
|
) {
|
||||||
|
bool in_single_quote;
|
||||||
|
bool in_double_quote;
|
||||||
|
|
||||||
|
in_single_quote = false;
|
||||||
|
in_double_quote = false;
|
||||||
|
while (line[*index] != '\0')
|
||||||
|
{
|
||||||
|
if (line[*index] == '\'' && !in_double_quote)
|
||||||
|
in_single_quote = !in_single_quote;
|
||||||
|
else if (line[*index] == '"' && !in_single_quote)
|
||||||
|
in_double_quote = !in_double_quote;
|
||||||
|
if (line[*index] == bound_char && !in_single_quote && !in_double_quote)
|
||||||
|
break ;
|
||||||
|
(*index)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *trim_whitespaces(
|
||||||
|
char *line,
|
||||||
|
size_t *start,
|
||||||
|
size_t *end
|
||||||
|
) {
|
||||||
|
while (*start < *end && ft_isspace(line[*start]))
|
||||||
|
(*start)++;
|
||||||
|
while (*end > *start && ft_isspace(line[*end - 1]))
|
||||||
|
(*end)--;
|
||||||
|
if (*end > *start)
|
||||||
|
return (ft_substr(line, *start, *end - *start));
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char **expand_envs(
|
static t_command *cmdnew(
|
||||||
char **argv
|
char *line,
|
||||||
){
|
t_minishell *minishell
|
||||||
int i;
|
) {
|
||||||
char *env;
|
t_command *command;
|
||||||
|
|
||||||
if (!argv)
|
command = (t_command *)ft_calloc(1, sizeof(t_command));
|
||||||
|
if (!command)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
else if (!*argv) // check if ft_split returned and empty matrix
|
// resolve_heredoc
|
||||||
|
set_argv(command, line, minishell);
|
||||||
|
if (!command->argv)
|
||||||
{
|
{
|
||||||
ft_free_split(argv);
|
free(command);
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
i = -1;
|
set_argc(command);
|
||||||
while (argv[++i])
|
// set_infile(command);
|
||||||
|
// set_outfile(command);
|
||||||
|
set_path(command, minishell);
|
||||||
|
return (command);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_argv(
|
||||||
|
t_command *command,
|
||||||
|
char *line,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
t_list *argv_list;
|
||||||
|
char *arg;
|
||||||
|
size_t i;
|
||||||
|
size_t start;
|
||||||
|
size_t end;
|
||||||
|
|
||||||
|
if (line == NULL)
|
||||||
|
return ;
|
||||||
|
i = 0;
|
||||||
|
argv_list = NULL;
|
||||||
|
while (line[i] != '\0')
|
||||||
{
|
{
|
||||||
if (!ft_strchr(argv[i], DOLLAR)
|
start = i;
|
||||||
|| (ft_strchr(argv[i], DOLLAR) && ft_strchr(argv[i], SINGLE_QUOTE) && ft_strchr(argv[i] + (ft_strchr(argv[i], SINGLE_QUOTE) + 1 - argv[i]), SINGLE_QUOTE))) // env is surrounded by single quote
|
find_boundary(line, &i, ' ');
|
||||||
continue ;
|
end = i;
|
||||||
env = getenv(ft_strchr(argv[i], DOLLAR) + 1);
|
arg = trim_whitespaces(line, &start, &end);
|
||||||
free(argv[i]);
|
expand_envs(arg, minishell);
|
||||||
if (env)
|
if (arg != NULL)
|
||||||
argv[i] = ft_strdup(env);
|
ft_lstadd_back(&argv_list, ft_lstnew(arg));
|
||||||
else
|
while (ft_isspace(line[i]))
|
||||||
argv[i] = ft_strdup("");
|
i++;
|
||||||
|
}
|
||||||
|
command->argv = lst_to_argv(argv_list);
|
||||||
|
ft_lstclear(&argv_list, free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void expand_envs(
|
||||||
|
char *arg,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
// TODO
|
||||||
|
(void)arg;
|
||||||
|
(void)minishell;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char **lst_to_argv(
|
||||||
|
t_list *argv_list
|
||||||
|
) {
|
||||||
|
char **argv;
|
||||||
|
t_list *current_arg;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
argv = (char **)ft_calloc(ft_lstsize(argv_list) + 1, sizeof(char *));
|
||||||
|
if (!argv)
|
||||||
|
return (NULL);
|
||||||
|
i = 0;
|
||||||
|
current_arg = argv_list;
|
||||||
|
while (current_arg != NULL)
|
||||||
|
{
|
||||||
|
argv[i] = ft_strdup((char *)current_arg->content);
|
||||||
|
i++;
|
||||||
|
current_arg = current_arg->next;
|
||||||
}
|
}
|
||||||
return (argv);
|
return (argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int count_argv(
|
static void set_argc(
|
||||||
char **argv
|
t_command *command
|
||||||
){
|
) {
|
||||||
int i;
|
int argc;
|
||||||
|
|
||||||
i = 0;
|
argc = 0;
|
||||||
while (argv[i])
|
while (command->argv[argc] != NULL)
|
||||||
i++;
|
argc++;
|
||||||
return (i);
|
command->argc = argc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static void set_infile(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// // test_infile
|
||||||
|
// command->infile = -1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static void set_outfile(
|
||||||
|
// t_command *command
|
||||||
|
// ) {
|
||||||
|
// // test_outfile
|
||||||
|
// command->outfile = STDOUT_FILENO;
|
||||||
|
// }
|
||||||
|
|
||||||
|
static void set_path(
|
||||||
|
t_command *command,
|
||||||
|
t_minishell *minishell
|
||||||
|
) {
|
||||||
|
char *command_path;
|
||||||
|
char *command_name;
|
||||||
|
|
||||||
|
command_name = command->argv[0];
|
||||||
|
if (!path_is_solved(command_name, minishell))
|
||||||
|
command_path = solve_path(command_name, minishell);
|
||||||
|
else
|
||||||
|
command_path = ft_strdup(command_name);
|
||||||
|
command->path = command_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *solve_path(
|
||||||
|
char *command_name,
|
||||||
|
t_minishell *minishell
|
||||||
|
){
|
||||||
|
char *command_path;
|
||||||
|
char **path_env;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
path_env = ft_split(get_env("PATH", minishell), ':');
|
||||||
|
if (!path_env)
|
||||||
|
return (NULL);
|
||||||
|
command_path = NULL;
|
||||||
|
i = -1;
|
||||||
|
while (!command_path && path_env[++i])
|
||||||
|
{
|
||||||
|
command_path = ft_strnjoin(3, path_env[i], "/", command_name);
|
||||||
|
if (command_path != NULL && access(command_path, F_OK) != EXIT_SUCCESS)
|
||||||
|
{
|
||||||
|
free(command_path);
|
||||||
|
command_path = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ft_free_split(path_env);
|
||||||
|
return (command_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u_int8_t path_is_solved(
|
||||||
|
char *command_name,
|
||||||
|
t_minishell *minishell
|
||||||
|
){
|
||||||
|
return (ft_strncmp(command_name, "/", 1) == 0
|
||||||
|
|| (command_name[1] && ft_strncmp(command_name, "./", 2) == 0)
|
||||||
|
|| (command_name[2] && ft_strncmp(command_name, "../", 3) == 0)
|
||||||
|
|| is_builtin(command_name, minishell)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/12/01 09:12:39 by sede-san #+# #+# */
|
/* Created: 2025/12/01 09:12:39 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/12/01 17:27:57 by sede-san ### ########.fr */
|
/* Updated: 2026/02/08 19:44:15 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "minishell.h"
|
#include "minishell.h"
|
||||||
|
#include "core.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Parses and stores environment variables from envp array into a hashmap
|
* @brief Parses and stores environment variables from envp array into a hashmap
|
||||||
@@ -118,7 +119,8 @@ char **get_envp(
|
|||||||
|
|
||||||
env_list = ft_hashmap_entries(msh->variables.environment);
|
env_list = ft_hashmap_entries(msh->variables.environment);
|
||||||
envp = (char **)malloc(
|
envp = (char **)malloc(
|
||||||
(msh->variables.environment->size + 1) * sizeof(char *));
|
(msh->variables.environment->size + 1) * sizeof(char *)
|
||||||
|
);
|
||||||
if (envp != NULL)
|
if (envp != NULL)
|
||||||
{
|
{
|
||||||
i = 0;
|
i = 0;
|
||||||
|
|||||||
41
src/variables/environment_unset.c
Normal file
41
src/variables/environment_unset.c
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* environment_unset.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2026/02/09 22:16:00 by codex #+# #+# */
|
||||||
|
/* Updated: 2026/02/09 22:16:00 by codex ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "minishell.h"
|
||||||
|
#include "core.h"
|
||||||
|
|
||||||
|
void unset_env(
|
||||||
|
const char *env_name,
|
||||||
|
t_minishell *msh
|
||||||
|
){
|
||||||
|
t_hashmap *new_env;
|
||||||
|
t_list *entries;
|
||||||
|
t_list *current;
|
||||||
|
t_map_entry *entry;
|
||||||
|
|
||||||
|
new_env = ft_hashmap_new(32, ft_hashmap_hashstr, ft_hashmap_strcmp);
|
||||||
|
if (new_env == NULL)
|
||||||
|
return ;
|
||||||
|
entries = ft_hashmap_entries(msh->variables.environment);
|
||||||
|
current = entries;
|
||||||
|
while (current != NULL)
|
||||||
|
{
|
||||||
|
entry = current->content;
|
||||||
|
if (ft_strcmp(entry->key, env_name) != 0)
|
||||||
|
ft_hashmap_put(new_env,
|
||||||
|
ft_strdup(entry->key), ft_strdup(entry->value));
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
ft_lstclear_nodes(&entries);
|
||||||
|
ft_hashmap_clear(&msh->variables.environment, free);
|
||||||
|
msh->variables.environment = new_env;
|
||||||
|
}
|
||||||
204
tests/builtins_edge_cases.sh
Executable file
204
tests/builtins_edge_cases.sh
Executable file
@@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd -- "$(dirname -- "$0")/.." && pwd)"
|
||||||
|
MSH_BIN="$ROOT_DIR/minishell"
|
||||||
|
TMP_DIR="$(mktemp -d /tmp/minishell-builtins-tests.XXXXXX)"
|
||||||
|
|
||||||
|
PASS_COUNT=0
|
||||||
|
FAIL_COUNT=0
|
||||||
|
CASE_OUT=""
|
||||||
|
CASE_ERR=""
|
||||||
|
CASE_EC=0
|
||||||
|
CASE_NAME=""
|
||||||
|
CASE_FAILED=0
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
pass() {
|
||||||
|
printf "PASS: %s\n" "$1"
|
||||||
|
PASS_COUNT=$((PASS_COUNT + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
printf "FAIL: %s\n" "$1"
|
||||||
|
FAIL_COUNT=$((FAIL_COUNT + 1))
|
||||||
|
CASE_FAILED=1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_exit_code() {
|
||||||
|
local expected="$1"
|
||||||
|
if [ "$CASE_EC" -ne "$expected" ]; then
|
||||||
|
fail "$CASE_NAME -> exit code esperado $expected, obtenido $CASE_EC"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_err_contains() {
|
||||||
|
local text="$1"
|
||||||
|
if ! grep -Fq -- "$text" "$CASE_ERR"; then
|
||||||
|
fail "$CASE_NAME -> stderr no contiene: $text"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_out_contains() {
|
||||||
|
local text="$1"
|
||||||
|
if ! grep -Fq -- "$text" "$CASE_OUT"; then
|
||||||
|
fail "$CASE_NAME -> stdout no contiene: $text"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_out_regex_count() {
|
||||||
|
local regex="$1"
|
||||||
|
local expected="$2"
|
||||||
|
local count
|
||||||
|
|
||||||
|
count="$(grep -Ec -- "$regex" "$CASE_OUT" || true)"
|
||||||
|
if [ "$count" -ne "$expected" ]; then
|
||||||
|
fail "$CASE_NAME -> regex [$regex] esperado $expected, obtenido $count"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_case() {
|
||||||
|
local name="$1"
|
||||||
|
local input="$2"
|
||||||
|
local expected_exit="$3"
|
||||||
|
local clean_env="${4:-0}"
|
||||||
|
|
||||||
|
CASE_NAME="$name"
|
||||||
|
CASE_OUT="$TMP_DIR/$name.out"
|
||||||
|
CASE_ERR="$TMP_DIR/$name.err"
|
||||||
|
CASE_EC=0
|
||||||
|
CASE_FAILED=0
|
||||||
|
|
||||||
|
if [ "$clean_env" -eq 1 ]; then
|
||||||
|
printf "%b" "$input" | env -i PATH="$PATH" "$MSH_BIN" >"$CASE_OUT" 2>"$CASE_ERR"
|
||||||
|
else
|
||||||
|
printf "%b" "$input" | "$MSH_BIN" >"$CASE_OUT" 2>"$CASE_ERR"
|
||||||
|
fi
|
||||||
|
CASE_EC=$?
|
||||||
|
assert_exit_code "$expected_exit"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_pwd_deleted_dir_case() {
|
||||||
|
local tmpd
|
||||||
|
|
||||||
|
tmpd="$TMP_DIR/pwd_deleted_dir"
|
||||||
|
mkdir -p "$tmpd"
|
||||||
|
run_case "pwd_deleted_dir" \
|
||||||
|
"cd $tmpd\n/bin/rmdir $tmpd\npwd\nexit\n" \
|
||||||
|
1
|
||||||
|
assert_err_contains "minishell: pwd:"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
if [ ! -x "$MSH_BIN" ]; then
|
||||||
|
printf "Compilando minishell...\n"
|
||||||
|
if ! make -C "$ROOT_DIR" >/dev/null; then
|
||||||
|
printf "No se pudo compilar minishell\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "exit_non_numeric" "exit abc\n" 2
|
||||||
|
assert_err_contains "numeric argument required"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "exit_sign_only" "exit +\n" 2
|
||||||
|
assert_err_contains "numeric argument required"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "exit_negative_wrap" "exit -1\n" 255
|
||||||
|
|
||||||
|
run_case "exit_too_many_args" "exit 7 8\npwd\nexit\n" 0
|
||||||
|
assert_err_contains "too many arguments"
|
||||||
|
assert_out_contains "$ROOT_DIR"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "cd_home_not_set" "cd\nexit\n" 1 1
|
||||||
|
assert_err_contains "HOME not set"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "cd_invalid_path" "cd /definitely/not/here\nexit\n" 1
|
||||||
|
assert_err_contains "No such file or directory"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "cd_dash_roundtrip" "cd /tmp\ncd -\npwd\nexit\n" 0
|
||||||
|
assert_out_contains "$ROOT_DIR"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_pwd_deleted_dir_case
|
||||||
|
|
||||||
|
run_case "echo_flags" "echo -nn hi\necho hi-there\nexit\n" 0
|
||||||
|
assert_out_contains "himinishell >"
|
||||||
|
assert_out_contains "hi-there"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "env_builtin_with_arg" "env EXTRA\nexit\n" 1
|
||||||
|
assert_err_contains "too many arguments"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "export_unset_cycle" \
|
||||||
|
"export FOO=bar\n/usr/bin/env\nunset FOO\n/usr/bin/env\nexit\n" \
|
||||||
|
0
|
||||||
|
assert_out_regex_count "^FOO=bar$" 1
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "env_builtin_lists_exported" \
|
||||||
|
"export FOO=bar\nenv\nunset FOO\nenv\nexit\n" \
|
||||||
|
0
|
||||||
|
assert_out_regex_count "^FOO=bar$" 1
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "export_invalid_identifier" "export 1A=2\nexit\n" 1
|
||||||
|
assert_err_contains "not a valid identifier"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "unset_invalid_identifier" "unset 1A\nexit\n" 1
|
||||||
|
assert_err_contains "not a valid identifier"
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_case "export_without_value" "unset ZZZ\nexport ZZZ\n/usr/bin/env\nexit\n" 0
|
||||||
|
assert_out_regex_count "^ZZZ=$" 1
|
||||||
|
if [ "$CASE_FAILED" -eq 0 ]; then
|
||||||
|
pass "$CASE_NAME assertions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "\nResumen tests builtins: PASS=%d FAIL=%d\n" "$PASS_COUNT" "$FAIL_COUNT"
|
||||||
|
if [ "$FAIL_COUNT" -ne 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user