update: functions done: echo + echo -n, pwd, exit with and without arguments / parser appears to work, test with commands and local files (only tested with ls -l)
This commit is contained in:
24
Makefile
24
Makefile
@@ -6,7 +6,7 @@
|
|||||||
# By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ #
|
# By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ #
|
||||||
# +#+#+#+#+#+ +#+ #
|
# +#+#+#+#+#+ +#+ #
|
||||||
# Created: 2025/07/30 20:22:21 by sede-san #+# #+# #
|
# Created: 2025/07/30 20:22:21 by sede-san #+# #+# #
|
||||||
# Updated: 2025/07/31 05:23:15 by sede-san ### ########.fr #
|
# Updated: 2025/08/03 23:38:53 by sede-san ### ########.fr #
|
||||||
# #
|
# #
|
||||||
# **************************************************************************** #
|
# **************************************************************************** #
|
||||||
|
|
||||||
@@ -58,7 +58,17 @@ SRC_PATH = src
|
|||||||
|
|
||||||
# Source files
|
# Source files
|
||||||
SRC = \
|
SRC = \
|
||||||
$(SRC_PATH)/minishell.c
|
$(SRC_PATH)/minishell.c \
|
||||||
|
$(SRC_PATH)/builtins/cd.c \
|
||||||
|
$(SRC_PATH)/builtins/echo.c \
|
||||||
|
$(SRC_PATH)/builtins/env.c \
|
||||||
|
$(SRC_PATH)/builtins/exit.c \
|
||||||
|
$(SRC_PATH)/builtins/export.c \
|
||||||
|
$(SRC_PATH)/builtins/pwd.c \
|
||||||
|
$(SRC_PATH)/builtins/unset.c \
|
||||||
|
$(SRC_PATH)/commands/exec.c \
|
||||||
|
$(SRC_PATH)/commands/parse.c \
|
||||||
|
$(SRC_PATH)/utils/get_hostname.c
|
||||||
|
|
||||||
# Include path
|
# Include path
|
||||||
INCLUDE_PATH = ./include
|
INCLUDE_PATH = ./include
|
||||||
@@ -109,6 +119,16 @@ fclean: clean
|
|||||||
@echo "$(GREEN)$(EMOJI_CHECK) Binaries cleaned.$(RESET)"
|
@echo "$(GREEN)$(EMOJI_CHECK) Binaries cleaned.$(RESET)"
|
||||||
.PHONY: fclean
|
.PHONY: fclean
|
||||||
|
|
||||||
|
update: fclean
|
||||||
|
@echo "$(RED)$(EMOJI_BROOM) Removing libraries...$(RESET)"
|
||||||
|
@rm -rf $(LIB_PATH)
|
||||||
|
@echo "$(GREEN)$(EMOJI_CHECK) Libraries removed.$(RESET)"
|
||||||
|
@echo "$(YELLOW)$(EMOJI_WRENCH) Fetching updates...$(RESET)"
|
||||||
|
@git pull
|
||||||
|
@echo "$(GREEN)$(EMOJI_CHECK) Updated.$(RESET)"
|
||||||
|
@echo "$(YELLOW)$(EMOJI_WRENCH) Recompiling...$(RESET)"
|
||||||
|
@$(MAKE)
|
||||||
|
|
||||||
# Recompile
|
# Recompile
|
||||||
re: fclean all
|
re: fclean all
|
||||||
.PHONY: re
|
.PHONY: re
|
||||||
|
|||||||
60
include/builtins.h
Normal file
60
include/builtins.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* builtins.h :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/08/01 17:18:21 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2025/08/02 17:55:22 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#ifndef BUILTINS_H
|
||||||
|
# define BUILTINS_H
|
||||||
|
|
||||||
|
# include <string.h>
|
||||||
|
# include <stdio.h>
|
||||||
|
|
||||||
|
typedef int (*t_builtin_func)(int argc, char const *argv[]);
|
||||||
|
|
||||||
|
typedef struct s_builtin
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
t_builtin_func function;
|
||||||
|
} t_builtin;
|
||||||
|
|
||||||
|
// cd.c
|
||||||
|
|
||||||
|
int cd_builtin(int argc, char const *argv[]);
|
||||||
|
|
||||||
|
// echo.c
|
||||||
|
|
||||||
|
typedef struct s_echo_flags
|
||||||
|
{
|
||||||
|
int newline; // print newline after everything has been printed
|
||||||
|
} t_echo_flags;
|
||||||
|
|
||||||
|
int echo_builtin(int argc, char const *argv[]);
|
||||||
|
|
||||||
|
// env.c
|
||||||
|
|
||||||
|
int env_builtin(int argc, char const *argv[]);
|
||||||
|
|
||||||
|
// exit.c
|
||||||
|
|
||||||
|
int exit_builtin(int argc, char const *argv[]);
|
||||||
|
|
||||||
|
// export.c
|
||||||
|
|
||||||
|
int export_builtin(int argc, char const *argv[]);
|
||||||
|
|
||||||
|
// pwd.c
|
||||||
|
|
||||||
|
int pwd_builtin(int argc, char const *argv[]);
|
||||||
|
|
||||||
|
// unset.c
|
||||||
|
|
||||||
|
int unset_builtin(int argc, char const *argv[]);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -6,33 +6,52 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/07/31 03:42:51 by sede-san #+# #+# */
|
/* Created: 2025/07/31 03:42:51 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/07/31 14:01:36 by sede-san ### ########.fr */
|
/* Updated: 2025/08/03 22:56:35 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#ifndef MINISHELL_H
|
#ifndef MINISHELL_H
|
||||||
# define MINISHELL_H
|
# define MINISHELL_H
|
||||||
|
|
||||||
|
# include "builtins.h"
|
||||||
# include "libft.h"
|
# include "libft.h"
|
||||||
# include "get_next_line.h"
|
# include "get_next_line.h"
|
||||||
|
# include <fcntl.h>
|
||||||
# include <signal.h>
|
# include <signal.h>
|
||||||
# include <readline/history.h>
|
# include <readline/history.h>
|
||||||
# include <readline/readline.h>
|
# include <readline/readline.h>
|
||||||
# include <sys/wait.h>
|
# include <sys/wait.h>
|
||||||
|
|
||||||
# define HISTORY_FILE "/.minishell_history"
|
typedef struct s_cmd
|
||||||
|
|
||||||
typedef struct s_env
|
|
||||||
{
|
{
|
||||||
char *home;
|
int argc;
|
||||||
char **path;
|
char **argv;
|
||||||
} t_env;
|
char **envp;
|
||||||
|
} t_cmd;
|
||||||
|
|
||||||
|
# define HISTORY_FILE "/.minishell_history"
|
||||||
|
|
||||||
typedef struct s_minishell
|
typedef struct s_minishell
|
||||||
{
|
{
|
||||||
char *prompt;
|
|
||||||
char *history_file;
|
char *history_file;
|
||||||
t_env env;
|
char **envp;
|
||||||
} t_minishell;
|
} t_minishell;
|
||||||
|
|
||||||
|
/* ******************************* Commands ********************************* */
|
||||||
|
|
||||||
|
typedef int (*t_msh_cmdfunc)(char **args, char **env, t_minishell* minshell);
|
||||||
|
|
||||||
|
// exec.c
|
||||||
|
|
||||||
|
int exec_cmd(char *cmd, char **args, t_minishell *minishell);
|
||||||
|
|
||||||
|
// parse.c
|
||||||
|
|
||||||
|
t_cmd parse_cmd(char *line, t_minishell *minishell);
|
||||||
|
|
||||||
|
/* ********************************* Utils ********************************** */
|
||||||
|
|
||||||
|
// get_hostname.c
|
||||||
|
char *get_hostname(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
28
src/builtins/cd.c
Normal file
28
src/builtins/cd.c
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* cd.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/08/01 11:09:13 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2025/08/03 02:11:18 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
int cd_builtin(
|
||||||
|
int argc,
|
||||||
|
char const *argv[])
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
if (argc == 1)
|
||||||
|
path = getenv("HOME");
|
||||||
|
else
|
||||||
|
path = (char *)argv[1];
|
||||||
|
if (chdir(path) != 0)
|
||||||
|
return (fprintf(stderr, "cd: %s\n", path), 1);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
106
src/builtins/echo.c
Normal file
106
src/builtins/echo.c
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* echo.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/08/01 13:22:11 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2025/08/03 02:15:38 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static t_echo_flags set_flags(char const *argv[], size_t *i);
|
||||||
|
static void set_default_flags(t_echo_flags *flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Executes the echo builtin command.
|
||||||
|
*
|
||||||
|
* This function processes the arguments passed to the echo command,
|
||||||
|
* handles any flags (such as -n for no trailing newline), and prints
|
||||||
|
* the arguments to standard output separated by spaces.
|
||||||
|
*
|
||||||
|
* @param argc The number of arguments in argv.
|
||||||
|
* @param argv The array of argument strings, with argv[0] being "echo".
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS on successful execution.
|
||||||
|
*/
|
||||||
|
int echo_builtin(
|
||||||
|
int argc,
|
||||||
|
char const *argv[])
|
||||||
|
{
|
||||||
|
t_echo_flags flags;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
(void)argc;
|
||||||
|
flags = set_flags(argv, &i);
|
||||||
|
while (argv[i])
|
||||||
|
{
|
||||||
|
ft_putstr((char *)argv[i]);
|
||||||
|
if (argv[++i])
|
||||||
|
ft_putchar(' ');
|
||||||
|
}
|
||||||
|
if (flags.newline)
|
||||||
|
ft_putchar('\n');
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parses command-line arguments to set flags for the echo builtin.
|
||||||
|
*
|
||||||
|
* This function examines the arguments passed to the echo command, starting
|
||||||
|
* from argv[1], and sets the appropriate flags based on the presence of
|
||||||
|
* options (e.g., "-n"). It updates the index `i` to point to the first
|
||||||
|
* non-option argument.
|
||||||
|
*
|
||||||
|
* @param argv Array of argument strings (NULL-terminated).
|
||||||
|
* @param i Pointer to a size_t variable that will be updated to the index
|
||||||
|
* of the first non-option argument.
|
||||||
|
*
|
||||||
|
* @return t_echo_flags Structure containing the parsed flags (e.g., whether
|
||||||
|
* to print a trailing newline).
|
||||||
|
*/
|
||||||
|
static t_echo_flags set_flags(
|
||||||
|
char const *argv[],
|
||||||
|
size_t *i
|
||||||
|
)
|
||||||
|
{
|
||||||
|
t_echo_flags flags;
|
||||||
|
size_t j;
|
||||||
|
|
||||||
|
set_default_flags(&flags);
|
||||||
|
*i = 1;
|
||||||
|
while (*argv[*i] == '-')
|
||||||
|
{
|
||||||
|
j = 1;
|
||||||
|
if (!argv[*i][j])
|
||||||
|
return (flags);
|
||||||
|
while (argv[*i][j])
|
||||||
|
{
|
||||||
|
if (argv[*i][j] == 'n')
|
||||||
|
flags.newline = 0;
|
||||||
|
else
|
||||||
|
return (set_default_flags(&flags), flags);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
(*i)++;
|
||||||
|
}
|
||||||
|
return (flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes the t_echo_flags structure with default values.
|
||||||
|
*
|
||||||
|
* This function zeroes out all fields of the given t_echo_flags structure
|
||||||
|
* and sets the 'newline' flag to 1, indicating that a newline should be
|
||||||
|
* printed by default.
|
||||||
|
*
|
||||||
|
* @param flags Pointer to the t_echo_flags structure to initialize.
|
||||||
|
*/
|
||||||
|
static void set_default_flags(t_echo_flags *flags)
|
||||||
|
{
|
||||||
|
ft_bzero(flags, sizeof(t_echo_flags));
|
||||||
|
flags->newline = 1;
|
||||||
|
}
|
||||||
36
src/builtins/env.c
Normal file
36
src/builtins/env.c
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* env.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/08/01 14:15:09 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2025/08/03 02:33:30 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
// static void printenv(char **envp);
|
||||||
|
|
||||||
|
int env_builtin(
|
||||||
|
int argc,
|
||||||
|
char const *argv[])
|
||||||
|
{
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
// if (!argv || !*argv)
|
||||||
|
// printenv(envp);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static void printenv(
|
||||||
|
// char **envp)
|
||||||
|
// {
|
||||||
|
// size_t i;
|
||||||
|
|
||||||
|
// i = 0;
|
||||||
|
// while (envp[i])
|
||||||
|
// ft_putendl(envp[i++]);
|
||||||
|
// }
|
||||||
62
src/builtins/exit.c
Normal file
62
src/builtins/exit.c
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* exit.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/08/01 14:38:18 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2025/08/03 02:11:33 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handles the shell's exit builtin command.
|
||||||
|
*
|
||||||
|
* Prints "exit" to standard output and determines the exit code based on the
|
||||||
|
* arguments provided.
|
||||||
|
*
|
||||||
|
* @param argc The number of arguments passed to the exit command.
|
||||||
|
* @param argv The array of argument strings.
|
||||||
|
* @return The exit code to be used by the shell.
|
||||||
|
*
|
||||||
|
* Behavior:
|
||||||
|
* - If no additional arguments are provided (argc == 1), exits with code 0.
|
||||||
|
* - If more than one argument is provided:
|
||||||
|
* - If the first argument is not a valid numeric string, prints an error
|
||||||
|
* message to stderr and exits with error code 2.
|
||||||
|
* - If the first argument is valid but there are too many arguments,
|
||||||
|
* prints an error message to stderr and returns -1. In this case the
|
||||||
|
* shell must not exit.
|
||||||
|
* - Otherwise, parses the first argument as a long integer and casts it to an
|
||||||
|
* unsigned char, then to an unsigned int to ensure the exit code is within
|
||||||
|
* the range 0-255.
|
||||||
|
*/
|
||||||
|
int exit_builtin(
|
||||||
|
int argc,
|
||||||
|
char const *argv[])
|
||||||
|
{
|
||||||
|
int exit_code;
|
||||||
|
|
||||||
|
ft_putendl("exit");
|
||||||
|
if (argc == 1)
|
||||||
|
exit_code = 0;
|
||||||
|
else if (argc > 1)
|
||||||
|
{
|
||||||
|
if (!ft_isdigit(*argv[1]) || !ft_strisnum(argv[1]))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "exit: %s: numeric argument required", argv[1]);
|
||||||
|
exit_code = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "exit: too many arguments");
|
||||||
|
exit_code = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
exit_code = (unsigned int)((unsigned char)(ft_atol(argv[1])));
|
||||||
|
return (exit_code);
|
||||||
|
}
|
||||||
0
src/builtins/export.c
Normal file
0
src/builtins/export.c
Normal file
27
src/builtins/pwd.c
Normal file
27
src/builtins/pwd.c
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* pwd.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/08/01 11:52:33 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2025/08/03 02:11:02 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
int pwd_builtin(
|
||||||
|
int argc,
|
||||||
|
char const *argv[])
|
||||||
|
{
|
||||||
|
char cwd[PATH_MAX];
|
||||||
|
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
if (!getcwd(cwd, sizeof(cwd)))
|
||||||
|
ft_eputendl("getcwd()");
|
||||||
|
ft_putendl(cwd);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
0
src/builtins/unset.c
Normal file
0
src/builtins/unset.c
Normal file
20
src/commands/exec.c
Normal file
20
src/commands/exec.c
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* exec.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/08/01 16:24:47 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2025/08/03 23:39:07 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
// int exec_cmd(
|
||||||
|
// t_cmd cmd,
|
||||||
|
// t_minishell *minishell)
|
||||||
|
// {
|
||||||
|
|
||||||
|
// }
|
||||||
120
src/commands/parse.c
Normal file
120
src/commands/parse.c
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* parse.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/08/01 16:40:35 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2025/08/04 00:28:53 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
static int is_local(char const *cmd);
|
||||||
|
static int is_builtin(char const *cmd);
|
||||||
|
static char *resolve_cmd_path(char const *cmd, char const *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parses a command line string into a t_cmd structure.
|
||||||
|
*
|
||||||
|
* This function takes a command line string and a pointer to the minishell
|
||||||
|
* context, splits the line into arguments, and fills a t_cmd structure with
|
||||||
|
* the command path, argument vector, and argument count.
|
||||||
|
*
|
||||||
|
* @param line The input command line string to parse.
|
||||||
|
* @param minishell Pointer to the minishell context.
|
||||||
|
*
|
||||||
|
* @return t_cmd The parsed command structure containing the command path,
|
||||||
|
* argument vector, and argument count.
|
||||||
|
*
|
||||||
|
* @note The string array cmd.argv must be freed after the command has been
|
||||||
|
* executed.
|
||||||
|
*/
|
||||||
|
t_cmd parse_cmd(
|
||||||
|
char *line,
|
||||||
|
t_minishell *minishell)
|
||||||
|
{
|
||||||
|
t_cmd cmd;
|
||||||
|
char *abs_path;
|
||||||
|
|
||||||
|
(void)minishell;
|
||||||
|
ft_bzero(&cmd, sizeof(t_cmd));
|
||||||
|
cmd.argv = ft_split(line, ' ');
|
||||||
|
// skip check for the moment
|
||||||
|
cmd.argc = 0;
|
||||||
|
while (cmd.argv[cmd.argc])
|
||||||
|
cmd.argc++;
|
||||||
|
if (!is_local(cmd.argv[0]) && !is_builtin(cmd.argv[0]))
|
||||||
|
{
|
||||||
|
abs_path = resolve_cmd_path(cmd.argv[0], getenv("PATH"));
|
||||||
|
if (abs_path)
|
||||||
|
{
|
||||||
|
free(cmd.argv[0]);
|
||||||
|
cmd.argv[0] = abs_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the given command string refers to a local executable.
|
||||||
|
*
|
||||||
|
* This function determines whether the provided command string starts with
|
||||||
|
* "./", which is a common convention for executing local files in Unix-like
|
||||||
|
* systems.
|
||||||
|
*
|
||||||
|
* @param cmd The command string to check.
|
||||||
|
*
|
||||||
|
* @return 1 if the command starts with "./", 0 otherwise.
|
||||||
|
*/
|
||||||
|
static int is_local(
|
||||||
|
char const *cmd)
|
||||||
|
{
|
||||||
|
return (cmd[0] == '.' && cmd[1] == '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the given command is a shell builtin.
|
||||||
|
*
|
||||||
|
* This function compares the input command string against a list of known
|
||||||
|
* shell builtin commands ("cd", "echo", "env", "exit", "export", "pwd",
|
||||||
|
* "unset"). It returns a non-zero value if the command matches any of these
|
||||||
|
* builtins, otherwise returns 0.
|
||||||
|
*
|
||||||
|
* @param cmd The command string to check.
|
||||||
|
*
|
||||||
|
* @return int Non-zero if the command is a builtin, 0 otherwise.
|
||||||
|
*/
|
||||||
|
static int is_builtin(
|
||||||
|
char const *cmd)
|
||||||
|
{
|
||||||
|
return (!ft_strncmp(cmd, "cd\0", 3)
|
||||||
|
|| !ft_strncmp(cmd, "echo\0", 5)
|
||||||
|
|| !ft_strncmp(cmd, "env\0", 4)
|
||||||
|
|| !ft_strncmp(cmd, "exit\0", 5)
|
||||||
|
|| !ft_strncmp(cmd, "export\0", 7)
|
||||||
|
|| !ft_strncmp(cmd, "pwd\0", 4)
|
||||||
|
|| !ft_strncmp(cmd, "unset\0", 6));
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *resolve_cmd_path(char const *cmd, char const *path)
|
||||||
|
{
|
||||||
|
char *abs_path;
|
||||||
|
char const **path_splitted = (char const **)ft_split(path, ':');
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
abs_path = NULL;
|
||||||
|
i = 0;
|
||||||
|
while (path_splitted[i])
|
||||||
|
{
|
||||||
|
abs_path = ft_strjoin_mul(
|
||||||
|
3, (char *)path_splitted[i], "/", (char *)cmd);
|
||||||
|
if (access(abs_path, F_OK) != -1)
|
||||||
|
break ;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
ft_free_split((char **)path_splitted);
|
||||||
|
return (abs_path);
|
||||||
|
}
|
||||||
196
src/minishell.c
196
src/minishell.c
@@ -6,69 +6,151 @@
|
|||||||
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2025/07/31 03:42:25 by sede-san #+# #+# */
|
/* Created: 2025/07/31 03:42:25 by sede-san #+# #+# */
|
||||||
/* Updated: 2025/07/31 14:43:43 by sede-san ### ########.fr */
|
/* Updated: 2025/08/04 00:29:45 by sede-san ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
#include "minishell.h"
|
#include "minishell.h"
|
||||||
|
|
||||||
static t_minishell init_minishell(void);
|
static t_minishell init_minishell(char **envp);
|
||||||
|
static void minishell(t_minishell *minishell, char **envp);
|
||||||
|
static void clear_minishell(t_minishell *minishell);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Entry point for the minishell program.
|
||||||
|
*
|
||||||
|
* Validates the number of command-line arguments, initializes the minishell
|
||||||
|
* structure, starts the main shell loop, and performs necessary cleanup before
|
||||||
|
* exiting.
|
||||||
|
*
|
||||||
|
* @param argc Number of command-line arguments.
|
||||||
|
* @param argv Array of command-line argument strings.
|
||||||
|
* @param envp Array of environment variable strings.
|
||||||
|
*
|
||||||
|
* @return int Returns EXIT_SUCCESS if the program completes successfully, or
|
||||||
|
* an error code on failure.
|
||||||
|
*/
|
||||||
int main(
|
int main(
|
||||||
int argc,
|
int argc,
|
||||||
char const *argv[],
|
char const *argv[],
|
||||||
char **envp
|
char **envp)
|
||||||
)
|
{
|
||||||
|
t_minishell shell;
|
||||||
|
|
||||||
|
(void)argv;
|
||||||
|
errno = 0;
|
||||||
|
if (argc != 1)
|
||||||
|
return (errno = EINVAL, errno);
|
||||||
|
shell = init_minishell(envp);
|
||||||
|
minishell(&shell, envp);
|
||||||
|
clear_minishell(&shell);
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes and returns a t_minishell structure with default values.
|
||||||
|
*
|
||||||
|
* This function zeroes out a t_minishell struct, sets up the history file path
|
||||||
|
* by joining the HOME environment variable with HISTORY_FILE, and splits the
|
||||||
|
* PATH environment variable into an array of strings using ':' as the
|
||||||
|
* delimiter.
|
||||||
|
*
|
||||||
|
* @return t_minishell The initialized minishell structure.
|
||||||
|
*/
|
||||||
|
static t_minishell init_minishell(
|
||||||
|
char **envp)
|
||||||
{
|
{
|
||||||
t_minishell minishell;
|
t_minishell minishell;
|
||||||
|
|
||||||
|
ft_bzero(&minishell, sizeof(t_minishell));
|
||||||
|
minishell.history_file = ft_strjoin(getenv("HOME"), HISTORY_FILE);
|
||||||
|
// minishell.path = ft_split(getenv("PATH"), ':');
|
||||||
|
minishell.envp = envp;
|
||||||
|
return (minishell);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Main loop for the minishell program.
|
||||||
|
*
|
||||||
|
* This function implements the main interactive loop of the minishell. It
|
||||||
|
* displays a dynamic prompt showing the user, hostname, and current working
|
||||||
|
* directory, reads user input, adds commands to the history, and executes
|
||||||
|
* commands by forking a new process. The loop continues until the user enters
|
||||||
|
* "exit" or EOF is encountered.
|
||||||
|
*
|
||||||
|
* @param minishell Pointer to the minishell state structure, containing at
|
||||||
|
* least the history file path.
|
||||||
|
* @param envp Environment variables to be passed to executed commands.
|
||||||
|
*
|
||||||
|
* The function performs the following steps:
|
||||||
|
* - Loads command history from the specified history file.
|
||||||
|
* - Constructs and displays a prompt with user, hostname, and current
|
||||||
|
* directory.
|
||||||
|
* - Reads a line of input from the user.
|
||||||
|
* - If the input is not empty:
|
||||||
|
* - Adds the command to the history and writes it to the history file.
|
||||||
|
* - If the command is "exit", breaks the loop and exits.
|
||||||
|
* - Otherwise, constructs the command path, forks a child process, and
|
||||||
|
* executes the command using execve.
|
||||||
|
* - Waits for the child process to finish.
|
||||||
|
* - Cleans up allocated memory and clears the history on exit.
|
||||||
|
*/
|
||||||
|
static void minishell(t_minishell *minishell, char **envp)
|
||||||
|
{
|
||||||
char *line;
|
char *line;
|
||||||
|
char *cmd;
|
||||||
|
char *args[2];
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
minishell = init_minishell();
|
|
||||||
line = NULL;
|
line = NULL;
|
||||||
(void)argv;
|
read_history(minishell->history_file);
|
||||||
(void)envp;
|
|
||||||
if (argc != 1)
|
|
||||||
return (EXIT_FAILURE);
|
|
||||||
|
|
||||||
/* ====================================================================== */
|
|
||||||
|
|
||||||
char *cmd = "/usr/bin/whoami";
|
|
||||||
char *args[] = {"whoami", NULL};
|
|
||||||
__pid_t pid = fork(); // Creates a "secondary" process
|
|
||||||
if (pid == 0)
|
|
||||||
{
|
|
||||||
execve(cmd, args, envp);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
waitpid(pid, NULL, 0); // Makes the "parent" process wait until the "child"
|
|
||||||
// has finished
|
|
||||||
|
|
||||||
/* ====================================================================== */
|
|
||||||
|
|
||||||
cmd = "/usr/bin/hostname";
|
|
||||||
args[0] = "hostname";
|
|
||||||
args[1] = NULL;
|
|
||||||
pid = fork();
|
|
||||||
if (pid == 0)
|
|
||||||
{
|
|
||||||
execve(cmd, args, envp);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
waitpid(pid, NULL, 0);
|
|
||||||
read_history(minishell.history_file);
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (line)
|
if (line)
|
||||||
free(line);
|
free(line);
|
||||||
line = readline(minishell.prompt);
|
char *prompt;
|
||||||
if (!line)
|
char *hostname = get_hostname();
|
||||||
|
if (hostname)
|
||||||
|
{
|
||||||
|
prompt = ft_strjoin_mul(12,
|
||||||
|
BOLD, BLUE_TEXT, getenv("USER"), "@", hostname,
|
||||||
|
RESET, ":",
|
||||||
|
BOLD, GREEN_TEXT, getenv("PWD"),
|
||||||
|
RESET, "> ");
|
||||||
|
free(hostname);
|
||||||
|
}
|
||||||
|
if (prompt)
|
||||||
|
{
|
||||||
|
line = readline(prompt);
|
||||||
|
free(prompt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
line = readline("minishell> ");
|
||||||
|
if (!line && exit_builtin(1, NULL))
|
||||||
break ;
|
break ;
|
||||||
if (*line)
|
if (*line)
|
||||||
{
|
{
|
||||||
add_history(line);
|
add_history(line);
|
||||||
write_history(minishell.history_file);
|
write_history(minishell->history_file);
|
||||||
if (!ft_strncmp("exit\0", line, 5))
|
|
||||||
|
t_cmd cmd_parse = parse_cmd(line, minishell);
|
||||||
|
// exec_cmd();
|
||||||
|
|
||||||
|
// builtins
|
||||||
|
if (ft_strncmp("exit\0", line, 5) == 0 && exit_builtin(1, NULL) != -1)
|
||||||
break ;
|
break ;
|
||||||
|
else if (ft_strncmp("cd\0", line, 3) == 0)
|
||||||
|
cd_builtin(1, NULL);
|
||||||
|
else if (ft_strncmp("pwd\0", line, 4) == 0)
|
||||||
|
pwd_builtin(1, NULL);
|
||||||
|
else if (ft_strncmp("env\0", line, 4) == 0)
|
||||||
|
env_builtin(1, NULL);
|
||||||
|
else if (ft_strncmp("echo\0", line, 5) == 0)
|
||||||
|
echo_builtin(0, NULL);
|
||||||
|
// not builtins
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if string does not start with "./"
|
||||||
cmd = ft_strjoin("/usr/bin/", line);
|
cmd = ft_strjoin("/usr/bin/", line);
|
||||||
args[0] = line;
|
args[0] = line;
|
||||||
args[1] = NULL;
|
args[1] = NULL;
|
||||||
@@ -81,23 +163,31 @@ int main(
|
|||||||
waitpid(pid, NULL, 0);
|
waitpid(pid, NULL, 0);
|
||||||
free(cmd);
|
free(cmd);
|
||||||
}
|
}
|
||||||
// ft_putendl(line);
|
}
|
||||||
}
|
}
|
||||||
rl_clear_history();
|
rl_clear_history();
|
||||||
free(line);
|
free(line);
|
||||||
free((char *)minishell.history_file);
|
|
||||||
ft_free_split(minishell.env.path);
|
|
||||||
return (EXIT_SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static t_minishell init_minishell(void)
|
/**
|
||||||
|
* @brief Clears and frees resources associated with a t_minishell instance.
|
||||||
|
*
|
||||||
|
* This function performs the following actions:
|
||||||
|
* - Zeroes out the contents of the history_file string.
|
||||||
|
* - Frees the memory allocated for the history_file.
|
||||||
|
* - Frees the memory allocated for the path array using ft_free_split.
|
||||||
|
* - Zeroes out the entire t_minishell structure.
|
||||||
|
*
|
||||||
|
* @param minishell Pointer to the t_minishell structure to be cleared and
|
||||||
|
* freed.
|
||||||
|
*/
|
||||||
|
static void clear_minishell(
|
||||||
|
t_minishell *minishell
|
||||||
|
)
|
||||||
{
|
{
|
||||||
t_minishell minishell;
|
ft_bzero(minishell->history_file,
|
||||||
|
ft_strlen(minishell->history_file) * sizeof(char));
|
||||||
ft_bzero(&minishell, sizeof(t_minishell));
|
free(minishell->history_file);
|
||||||
minishell.prompt = RED_TEXT"minishell> "RESET;
|
// ft_free_split(minishell->path);
|
||||||
minishell.env.home = getenv("HOME");
|
ft_bzero(minishell, sizeof(t_minishell));
|
||||||
minishell.history_file = ft_strjoin(minishell.env.home, HISTORY_FILE);
|
|
||||||
minishell.env.path = ft_split(getenv("PATH"), ':');
|
|
||||||
return (minishell);
|
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/utils/get_hostname.c
Normal file
46
src/utils/get_hostname.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/* ************************************************************************** */
|
||||||
|
/* */
|
||||||
|
/* ::: :::::::: */
|
||||||
|
/* get_hostname.c :+: :+: :+: */
|
||||||
|
/* +:+ +:+ +:+ */
|
||||||
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
||||||
|
/* +#+#+#+#+#+ +#+ */
|
||||||
|
/* Created: 2025/08/01 02:32:19 by sede-san #+# #+# */
|
||||||
|
/* Updated: 2025/08/01 13:13:54 by sede-san ### ########.fr */
|
||||||
|
/* */
|
||||||
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
#include "minishell.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the system's hostname from the /etc/hostname file.
|
||||||
|
*
|
||||||
|
* This function opens the /etc/hostname file, reads the first line to obtain
|
||||||
|
* the hostname, trims any trailing newline character, and returns the result
|
||||||
|
* as a dynamically allocated string. The caller is responsible for freeing
|
||||||
|
* the returned string.
|
||||||
|
*
|
||||||
|
* @return A pointer to the trimmed hostname string on success, or NULL on
|
||||||
|
* failure.
|
||||||
|
*
|
||||||
|
* @note The returned string must be freed later to avoid leaks.
|
||||||
|
*/
|
||||||
|
char *get_hostname(void)
|
||||||
|
{
|
||||||
|
char *hostname;
|
||||||
|
char *hostname_trimmed;
|
||||||
|
int hostname_file;
|
||||||
|
|
||||||
|
hostname_file = open("/etc/hostname", O_RDONLY);
|
||||||
|
if (hostname_file != -1)
|
||||||
|
{
|
||||||
|
hostname = get_next_line(hostname_file);
|
||||||
|
close(hostname_file);
|
||||||
|
}
|
||||||
|
if (!hostname)
|
||||||
|
hostname = NULL;
|
||||||
|
hostname_trimmed = ft_strtrim(hostname, "\n");
|
||||||
|
if (hostname)
|
||||||
|
free(hostname);
|
||||||
|
return (hostname_trimmed);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user