# 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-\.