Compare commits

...

60 commits

Author SHA1 Message Date
6f75f2d181
connec_cmd_execute: naive recursive pipe implementation (does not handle redirection) 2025-04-15 15:11:26 +02:00
df73b3d0c7
connec_cmd_execute: handle && and || 2025-04-15 15:11:26 +02:00
8feacccb15
group_cmd_execute: fork and passthrough to cmd_execute 2025-04-15 15:11:26 +02:00
425722801b
refactor(do_waitpid): put into own file for easier acces from other modules 2025-04-15 15:11:26 +02:00
f9f8a804a3
cmd_execute: execute simple commands 2025-04-15 15:11:11 +02:00
7af032539a
cmd_execute: initial scafholding 2025-04-15 15:11:11 +02:00
881c7f264c
signal: if a command is terminated by SIGQUIT, display that it core dumped 2025-04-15 15:11:11 +02:00
ceee5f99d4
signal: SIGQUIT prints ^\ to the terminal, and does not redisplay the prompt 2025-04-15 15:10:40 +02:00
10e8738336
signal: handle redisplaying the prompt correctly 2025-04-15 15:10:40 +02:00
3a309062d8
refactor(signal): remove unneded function
excve already resets signal handling to the default
2025-04-15 15:09:39 +02:00
cb8f64367d
minishell: exit with last command exit status 2025-04-15 15:09:39 +02:00
67b2347df9
signal: keep waiting for process to exit on SIGQUIT
the SA_RESTART flag causes syscalls such as waitpid to be resumed after a signal
is handled, which is what we want.
2025-04-15 15:08:06 +02:00
f0145d26f4
signal: do not exit shell on SIGINT, ignore SIGQUIT 2025-04-15 15:08:06 +02:00
be183b99ee
minishell: do wildcard expansion 2025-04-15 15:06:46 +02:00
f06f6e26e2
fix(expand_star): memory leak
You have to call closedir().

I also removed the errno checking to make some space, I don't think it is needed
when looking at the errors returned by these functions.
2025-04-15 15:05:52 +02:00
32e3976774
field splitting: implement field splitting 2025-04-15 15:05:52 +02:00
0cfa2677c5
fix(echo): echo - was treated as echo -n
Small logic error on my part
2025-04-15 15:03:13 +02:00
d40560bb37
unset: implement unset 2025-04-15 15:03:13 +02:00
1ef8b7a0ae
env: implement builtin env 2025-04-15 15:03:13 +02:00
0de583cf45
echo: implement builtin echo 2025-04-15 15:03:13 +02:00
715c2aced8
exit: handle invalid and large arguments 2025-04-15 15:03:13 +02:00
f1c132337b
exit: work for simple arguments
This does not address non-integer arguments, or extremly high/low arguments.
2025-04-15 15:03:13 +02:00
24ba87abba
exec: retain exit status of commands, including if they were signaled 2025-04-15 15:01:19 +02:00
5b7367925f
simple_cmd refactor: put subprocess functions in own file
This clears some space for new functions later
2025-04-15 14:41:44 +02:00
9e79a0829a
fix: do not read ahead in STDIN
The Open Group Base Specifications Issue 8 IEEE Std 1003.1-2024 sh — shell, the
standard command language interpreter says:

> When the shell is using standard input and it invokes a command that also uses
> standard input, the shell shall ensure that the standard input file pointer
> points directly after the command it has read when the command begins
> execution. It shall not read ahead in such a manner that any characters
> intended to be read by the invoked command are consumed by the shell (whether
> interpreted by the shell or not) or that characters that are not read by the
> invoked command are not seen by the shell.

We used the default BUFFER_SIZE for get_next_line of 1024, which caused us to
read ahead farther than was allowed by the Open Group Base Specification.

Setting BUFFER_SIZE=1 ensures that we don't read too far ahead, since
get_next_line will always immediatly stop once a newline is found.

This is for me the simplest way to solve this issue.
2025-04-15 14:41:44 +02:00
0842fdfe1d
fix: double free when getting command path for empty command
```
minishell$ ""
```
leads to the following error:
```
=================================================================
==1158770==ERROR: AddressSanitizer: attempting double-free on 0x5060000020c0 in thread T0:
    #0 0x5649fcb5b8a8 in free.part.0 asan_malloc_linux.cpp.o
    #1 0x5649fcbac549 in deal_with_filled_path /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:119:3
    #2 0x5649fcbac38b in filepath_from_env /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:165:10
    #3 0x5649fcbac220 in get_cmdpath /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:186:10
    #4 0x5649fcba8664 in simple_cmd_execute /home/khais/src/42/common_env/minishell/src/executing/simple_cmd/simple_cmd_execute.c:71:8
    #5 0x5649fcba6e4c in execute_command /home/khais/src/42/common_env/minishell/src/minishell.c:49:2
    #6 0x5649fcba6ce5 in main /home/khais/src/42/common_env/minishell/src/minishell.c:92:3
    #7 0x7fb4f2dcd27d in __libc_start_call_main (/nix/store/nqb2ns2d1lahnd5ncwmn6k84qfd7vx2k-glibc-2.40-36/lib/libc.so.6+0x2a27d) (BuildId: 704cab5816f130c494208e8cc24d87a386ef085b)
    #8 0x7fb4f2dcd338 in __libc_start_main@GLIBC_2.2.5 (/nix/store/nqb2ns2d1lahnd5ncwmn6k84qfd7vx2k-glibc-2.40-36/lib/libc.so.6+0x2a338) (BuildId: 704cab5816f130c494208e8cc24d87a386ef085b)
    #9 0x5649fca6f394 in _start (/home/khais/src/42/common_env/minishell/minishell+0x2b394)

0x5060000020c0 is located 0 bytes inside of 58-byte region [0x5060000020c0,0x5060000020fa)
freed by thread T0 here:
    #0 0x5649fcb5b8a8 in free.part.0 asan_malloc_linux.cpp.o
    #1 0x5649fcbac669 in select_path /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:81:3
    #2 0x5649fcbac524 in deal_with_filled_path /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:116:9
    #3 0x5649fcbac38b in filepath_from_env /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:165:10
    #4 0x5649fcbac220 in get_cmdpath /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:186:10
    #5 0x5649fcba8664 in simple_cmd_execute /home/khais/src/42/common_env/minishell/src/executing/simple_cmd/simple_cmd_execute.c:71:8
    #6 0x5649fcba6e4c in execute_command /home/khais/src/42/common_env/minishell/src/minishell.c:49:2
    #7 0x5649fcba6ce5 in main /home/khais/src/42/common_env/minishell/src/minishell.c:92:3
    #8 0x7fb4f2dcd27d in __libc_start_call_main (/nix/store/nqb2ns2d1lahnd5ncwmn6k84qfd7vx2k-glibc-2.40-36/lib/libc.so.6+0x2a27d) (BuildId: 704cab5816f130c494208e8cc24d87a386ef085b)

previously allocated by thread T0 here:
    #0 0x5649fcb5c897 in malloc (/home/khais/src/42/common_env/minishell/minishell+0x118897)
    #1 0x5649fcbac6be in alloc_path /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp_utils.c:34:9
    #2 0x5649fcbac4e9 in deal_with_filled_path /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:113:15
    #3 0x5649fcbac38b in filepath_from_env /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:165:10
    #4 0x5649fcbac220 in get_cmdpath /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:186:10
    #5 0x5649fcba8664 in simple_cmd_execute /home/khais/src/42/common_env/minishell/src/executing/simple_cmd/simple_cmd_execute.c:71:8
    #6 0x5649fcba6e4c in execute_command /home/khais/src/42/common_env/minishell/src/minishell.c:49:2
    #7 0x5649fcba6ce5 in main /home/khais/src/42/common_env/minishell/src/minishell.c:92:3
    #8 0x7fb4f2dcd27d in __libc_start_call_main (/nix/store/nqb2ns2d1lahnd5ncwmn6k84qfd7vx2k-glibc-2.40-36/lib/libc.so.6+0x2a27d) (BuildId: 704cab5816f130c494208e8cc24d87a386ef085b)

SUMMARY: AddressSanitizer: double-free asan_malloc_linux.cpp.o in free.part.0
==1158770==ABORTING
```

Looks like select_path frees filepath sometimes, no idea why. Removing these
lines didn't break any tests.

I'm pushing this to get a second opinion
2025-04-15 14:41:44 +02:00
cea8043ec9
simple_cmd executing: show error in case execve fails
We don't have to check the return status, since execve only returns on failure.

Fixes #52
2025-04-15 14:41:43 +02:00
4de1d0024a parsing: refactor out the last two functions needed to pass the norm 2025-04-15 14:41:43 +02:00
5e84e9a111 parsing: remove (at least some) dead code 2025-04-15 14:41:43 +02:00
218be08049 parsing: refactor group_cmd_parse into own file 2025-04-15 14:41:43 +02:00
b7871be426 parsing: refactor minishell_optional_pipeline_parse into own file 2025-04-15 14:41:43 +02:00
811ce3ef8e parsing: refactor minishell_simple_cmd_parse into own file 2025-04-15 14:41:43 +02:00
e033909819 makefile: generate depfiles for the correct object files
The depfiles were completely ineffective.

Now this is fixed.

The -MT argument sets the path for the dependenency root. By default it is set
only to a filename, and thus does not include the full path of the file.
2025-04-15 14:41:43 +02:00
9a58198303 parsing: refactor minishell_redirect_parse to own file 2025-04-15 14:41:43 +02:00
d4197fec38 parsing: create specialised function to create a t_cmd 2025-04-15 14:41:43 +02:00
fde0bf4dc9 parsing: refactor: simple norm linecount gains 2025-04-15 14:41:43 +02:00
055dabc546 parsing: add newline at end of error message 2025-04-15 14:41:43 +02:00
dbba36dc44 parsing: split minishell_group_or_simple_parse 2025-04-15 14:41:43 +02:00
86e658fcca parsing: rename minishell_group_or_smp_parse -> minishell_group_or_simple_parse 2025-04-15 14:41:43 +02:00
283458b24f parsing: put redirect specific functions into own file 2025-04-15 14:41:43 +02:00
54f6c2a609 parsing: remove debugs 2025-04-15 14:41:43 +02:00
0c489f25bb parsing: fix some easy leaks 2025-04-15 14:41:43 +02:00
d40b6c3586
debug, destroy all new types (also some code for testing it) 2025-04-15 14:41:43 +02:00
53693e171e parsing: do not use a builder struct
since only one field was being used, just pass that field around
2025-04-14 14:53:31 +02:00
7425195350 norm: fix all easy issues 2025-04-14 14:44:42 +02:00
1c9653a5a5 parsing: parse redirections 2025-04-14 14:44:42 +02:00
1866da6ea6
fix: make it compile 2025-04-11 16:57:31 +02:00
Jérôme Guélen
4baad88a44
parse-cmd: Only redirections left to parse. 2025-04-10 18:57:04 +02:00
Jérôme Guélen
926774846f
parse-cmd: Almost done but stopped by the gong 2025-04-08 19:56:46 +02:00
Jérôme Guélen
d0cea8828d
parse-cmd: Mainly adding destructors and a token type. 2025-04-08 16:29:57 +02:00
Jérôme Guélen
72714855f7
parse-cmd: fixes and partial norm. 2025-04-08 13:08:43 +02:00
Jérôme Guélen
10a3c9c411
parse-cmd: Various fixes and a rotation of the parsing tree 2025-04-08 12:14:38 +02:00
Jerome
90d213bf98 parse-cmd: Slow start. Ideas starting to coalesce. 2025-04-06 21:52:33 +02:00
Jerome
9acee63d22 parse-cmd: grammar.md 2025-04-06 15:49:52 +02:00
Jerome
1a22a39336 parse-cmd: fix typo and grammar.md appearance 2025-04-06 15:49:52 +02:00
Jérôme Guélen
fea514ea63 parse-cmd: Just a forgotten check to be reviewed later. 2025-04-06 15:49:52 +02:00
Jérôme Guélen
5f95751b36 parse-cmd: A very basic skeleton of functions to come 2025-04-06 15:49:52 +02:00
Jérôme Guélen
f490cc22f5 Parsing: Added a document to describe the underlying grammar 2025-04-06 15:49:52 +02:00
Jérôme Guélen
4d9fabc794 Parse-cmd: Added a builder structure (unfinished) 2025-04-06 15:49:52 +02:00
Jérôme Guélen
10d7b65b08 Parse-cmd: fix and add comment 2025-04-06 15:49:52 +02:00
86 changed files with 2161 additions and 1476 deletions

View file

@ -15,7 +15,10 @@ IFLAGS = -I$(LIBFTDIR)
LINCLUDE = -L$(LIBFTDIR)
ifeq ($(CFLAGS),)
CFLAGS = -Wall -Wextra -Werror $(DEBUG)
CFLAGS = -Wall -Wextra \
$(DEBUG) \
# -Werror \
endif
export CFLAGS
srcs = \
@ -25,27 +28,36 @@ srcs = \
src/env/env_convert.c \
src/env/env_manip.c \
src/env/envp.c \
src/executing/cmd/cmd_execute.c \
src/executing/common/do_waitpid.c \
src/executing/connec_cmd/connec_cmd_execute.c \
src/executing/group_cmd/group_cmd_execute.c \
src/executing/here_doc/here_doc.c \
src/executing/here_doc/here_doc_errors.c \
src/executing/here_doc/here_doc_expand_line.c \
src/executing/here_doc/random_filename.c \
src/executing/here_doc/strip_newline.c \
src/executing/simple_cmd/builtin_cd.c \
src/executing/simple_cmd/builtin_echo.c \
src/executing/simple_cmd/builtin_env.c \
src/executing/simple_cmd/builtin_exit.c \
src/executing/simple_cmd/builtin_export.c \
src/executing/simple_cmd/builtin_invalid.c \
src/executing/simple_cmd/builtin_pwd.c \
src/executing/simple_cmd/builtins.c \
src/executing/simple_cmd/builtin_unset.c \
src/executing/simple_cmd/simple_cmd_execute.c \
src/executing/simple_cmd/subprocess.c \
src/ft_errno.c \
src/get_command.c \
src/parser/cmdgroup/cmdgroup.c \
src/parser/cmdgroup/cmdgroup_builder.c \
src/parser/cmd/cmd.c \
src/parser/cmd/cmd_debug.c \
src/parser/cmd/cmd_destroy.c \
src/parser/cmdgroup/paren.c \
src/parser/cmdlist/cmdlist.c \
src/parser/cmdlist/cmdlist_builder.c \
src/parser/cmdlist/cmdlist_debug.c \
src/parser/cmdlist/cmdlist_item.c \
src/parser/cmdlist/operator.c \
src/parser/connec_cmd/connec_cmd_debug.c \
src/parser/connec_cmd/connec_reorient_subtree.c \
src/parser/group_cmd/group_cmd_debug.c \
src/parser/group_cmd/group_cmd_parse.c \
src/parser/matchers/blank.c \
src/parser/matchers/identifier.c \
src/parser/matchers/metacharacter.c \
@ -53,13 +65,16 @@ srcs = \
src/parser/matchers/operator_start.c \
src/parser/matchers/pipe.c \
src/parser/matchers/quote.c \
src/parser/pipeline/pipeline.c \
src/parser/pipeline/pipeline_debug.c \
src/parser/pipeline/optional_pipeline_parse.c \
src/parser/pipeline/pipeline_parse.c \
src/parser/pipeline/pipeline_parse_baseops.c \
src/parser/redirect/redirect.c \
src/parser/redirect/redirect_debug.c \
src/parser/redirect/redirect_from_words.c \
src/parser/redirect/redirect_parse.c \
src/parser/remove_quotes/cmdgroup_remove_quotes.c \
src/parser/remove_quotes/remove_quotes.c \
src/parser/simple_cmd/simple_cmd.c \
src/parser/simple_cmd/simple_cmd_parse.c \
src/parser/worddesc/worddesc.c \
src/parser/wordlist/wordlist.c \
src/parser/wordlist/wordlist_copy.c \
@ -73,6 +88,9 @@ srcs = \
src/parser/wordsplit/wordsplit.c \
src/parser/wordsplit/wordsplit_utils.c \
src/postprocess/expansion/expand_vars.c \
src/postprocess/expansion/expand_wildcard.c \
src/postprocess/fieldsplit/fieldsplit.c \
src/sig/sig.c \
src/subst/path_split.c \
src/subst/replace_substr.c \
src/subst/simple_filename_exp.c \
@ -83,6 +101,8 @@ srcs = \
src/subst/wildcard_exp_utils.c \
src/subst/wildcard_exp_utils2.c \
src/treedrawing.c \
src/parser/cmd_parsing.c
objs = $(srcs:.c=.o)
export objs
@ -100,12 +120,13 @@ all: $(NAME)
$(NAME): $(minishell_objs) $(LIBFT)
$(CC) $(CFLAGS) -o $@ $(minishell_objs) $(LINCLUDE) $(LDLIBS)
$(LIBFT): CFLAGS+=-DBUFFER_SIZE=1
$(LIBFT):
+$(MAKE) -C $(LIBFTDIR)
%.o: %.c
$(CC) -c $(CFLAGS) $(IFLAGS) -o $*.o $*.c
$(CC) -MM $(CFLAGS) $(IFLAGS) $*.c > $*.d
$(CC) -MM $(CFLAGS) $(IFLAGS) -MT $*.o $*.c > $*.d
clean:
+$(MAKE) -C $(LIBFTDIR) clean

54
grammar.md Normal file
View file

@ -0,0 +1,54 @@
## Initial Grammar (Left recursivity)
This grammar is conceived to take into account both left associativity
and priority of operators to wit () is of highest priority followed by |
and then || and && which share the same priority (priorization therefore
occurs because of left associativity).
```
LINE -> CMDS eol
CMDS -> CMDS LIST_OP PIPELINE
CMDS -> PIPELINE
PIPELINE -> PIPELINE | GROUP_OR_SIMPLE
PIPELINE -> GROUP_OR_SIMPLE
GROUP_OR_SIMPLE -> (CMDS) REDIR
GROUP_OR_SIMPLE -> SIMPLE
SIMPLE -> REDIR word REDIR SIMPLE_LST
SIMPLE_LST -> word REDIR SIMPLE_LST
SIMPLE_LST -> ε
REDIR -> > word REDIR
REDIR -> >> word REDIR
REDIR -> < word REDIR
REDIR -> << word REDIR
REDIR -> ε
LIST_OP -> &&
LIST_OP -> ||
```
## Grammar after removal of left recursivity
The same priorities as the previous version except it is now LL(1) and
therefore compatible with descending syntax analysis (LL(1)).
```
LINE -> CMDS eol
CMDS -> PIPELINE OPT_CMDS
OPT_CMDS -> LIST_OP PIPELINE OPT_CMDS
OPT_CMDS -> ε
PIPELINE -> GROUP_OR_SIMPLE OPT_PIPELINE
OPT_PIPELINE -> | GROUP_OR_SIMPLE OPT_PIPELINE
OPT_PIPELINE -> ε
GROUP_OR_SIMPLE -> (CMDS) REDIR
GROUP_OR_SIMPLE -> SIMPLE
SIMPLE -> REDIR word REDIR SIMPLE_LST
SIMPLE_LST -> word REDIR SIMPLE_LST
SIMPLE_LST -> ε
REDIR -> > word REDIR
REDIR -> >> word REDIR
REDIR -> < word REDIR
REDIR -> << word REDIR
REDIR -> ε
LIST_OP -> &&
LIST_OP -> ||
```

View file

@ -0,0 +1,26 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmd_execute.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/04 19:26:37 by khais #+# #+# */
/* Updated: 2025/04/07 10:38:07 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "cmd_execute.h"
#include "../simple_cmd/simple_cmd_execute.h"
#include "../connec_cmd/connec_cmd_execute.h"
#include "../group_cmd/group_cmd_execute.h"
void cmd_execute(t_cmd *cmd, t_minishell *app)
{
if (cmd->type == FT_SIMPLE)
simple_cmd_execute(cmd->value.simple, app);
if (cmd->type == FT_GROUP)
group_cmd_execute(cmd->value.group, app);
if (cmd->type == FT_CONNECTION)
connec_cmd_execute(cmd->value.connection, app);
}

View file

@ -1,23 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdlist_item_type.h :+: :+: :+: */
/* cmd_execute.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/18 12:56:30 by khais #+# #+# */
/* Updated: 2025/03/18 15:02:27 by khais ### ########.fr */
/* Created: 2025/04/04 19:25:39 by khais #+# #+# */
/* Updated: 2025/04/04 19:37:46 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CMDLIST_ITEM_TYPE_H
# define CMDLIST_ITEM_TYPE_H
#ifndef CMD_EXECUTE_H
# define CMD_EXECUTE_H
typedef enum e_cmdlist_item_type
{
TYPE_INVALID,
TYPE_CMDGROUP,
TYPE_PIPELINE,
} t_cmdlist_item_type;
# include "../../minishell.h"
#endif // CMDLIST_ITEM_TYPE_H
void cmd_execute(t_cmd *cmd, t_minishell *app);
#endif // CMD_EXECUTE_H

View file

@ -1,46 +1,31 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* operator.h :+: :+: :+: */
/* do_waitpid.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/26 12:49:46 by khais #+# #+# */
/* Updated: 2025/03/19 14:41:55 by khais ### ########.fr */
/* Created: 2025/04/04 19:55:22 by khais #+# #+# */
/* Updated: 2025/04/04 19:55:59 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef OPERATOR_H
# define OPERATOR_H
#include "do_waitpid.h"
#include "libft.h"
#include <signal.h>
#include <sys/wait.h>
# include "../../buffer/buffer.h"
# include <stdbool.h>
/*
** An operator used by a cmdlist
*/
typedef enum e_operator
void do_waitpid(t_minishell *app, int pid)
{
/*
** Not a valid operator
*/
OP_INVALID,
/*
** &&
*/
OP_AND,
/*
** ||
*/
OP_OR,
/*
** End of operator list
*/
OP_END,
} t_operator;
int wstatus;
t_operator match_op(char *op);
void operator_debug(t_operator op, t_buffer *leader,
bool is_last);
#endif // OPERATOR_H
waitpid(pid, &wstatus, 0);
if (WIFEXITED(wstatus))
app->last_return_value = WEXITSTATUS(wstatus);
if (WIFSIGNALED(wstatus))
{
app->last_return_value = 128 + WTERMSIG(wstatus);
if (WTERMSIG(wstatus) == SIGQUIT)
ft_dprintf(STDERR_FILENO, "Quit (core dumped)");
}
}

View file

@ -1,23 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* pipeline_parse_baseops.h :+: :+: :+: */
/* do_waitpid.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/24 15:05:16 by khais #+# #+# */
/* Updated: 2025/02/24 15:09:01 by khais ### ########.fr */
/* Created: 2025/04/04 19:54:53 by khais #+# #+# */
/* Updated: 2025/04/04 19:57:07 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef PIPELINE_PARSE_BASEOPS_H
# define PIPELINE_PARSE_BASEOPS_H
# include "pipeline.h"
#ifndef DO_WAITPID_H
# define DO_WAITPID_H
void ignore_word(t_pipeline_builder *builder);
bool current_is_pipe(t_pipeline_builder *builder);
void next_word(t_pipeline_builder *builder);
bool eof(t_pipeline_builder *builder);
void push_word(t_pipeline_builder *builder);
# include "../../minishell.h"
#endif // PIPELINE_PARSE_BASEOPS_H
void do_waitpid(t_minishell *app, int pid);
#endif // DO_WAITPID_H

View file

@ -0,0 +1,55 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* connec_cmd_execute.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/07 10:38:55 by khais #+# #+# */
/* Updated: 2025/04/07 11:36:57 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "connec_cmd_execute.h"
#include "../cmd/cmd_execute.h"
#include "../common/do_waitpid.h"
#include <unistd.h>
static void connec_and_cmd_execute(t_connec_cmd *cmd, t_minishell *app)
{
cmd_execute(cmd->first, app);
if (app->last_return_value == 0)
cmd_execute(cmd->second, app);
}
static void connec_or_cmd_execute(t_connec_cmd *cmd, t_minishell *app)
{
cmd_execute(cmd->first, app);
if (app->last_return_value != 0)
cmd_execute(cmd->second, app);
}
static void connec_pipe_cmd_execute(t_connec_cmd *cmd, t_minishell *app)
{
int pid1;
int pid2;
pid1 = fork();
if (pid1 == 0)
cmd_execute(cmd->first, app);
pid2 = fork();
if (pid2 == 0)
cmd_execute(cmd->second, app);
do_waitpid(app, pid1);
do_waitpid(app, pid2);
}
void connec_cmd_execute(t_connec_cmd *cmd, t_minishell *app)
{
if (cmd->connector == FT_AND)
connec_and_cmd_execute(cmd, app);
if (cmd->connector == FT_OR)
connec_or_cmd_execute(cmd, app);
if (cmd->connector == FT_PIPE)
connec_pipe_cmd_execute(cmd, app);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* connec_cmd_execute.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/07 10:38:19 by khais #+# #+# */
/* Updated: 2025/04/07 10:38:48 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CONNEC_CMD_EXECUTE_H
# define CONNEC_CMD_EXECUTE_H
# include "../../minishell.h"
void connec_cmd_execute(t_connec_cmd *cmd, t_minishell *app);
#endif // CONNEC_CMD_EXECUTE_H

View file

@ -0,0 +1,26 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* group_cmd_execute.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/04 19:50:42 by khais #+# #+# */
/* Updated: 2025/04/04 20:01:20 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "group_cmd_execute.h"
#include "../cmd/cmd_execute.h"
#include "../common/do_waitpid.h"
#include <unistd.h>
void group_cmd_execute(t_group_cmd *cmd, t_minishell *app)
{
int pid;
pid = fork();
if (pid == 0)
cmd_execute(cmd->cmd, app);
do_waitpid(app, pid);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* group_cmd_execute.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/04 19:42:36 by khais #+# #+# */
/* Updated: 2025/04/04 19:43:09 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef GROUP_CMD_EXECUTE_H
# define GROUP_CMD_EXECUTE_H
# include "../../minishell.h"
void group_cmd_execute(t_group_cmd *cmd, t_minishell *app);
#endif // GROUP_CMD_EXECUTE_H

View file

@ -3,15 +3,14 @@
/* ::: :::::::: */
/* builtin_cd.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/31 16:20:17 by khais #+# #+# */
/* Updated: 2025/03/31 20:04:28 by khais ### ########.fr */
/* Updated: 2025/04/07 17:35:16 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "builtins.h"
#include "ft_printf.h"
#include "libft.h"
#include <unistd.h>
#include "../../env/env_manip.h"

View file

@ -0,0 +1,63 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* builtin_echo.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/03 13:59:13 by khais #+# #+# */
/* Updated: 2025/04/03 17:18:54 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "builtins.h"
#include "libft.h"
#include "simple_cmd_execute.h"
static bool should_print_newline(t_wordlist **arg)
{
bool print_newline;
bool end_of_args;
size_t i;
print_newline = true;
while (*arg != NULL)
{
if ((*arg)->word->word[0] != '-')
break ;
i = 1;
end_of_args = (*arg)->word->word[i] == '\0';
if (end_of_args)
break ;
while ((*arg)->word->word[i] != '\0' && !end_of_args)
{
if ((*arg)->word->word[i++] != 'n')
end_of_args = true;
}
if (end_of_args)
break ;
print_newline = false;
(*arg) = (*arg)->next;
}
return (print_newline);
}
t_builtin_type builtin_echo(t_simple_cmd *cmd, t_minishell *app)
{
t_wordlist *arg;
bool print_newline;
arg = cmd->words->next;
print_newline = should_print_newline(&arg);
while (arg != NULL)
{
ft_printf("%s", arg->word->word);
arg = arg->next;
if (arg != NULL)
ft_printf(" ");
}
if (print_newline)
ft_printf("\n");
app->last_return_value = 0;
return (BUILTIN_ECHO);
}

View file

@ -0,0 +1,30 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* builtin_env.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/02 19:35:52 by khais #+# #+# */
/* Updated: 2025/04/02 19:38:16 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "builtins.h"
#include "libft.h"
t_builtin_type builtin_env(t_simple_cmd *cmd, t_minishell *app)
{
t_env *env;
if (cmd->words->next != NULL)
ft_dprintf(STDERR_FILENO, "minishell: env: ignoring arguments\n");
env = app->env;
while (env != NULL)
{
ft_printf("%s=%s\n", env->key, env->value);
env = env->next;
}
app->last_return_value = 0;
return (BUILTIN_ENV);
}

View file

@ -0,0 +1,79 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* builtin_exit.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/01 18:17:56 by khais #+# #+# */
/* Updated: 2025/04/15 11:48:05 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "builtins.h"
#include <stdlib.h>
#include "libft.h"
#include "simple_cmd_execute.h"
#include "unistd.h"
static int ft_isspace(char c)
{
return ((c == ' ' || c == '\f' || c == '\n'
|| c == '\r' || c == '\t' || c == '\v'));
}
static int ft_atoi_strict(const char *nptr, bool *ok)
{
int sign;
int calc;
sign = 1;
calc = 0;
*ok = true;
while (*nptr && ft_isspace(*nptr))
nptr++;
if (*nptr == '+' || *nptr == '-')
{
if (nptr[0] == '-')
sign = -1;
nptr++;
}
while (*nptr && ft_isdigit(*nptr))
{
calc = calc * 10 + (*nptr - '0');
nptr++;
}
if (*nptr != '\0')
*ok = false;
return (sign * calc);
}
static int numeric_arg_required(char *arg)
{
ft_dprintf(STDERR_FILENO,
"minishell: exit: %s: numeric argument required\n", arg);
return (2);
}
t_builtin_type builtin_exit(t_simple_cmd *cmd, t_minishell *app)
{
int status;
bool ok;
status = 0;
if (cmd->words->next != NULL)
{
status = ft_atoi_strict(cmd->words->next->word->word, &ok);
if (!ok)
status = numeric_arg_required(cmd->words->next->word->word);
else if (cmd->words->next->next != NULL)
{
ft_dprintf(STDERR_FILENO, "minishell: exit: too many arguments\n");
app->last_return_value = 1;
return (BUILTIN_EXIT);
}
}
simple_cmd_destroy(cmd);
env_destroy(app->env);
exit(status);
}

View file

@ -0,0 +1,28 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* builtin_unset.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/03 14:35:53 by khais #+# #+# */
/* Updated: 2025/04/03 14:39:45 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "builtins.h"
#include "../../env/env_manip.h"
t_builtin_type builtin_unset(t_simple_cmd *cmd, t_minishell *app)
{
t_wordlist *arg;
arg = cmd->words->next;
while (arg != NULL)
{
env_rm_entry(&app->env, arg->word->word);
arg = arg->next;
}
app->last_return_value = 0;
return (BUILTIN_UNSET);
}

View file

@ -6,7 +6,7 @@
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/01 16:37:21 by khais #+# #+# */
/* Updated: 2025/04/01 16:37:38 by khais ### ########.fr */
/* Updated: 2025/04/03 14:35:30 by khais ### ########.fr */
/* */
/* ************************************************************************** */
@ -24,6 +24,14 @@ t_builtin_type get_builtin(t_simple_cmd *cmd)
return (BUILTIN_CD);
if (ft_strcmp("export", word) == 0)
return (BUILTIN_EXPORT);
if (ft_strcmp("exit", word) == 0)
return (BUILTIN_EXIT);
if (ft_strcmp("echo", word) == 0)
return (BUILTIN_ECHO);
if (ft_strcmp("env", word) == 0)
return (BUILTIN_ENV);
if (ft_strcmp("unset", word) == 0)
return (BUILTIN_UNSET);
return (BUILTIN_INVALID);
}
@ -35,6 +43,10 @@ t_builtin_type execute_builtin(t_simple_cmd *cmd, t_minishell *app)
[BUILTIN_PWD] = builtin_pwd,
[BUILTIN_CD] = builtin_cd,
[BUILTIN_EXPORT] = builtin_export,
[BUILTIN_EXIT] = builtin_exit,
[BUILTIN_ECHO] = builtin_echo,
[BUILTIN_ENV] = builtin_env,
[BUILTIN_UNSET] = builtin_unset,
};
type = get_builtin(cmd);

View file

@ -6,7 +6,7 @@
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/31 14:16:13 by khais #+# #+# */
/* Updated: 2025/04/01 16:37:15 by khais ### ########.fr */
/* Updated: 2025/04/03 14:35:44 by khais ### ########.fr */
/* */
/* ************************************************************************** */
@ -19,6 +19,10 @@ t_builtin_type builtin_invalid(t_simple_cmd *cmd, t_minishell *app);
t_builtin_type builtin_pwd(t_simple_cmd *cmd, t_minishell *app);
t_builtin_type builtin_cd(t_simple_cmd *cmd, t_minishell *app);
t_builtin_type builtin_export(t_simple_cmd *cmd, t_minishell *app);
t_builtin_type builtin_exit(t_simple_cmd *cmd, t_minishell *app);
t_builtin_type builtin_echo(t_simple_cmd *cmd, t_minishell *app);
t_builtin_type builtin_env(t_simple_cmd *cmd, t_minishell *app);
t_builtin_type builtin_unset(t_simple_cmd *cmd, t_minishell *app);
t_builtin_type get_builtin(t_simple_cmd *cmd);
t_builtin_type execute_builtin(t_simple_cmd *cmd, t_minishell *app);

View file

@ -6,32 +6,20 @@
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/27 16:21:56 by khais #+# #+# */
/* Updated: 2025/04/01 16:37:31 by khais ### ########.fr */
/* Updated: 2025/04/04 17:09:39 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "simple_cmd_execute.h"
#include "builtins.h"
#include "subprocess.h"
#include "libft.h"
#include "../../subst/subst.h"
#include "../../env/env_convert.h"
#include "../common/do_waitpid.h"
#include <unistd.h>
#include <sys/wait.h>
static char **argv_from_wordlist(t_wordlist *wordlist)
{
char **out;
int i;
out = ft_calloc(wordlist_size(wordlist) + 1, sizeof(char *));
i = 0;
while (wordlist != NULL)
{
out[i++] = ft_strdup(wordlist->word->word);
wordlist = wordlist->next;
}
return (out);
}
#include <stdio.h>
#include <stdlib.h>
static void command_not_found(t_simple_cmd *cmd)
{
@ -56,8 +44,8 @@ void simple_cmd_execute(t_simple_cmd *cmd, t_minishell *app)
}
pid = fork();
if (pid == 0)
execve(exe, argv_from_wordlist(cmd->words), envp_from_env(app->env));
execute_subprocess(exe, cmd, app);
free(exe);
waitpid(pid, NULL, 0);
do_waitpid(app, pid);
return ;
}

View file

@ -5,8 +5,8 @@
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/27 16:20:31 by khais #+# #+# */
/* Updated: 2025/04/01 13:54:10 by khais ### ########.fr */
/* Created: 2025/04/03 14:35/14 by khais #+# #+# */
/* Updated: 2025/04/03 14:35:14 by khais ### ########.fr */
/* */
/* ************************************************************************** */
@ -24,6 +24,10 @@ typedef enum e_builtin_type
BUILTIN_PWD,
BUILTIN_CD,
BUILTIN_EXPORT,
BUILTIN_EXIT,
BUILTIN_ECHO,
BUILTIN_ENV,
BUILTIN_UNSET,
} t_builtin_type;
#endif // SIMPLE_CMD_EXECUTE_H

View file

@ -0,0 +1,50 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* subprocess.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/02 18:19:23 by khais #+# #+# */
/* Updated: 2025/04/02 18:20:54 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "subprocess.h"
#include "../../env/env_convert.h"
#include "../../parser/simple_cmd/simple_cmd.h"
#include "../../subst/path_split.h"
#include <stdio.h>
static char **argv_from_wordlist(t_wordlist *wordlist)
{
char **out;
int i;
out = ft_calloc(wordlist_size(wordlist) + 1, sizeof(char *));
i = 0;
while (wordlist != NULL)
{
out[i++] = ft_strdup(wordlist->word->word);
wordlist = wordlist->next;
}
return (out);
}
static void ft_execve(char *exe, char **argv, char **envp)
{
execve(exe, argv, envp);
ft_dprintf(STDERR_FILENO, "minishell: %s: ", argv[0]);
perror(NULL);
free(exe);
path_split_destroy(argv);
path_split_destroy(envp);
}
void execute_subprocess(char *exe, t_simple_cmd *cmd, t_minishell *app)
{
ft_execve(exe, argv_from_wordlist(cmd->words), envp_from_env(app->env));
simple_cmd_destroy(cmd);
env_destroy(app->env);
exit(127);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* subprocess.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/02 18:17:48 by khais #+# #+# */
/* Updated: 2025/04/02 18:21:30 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef SUBPROCESS_H
# define SUBPROCESS_H
# include "../../minishell.h"
void execute_subprocess(char *exe, t_simple_cmd *cmd, t_minishell *app);
#endif // SUBPROCESS_H

View file

@ -3,65 +3,51 @@
/* ::: :::::::: */
/* minishell.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <marvin@42.fr> +#+ +:+ +#+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/06 13:44:06 by kcolin #+# #+# */
/* Updated: 2025/04/01 13:36:52 by khais ### ########.fr */
/* Updated: 2025/04/15 11:53:49 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "minishell.h"
#include "env/env.h"
#include "get_command.h"
#include "parser/cmd/cmd_destroy.h"
#include "parser/simple_cmd/simple_cmd.h"
#include "parser/wordlist/wordlist.h"
#include "parser/wordsplit/wordsplit.h"
#include "parser/remove_quotes/remove_quotes.h"
#include "libft.h"
#include "executing/simple_cmd/simple_cmd_execute.h"
#include <stdlib.h>
#include "env/env_convert.h"
#include "postprocess/expansion/expand_vars.h"
/*
** Parse shell commands from line.
**
** Frees line before exiting
*/
static t_simple_cmd *parse_command(char *line)
{
t_wordlist *words;
t_simple_cmd *cmd;
words = minishell_wordsplit(line);
free(line);
if (words == NULL)
return (NULL);
cmd = simple_cmd_from_wordlist(words);
return (cmd);
}
#include "parser/cmd_parsing.h"
#include "postprocess/fieldsplit/fieldsplit.h"
#include "postprocess/expansion/expand_wildcard.h"
#include "sig/sig.h"
/*
** execute command
*/
static void execute_command(t_simple_cmd *cmd, t_minishell *app)
{
set_exec_mode_sig_handling();
simple_cmd_execute(cmd, app);
simple_cmd_destroy(cmd);
set_interactive_mode_sig_handling();
}
/*
** Do all the post-processing steps relating to a command.
**
** Currently, this is the following:
** 1. variable expansion
** 2. quote removal
*/
static t_simple_cmd *post_process_command(t_simple_cmd *cmd, t_minishell *app)
static t_cmd *post_process_command(t_cmd *cmd, t_minishell *app)
{
if (simple_cmd_expand_vars(cmd, app) == NULL)
return (simple_cmd_destroy(cmd), NULL);
if (simple_cmd_remove_quotes(cmd) == NULL)
if (simple_cmd_fieldsplit(cmd) == NULL)
return (simple_cmd_destroy(cmd), NULL);
if (simple_cmd_expand_wildcards(cmd) == NULL)
return (simple_cmd_destroy(cmd), NULL);
return (cmd);
}
@ -77,21 +63,44 @@ static void app_init(t_minishell *app, char **envp)
int main(int argc, char *argv[], char **envp)
{
char *line;
t_simple_cmd *cmd;
t_minishell app;
char *line;
t_cmd *cmd;
t_minishell app;
(void)argc;
(void)argv;
set_interactive_mode_sig_handling();
app_init(&app, envp);
line = get_command();
while (line != NULL)
{
cmd = parse_command(line);
cmd = post_process_command(cmd, &app);
execute_command(cmd, &app);
line = get_command();
}
line = "echo coucou";
cmd = minishell_parse(&app, line);
cmd_destroy(cmd);
env_destroy(app.env);
return (0);
}
/* int main(int argc, char *argv[], char **envp) */
/* { */
/* char *line; */
/* t_cmd *cmd; */
/* t_minishell app; */
/* (void)argc; */
/* (void)argv; */
/* app_init(&app, envp); */
/* line = get_command(); */
/* while (line != NULL) */
/* { */
/* cmd = minishell_parse(&app, line); */
/* cmd = post_process_command(cmd, &app); */
/* execute_command(cmd, &app); */
/* if (g_signum != -1) */
/* { */
/* ft_printf("\n"); */
/* g_signum = -1; */
/* readline_reset(); */
/* } */
/* line = get_command(); */
/* } */
/* env_destroy(app.env); */
/* return (app.last_return_value); */
/* } */

View file

@ -5,16 +5,18 @@
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/28 14:55/31 by khais #+# #+# */
/* Updated: 2025/03/28 14:55:31 by khais ### ########.fr */
/* Created: 2025/04/14 15:00/22 by khais #+# #+# */
/* Updated: 2025/04/14 15:00:22 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef MINISHELL_H
# define MINISHELL_H
# include "libft.h"
# include "env/env.h"
# include "parser/wordlist/wordlist.h"
# include "parser/wordsplit/wordsplit.h"
# include <sys/stat.h>
# include <fcntl.h>
@ -57,12 +59,14 @@ typedef enum e_cmd_type
typedef struct s_cmd
{
t_cmd_type type;
/*
** flags will probably be useless to us for now.
*/
int flags;
/*
** Line number the command starts on -> will probably be unused.
*/
int line;
t_redirect *redirects;
union u_value
{
struct s_connec_cmd *connection;
@ -73,6 +77,7 @@ typedef struct s_cmd
typedef enum e_connector
{
FT_INVALID_CONNECTOR,
FT_PIPE,
FT_AND,
FT_OR,
@ -111,7 +116,10 @@ typedef struct s_simple_cmd
typedef struct s_minishell
{
t_env *env;
int lines_read;
int last_return_value;
} t_minishell;
t_redirect *t_redirect_add_back(t_redirect **init, t_redirect *back);
#endif

25
src/parser/cmd/cmd.c Normal file
View file

@ -0,0 +1,25 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmd.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/14 17:50:28 by khais #+# #+# */
/* Updated: 2025/04/14 17:53:52 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "cmd.h"
#include "../../ft_errno.h"
t_cmd *cmd_create(t_cmd_type type)
{
t_cmd *cmd;
cmd = ft_calloc(1, sizeof(t_cmd));
if (cmd == NULL)
return (ft_errno(FT_ENOMEM), NULL);
cmd->type = type;
return (cmd);
}

20
src/parser/cmd/cmd.h Normal file
View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmd.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/14 17:49:55 by khais #+# #+# */
/* Updated: 2025/04/14 17:50:20 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CMD_H
# define CMD_H
# include "../../minishell.h"
t_cmd *cmd_create(t_cmd_type type);
#endif // CMD_H

View file

@ -0,0 +1,65 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmd_debug.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 16:53:27 by khais #+# #+# */
/* Updated: 2025/04/14 15:07:59 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "cmd_debug.h"
#include "libft.h"
#include "../../treedrawing.h"
#include "../simple_cmd/simple_cmd.h"
#include "../group_cmd/group_cmd_debug.h"
#include "../connec_cmd/connec_cmd_debug.h"
#include "../redirect/redirect_debug.h"
static void cmd_type_debug(t_cmd_type type, t_buffer *leader, bool is_last)
{
indent(leader, is_last);
ft_printf("t_cmd_type = ");
if (type == FT_CONNECTION)
ft_printf("FT_CONNECTION\n");
if (type == FT_GROUP)
ft_printf("FT_GROUP\n");
if (type == FT_SIMPLE)
ft_printf("FT_SIMPLE\n");
dedent(leader, is_last);
}
static void cmd_value_debug(t_cmd *cmd, t_buffer *leader, bool is_last)
{
indent(leader, is_last);
ft_printf("value\n");
if (cmd->type == FT_CONNECTION)
connec_cmd_debug(cmd->value.connection, leader, true);
if (cmd->type == FT_GROUP)
group_cmd_debug(cmd->value.group, leader, true);
if (cmd->type == FT_SIMPLE)
simple_cmd_debug(cmd->value.simple, leader, true);
dedent(leader, is_last);
}
void cmd_debug(t_cmd *cmd, t_buffer *leader, bool is_last)
{
indent(leader, is_last);
if (cmd == NULL)
ft_printf("t_cmd = NULL\n");
else
{
ft_printf("t_cmd\n");
cmd_type_debug(cmd->type, leader, false);
indent(leader, false);
ft_printf("flags = %d\n", cmd->flags);
dedent(leader, false);
indent(leader, false);
ft_printf("line = %d\n", cmd->line);
dedent(leader, false);
cmd_value_debug(cmd, leader, true);
}
dedent(leader, is_last);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmd_debug.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 16:51:17 by khais #+# #+# */
/* Updated: 2025/04/09 16:54:24 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CMD_DEBUG_H
# define CMD_DEBUG_H
# include "../../minishell.h"
void cmd_debug(t_cmd *cmd, t_buffer *leader, bool is_last);
#endif // CMD_DEBUG_H

View file

@ -0,0 +1,61 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmd_destroy.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 16:53:02 by khais #+# #+# */
/* Updated: 2025/04/14 15:01:23 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "cmd_destroy.h"
#include <stdlib.h>
#include "../simple_cmd/simple_cmd.h"
#include "libft.h"
void redirect_destroy(t_redirect *redirect)
{
t_redirect *next;
while (redirect != NULL)
{
next = redirect->next;
free(redirect->here_doc_eof);
worddesc_destroy(redirect->redirectee.filename);
free(redirect);
redirect = next;
}
}
static void connec_cmd_destroy(t_connec_cmd *cmd)
{
if (cmd == NULL)
return ;
cmd_destroy(cmd->first);
cmd_destroy(cmd->second);
free(cmd);
}
static void group_cmd_destroy(t_group_cmd *cmd)
{
if (cmd == NULL)
return ;
cmd_destroy(cmd->cmd);
redirect_destroy(cmd->redirects);
free(cmd);
}
void cmd_destroy(t_cmd *cmd)
{
if (cmd == NULL)
return ;
if (cmd->type == FT_CONNECTION)
connec_cmd_destroy(cmd->value.connection);
if (cmd->type == FT_GROUP)
group_cmd_destroy(cmd->value.group);
if (cmd->type == FT_SIMPLE)
simple_cmd_destroy(cmd->value.simple);
free(cmd);
}

View file

@ -0,0 +1,21 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmd_destroy.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 16:52:31 by khais #+# #+# */
/* Updated: 2025/04/14 15:10:13 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CMD_DESTROY_H
# define CMD_DESTROY_H
# include "../../minishell.h"
void cmd_destroy(t_cmd *cmd);
void redirect_destroy(t_redirect *redirect);
#endif // CMD_DESTROY_H

127
src/parser/cmd_parsing.c Normal file
View file

@ -0,0 +1,127 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmd_parsing.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/31 10:28:28 by jguelen #+# #+# */
/* Updated: 2025/04/15 11:38:55 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "cmd_parsing.h"
#include <fcntl.h>
#include "connec_cmd/connec_reorient_subtree.h"
#include "worddesc/worddesc.h"
#include "wordlist/wordlist.h"
#include <unistd.h>
#include "simple_cmd/simple_cmd_parse.h"
#include "cmd/cmd_destroy.h"
#include "group_cmd/group_cmd_parse.h"
#include "pipeline/pipeline_parse.h"
void parse_error(t_minishell *app, t_worddesc *token)
{
ft_dprintf(STDERR_FILENO, "minishell: syntax error near unexpected "
"token `%s'\n", token->word);
app->last_return_value = 2;
}
t_cmd *minishell_group_or_simple_parse(t_minishell *app, t_wordlist *tokens)
{
if (tokens->word->token_type == OPEN_PARENTH_TOKEN)
{
worddesc_destroy(wordlist_pop(&tokens));
return (minishell_group_cmd_parse(app, tokens));
}
else
return (minishell_simple_cmd_parse(app, tokens));
}
t_cmd *minishell_opt_cmds_parse(t_minishell *app, t_wordlist *tokens,
t_connector *connec)
{
t_worddesc *token;
t_cmd *opt;
token = tokens->word;
if (token->token_type == OR_TOKEN || token->token_type == AND_TOKEN)
{
if (token->token_type == OR_TOKEN)
*connec = FT_OR;
else
*connec = FT_AND;
token = wordlist_pop(&tokens);
worddesc_destroy(token);
opt = minishell_pipeline_parse(app, tokens);
if (!opt)
ft_errno(FT_EERRNO);
return (opt);
}
return (NULL);
}
/*
** Parse list of commands or pipeline.
*/
t_cmd *minishell_cmds_parse(t_minishell *app, t_wordlist *tokens)
{
t_cmd *subtree;
t_cmd *opt;
t_cmd *list;
t_connector connec;
subtree = minishell_pipeline_parse(app, tokens);
if (!subtree)
return (NULL);
opt = minishell_opt_cmds_parse(app, tokens, &connec);
if (!opt && ft_errno_get() != FT_ESUCCESS)
return (cmd_destroy(subtree), NULL);
if (!opt)
return (subtree);
while (opt)
{
if (connec_reorient_subtree(&list, &subtree, &opt, connec) == NULL)
{
app->last_return_value = 1;
return (ft_perror("minishell_cmds_parse"), NULL);
}
opt = minishell_opt_cmds_parse(app, tokens, &connec);
}
if (ft_errno_get() != FT_ESUCCESS)
return (cmd_destroy(subtree), NULL);
return (list);
}
/*
** TODO check if we need to differentiate the cause of a NULL return.
*/
t_cmd *minishell_parse(t_minishell *app, char *command_line)
{
t_cmd *root_cmd;
t_wordlist *tokens;
ft_errno(FT_ESUCCESS);
app->last_return_value = 0;
if (!command_line)
return (NULL);
tokens = minishell_wordsplit(command_line);
if (!tokens)
return (NULL);
root_cmd = minishell_cmds_parse(app, tokens);
if (!root_cmd)
{
if (ft_errno_get() != FT_ESUCCESS && !app->last_return_value)
app->last_return_value = 1;
return (wordlist_destroy(tokens), NULL);
}
if (tokens)
{
parse_error(app, tokens->word);
wordlist_destroy(tokens);
cmd_destroy(root_cmd);
return (NULL);
}
return (root_cmd);
}

30
src/parser/cmd_parsing.h Normal file
View file

@ -0,0 +1,30 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmd_parsing.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 10:14:13 by khais #+# #+# */
/* Updated: 2025/04/15 11:35:26 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CMD_PARSING_H
# define CMD_PARSING_H
# include "../minishell.h"
# include "../ft_errno.h"
# include <stdbool.h>
void parse_error(t_minishell *app, t_worddesc *token);
t_redirect *t_redirect_add_back(t_redirect **init, t_redirect *back);
t_cmd *minishell_parse(t_minishell *app, char *command_line);
t_cmd *minishell_cmds_parse(t_minishell *app, t_wordlist *tokens);
t_cmd *minishell_opt_cmds_parse(t_minishell *app, t_wordlist *tokens,
t_connector *connec);
t_cmd *minishell_group_or_simple_parse(t_minishell *app,
t_wordlist *tokens);
#endif

View file

@ -1,72 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdgroup.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/11 15:18:02 by khais #+# #+# */
/* Updated: 2025/03/28 15:01:55 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "cmdgroup.h"
#include "cmdgroup_builder.h"
#include <stdlib.h>
#include "libft.h"
#include "paren.h"
#include "../../treedrawing.h"
#include "libft/libft.h"
/*
** Recursively parse a cmdgroup
**
** This is responsible for parsing one optional set of parentheses surrounding a
** cmdlist.
**
** If parentheses are present, also parse redirections, which may only occur
** after the closing parenthesis.
*/
t_cmdgroup *cmdgroup_from_wordlist(t_wordlist *wordlist)
{
t_cmdgroup_builder builder;
t_wordlist *words;
t_paren paren;
words = wordlist_copy(wordlist);
if (words == NULL)
return (NULL);
if (setup_cmdgroup_builder(&builder, &words) == NULL)
return (wordlist_destroy(words), NULL);
while (builder.error == false && builder.current_word != NULL)
{
paren = match_paren(builder.current_word->word);
if (paren == PAREN_INVALID)
cmdgroup_builder_next_word(&builder, &words);
else
cmdgroup_builder_paren(&builder, &words, paren);
}
if (builder.current_wordlist != NULL)
cmdgroup_builder_delimit(&builder, &words);
if (builder.error == true)
return (NULL);
return (builder.cmdgroup);
}
void cmdgroup_destroy(t_cmdgroup *cmd)
{
if (cmd == NULL)
return ;
cmdlist_destroy(cmd->item);
free(cmd);
}
void cmdgroup_debug(t_cmdgroup *cmd, t_buffer *leader, bool is_last)
{
if (cmd == NULL)
return ;
indent(leader, is_last);
ft_printf("%s\n", "t_cmdgroup");
cmdlist_debug(cmd->item, leader, true);
dedent(leader, is_last);
}

View file

@ -1,41 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdgroup.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/11 15:11:57 by khais #+# #+# */
/* Updated: 2025/03/19 12:10:37 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CMDGROUP_H
# define CMDGROUP_H
# include "../wordlist/wordlist.h"
# include "../cmdlist/cmdlist.h"
# include "../../buffer/buffer.h"
/*
** A grouping of commands, surrounded by parentheses.
**
** A top-level cmdgroup is modeled as having an implicit set of parentheses.
*/
typedef struct s_cmdgroup
{
/*
** list of the commands inside this group
*/
struct s_cmdlist *item;
/*
** redirections to apply to the whole group
*/
struct s_redirection_list *redirections;
} t_cmdgroup;
t_cmdgroup *cmdgroup_from_wordlist(t_wordlist *words);
void cmdgroup_destroy(t_cmdgroup *cmd);
void cmdgroup_debug(t_cmdgroup *cmd, t_buffer *leader, bool is_last);
#endif // CMDGROUP_H

View file

@ -1,73 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdgroup_builder.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/20 12:32:14 by khais #+# #+# */
/* Updated: 2025/03/20 17:19:55 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "cmdgroup_builder.h"
#include "libft.h"
/*
** setup a cmdlist_builder by allocating additionall needed memory and
** performing other init steps.
*/
t_cmdgroup_builder *setup_cmdgroup_builder(t_cmdgroup_builder *builder,
t_wordlist **words)
{
ft_bzero(builder, sizeof (*builder));
builder->cmdgroup = ft_calloc(1, sizeof(*builder->cmdgroup));
if (builder->cmdgroup == NULL)
return (NULL);
builder->current_wordlist = NULL;
builder->current_word = wordlist_pop(words);
return (builder);
}
/*
** Advance the state to the next word
*/
void cmdgroup_builder_next_word(t_cmdgroup_builder *builder,
t_wordlist **words)
{
builder->current_wordlist = wordlist_push(builder->current_wordlist,
builder->current_word);
builder->current_word = wordlist_pop(words);
}
/*
** Delimit the cmdlist, setting up the t_cmdlist item.
*/
void cmdgroup_builder_delimit(t_cmdgroup_builder *builder,
t_wordlist **words)
{
builder->cmdgroup->item = cmdlist_from_wordlist(builder->current_wordlist);
wordlist_destroy(builder->current_wordlist);
builder->current_wordlist = NULL;
(void)words;
}
/*
** discard the current word
*/
void cmdgroup_builder_discard_word(t_cmdgroup_builder *builder,
t_wordlist **words)
{
worddesc_destroy(builder->current_word);
builder->current_word = wordlist_pop(words);
}
/*
** Handle encountering a parenthesis token
*/
void cmdgroup_builder_paren(t_cmdgroup_builder *builder,
t_wordlist **words, t_paren paren)
{
cmdgroup_builder_discard_word(builder, words);
(void)paren;
}

View file

@ -1,55 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdgroup_builder.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/20 12:27:20 by khais #+# #+# */
/* Updated: 2025/03/20 17:12:52 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CMDGROUP_BUILDER_H
# define CMDGROUP_BUILDER_H
# include "../wordlist/wordlist.h"
# include "cmdgroup.h"
# include "paren.h"
typedef struct s_cmdgroup_builder
{
/*
** set to true if in an error state
*/
bool error;
/*
** current word that is being examined
*/
t_worddesc *current_word;
/*
** wordlist to be passed to lower stages of parsing
*/
t_wordlist *current_wordlist;
/*
** cmdgroup that is being constructed
*/
t_cmdgroup *cmdgroup;
/*
** count of parenthesis tokens
**
** +1 for each opening parenthesis, -1 for each closing
*/
int paren;
} t_cmdgroup_builder;
t_cmdgroup_builder *setup_cmdgroup_builder(t_cmdgroup_builder *builder,
t_wordlist **words);
void cmdgroup_builder_next_word(t_cmdgroup_builder *builder,
t_wordlist **words);
void cmdgroup_builder_delimit(t_cmdgroup_builder *builder,
t_wordlist **words);
void cmdgroup_builder_paren(t_cmdgroup_builder *builder,
t_wordlist **words, t_paren paren);
#endif // CMDGROUP_BUILDER_H

View file

@ -1,117 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdlist.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/24 17:49:46 by khais #+# #+# */
/* Updated: 2025/03/20 12:23:39 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "cmdlist.h"
#include "cmdlist_item.h"
#include "cmdlist_builder.h"
#include "cmdlist_item_type.h"
#include "operator.h"
#include <unistd.h>
#include <stdlib.h>
/*
** Frees all memory associated with this builder and set error to true.
*/
static void cmdlist_builder_error(t_cmdlist_builder *builder)
{
if (builder == NULL)
return ;
cmdlist_destroy(builder->cmdlist);
builder->cmdlist = NULL;
wordlist_destroy(builder->current_wordlist);
builder->current_wordlist = NULL;
worddesc_destroy(builder->current_word);
builder->current_word = NULL;
builder->error = true;
}
/*
** Delimit the current pipeline, creating a new t_pipeline object.
**
** Sets the operator for that pipeline.
**
** Assumes that the current word is a valid operator.
*/
static void cmdlist_builder_delimit(
t_cmdlist_builder *builder,
t_wordlist **words)
{
builder->cmdlist->cmds[builder->idx] = cmdlist_item_create();
builder->cmdlist->cmds[builder->idx]->inner.pipeline
= pipeline_from_wordlist(builder->current_wordlist);
builder->cmdlist->cmds[builder->idx]->type = TYPE_PIPELINE;
if (builder->cmdlist->cmds[builder->idx]->inner.pipeline == NULL)
{
cmdlist_builder_error(builder);
wordlist_destroy(*words);
return ;
}
if (cmdlist_builder_at_last_pipeline(builder))
builder->cmdlist->operators[builder->idx] = OP_END;
else
builder->cmdlist->operators[builder->idx]
= match_op(builder->current_word->word);
wordlist_destroy(builder->current_wordlist);
builder->current_wordlist = NULL;
worddesc_destroy(builder->current_word);
builder->current_word = wordlist_pop(words);
builder->idx++;
}
/*
** Create a new command list from the given wordlist.
**
** Makes a copy of the given wordlist
*/
t_cmdlist *cmdlist_from_wordlist(const t_wordlist *wordlist)
{
t_cmdlist_builder builder;
t_wordlist *words;
words = wordlist_copy(wordlist);
if (words == NULL)
return (NULL);
if (setup_cmdlist_builder(&builder, &words) == NULL)
return (wordlist_destroy(words), NULL);
while (builder.error == false && builder.current_word != NULL)
{
if (match_op(builder.current_word->word) == OP_INVALID)
cmdlist_builder_next_word(&builder, &words);
else
cmdlist_builder_delimit(&builder, &words);
}
if (builder.current_wordlist != NULL)
cmdlist_builder_delimit(&builder, &words);
if (builder.error == true)
return (NULL);
return (builder.cmdlist);
}
/*
** destroy the given command list and all associated memory
*/
void cmdlist_destroy(t_cmdlist *cmd)
{
int i;
if (cmd == NULL)
return ;
i = 0;
while (i < cmd->num_cmd)
{
cmdlist_item_destroy(cmd->cmds[i]);
i++;
}
free(cmd->cmds);
free(cmd->operators);
free(cmd);
}

View file

@ -1,91 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdlist.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/24 17:45:01 by khais #+# #+# */
/* Updated: 2025/03/19 16:43:14 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CMDLIST_H
# define CMDLIST_H
# include "../wordlist/wordlist.h"
# include "../pipeline/pipeline.h"
# include "operator.h"
# include "../../buffer/buffer.h"
/*
** cf. 3.2.4 Lists of Commands
**
** A list is a sequence of one or more pipelines separated by one of the
** **operators** &&, or ||, and optionally terminated by a newline.
**
** AND and OR lists are sequences of one or more pipelines separated by the
** control operators && and ||, respectively.
**
** AND and OR lists are executed with left associativity.
**
** e.g.
**
** ```shell
** A && B && C
** ```
**
** is the same as
**
** ```shell
** (A && B) && C
** ```
**
** An AND list has the form
**
** ```shell
** A && B
** ```
**
** B is executed if and only if A has an exit status of 0 (succes).
**
** An OR list has the form
**
** ```shell
** A || B
** ```
**
** B is executed if and only if A has a non-zero exit status (failure).
**
** The return status of AND and OR lists is the exit status of the last command
** executed in the list.
*/
typedef struct s_cmdlist
{
/*
** Array of pointers to commands in this command list
**
** These should be executed left to right.
*/
struct s_cmdlist_item **cmds;
/*
** Number of commands in this command list.
*/
int num_cmd;
/*
** Operators that separate the pipelines.
**
** pipelines[0] operators[0] pipelines[1] operators[1]
**
** Terminated by OP_END
**
** In example above, operators[1] == OP_END
*/
t_operator *operators;
} t_cmdlist;
t_cmdlist *cmdlist_from_wordlist(const t_wordlist *wordlist);
void cmdlist_destroy(t_cmdlist *cmd);
void cmdlist_debug(t_cmdlist *cmd, t_buffer *leader, bool is_last);
#endif // CMDLIST_H

View file

@ -1,104 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdlist_builder.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/26 13:51:18 by khais #+# #+# */
/* Updated: 2025/03/19 16:43:14 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "cmdlist_builder.h"
#include "libft.h"
/*
** Count the number of pipelines in the given wordstream
**
** Pipelines are separated by operators || and &&.
**
** Does not do error checking about repeated operators, or operators in the
** wrong place.
*/
static int cmdlist_count_pipelines(t_wordlist *words)
{
t_wordlist *current_word;
int count;
current_word = words;
if (current_word == NULL)
return (0);
count = 1;
while (current_word != NULL)
{
if (match_op(current_word->word->word) != OP_INVALID)
count++;
current_word = current_word->next;
}
return (count);
}
/*
** Allocate memory for a new cmdlist, given the input word stream.
**
** Handles malloc error.
*/
static t_cmdlist *allocate_cmdlist(t_wordlist *words)
{
t_cmdlist *output;
if (words == NULL)
return (NULL);
output = ft_calloc(1, sizeof(t_cmdlist));
if (output == NULL)
return (NULL);
output->num_cmd = cmdlist_count_pipelines(words);
output->cmds
= ft_calloc(output->num_cmd, sizeof(struct s_cmdlist_item **));
if (output->cmds == NULL)
return (free(output), NULL);
output->operators
= ft_calloc(output->num_cmd, sizeof(t_operator));
if (output->operators == NULL)
return (free(output->cmds), free(output), NULL);
return (output);
}
/*
** setup a given cmdlist_builder by allocating additional needed memory, and
** performing other initialization steps.
*/
t_cmdlist_builder *setup_cmdlist_builder(
t_cmdlist_builder *builder,
t_wordlist **words)
{
ft_bzero(builder, sizeof(t_cmdlist_builder));
builder->cmdlist = allocate_cmdlist(*words);
if (builder->cmdlist == NULL)
return (NULL);
builder->current_wordlist = NULL;
builder->current_word = wordlist_pop(words);
builder->idx = 0;
return (builder);
}
/*
** Advance the state to the next word
*/
void cmdlist_builder_next_word(
t_cmdlist_builder *builder,
t_wordlist **words)
{
builder->current_wordlist
= wordlist_push(builder->current_wordlist, builder->current_word);
builder->current_word = wordlist_pop(words);
}
/*
** return true if we are currently adding the last pipeline in the chain
*/
bool cmdlist_builder_at_last_pipeline(t_cmdlist_builder *builder)
{
return (builder->idx == builder->cmdlist->num_cmd - 1);
}

View file

@ -1,52 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdlist_builder.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/26 13:50:25 by khais #+# #+# */
/* Updated: 2025/03/18 15:02:53 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CMDLIST_BUILDER_H
# define CMDLIST_BUILDER_H
# include "cmdlist.h"
typedef struct s_cmdlist_builder
{
/*
** list of cmds that we are building
*/
t_cmdlist *cmdlist;
/*
** wordlist for the next pipeline
*/
t_wordlist *current_wordlist;
/*
** word we are currently examining
*/
t_worddesc *current_word;
/*
** index of the pipeline that is about to be inserted into the cmdlist
*/
int idx;
/*
** set to true in an error was encountered that requires stopping the
** processing.
*/
bool error;
} t_cmdlist_builder;
t_cmdlist_builder *setup_cmdlist_builder(
t_cmdlist_builder *builder,
t_wordlist **words);
bool cmdlist_builder_at_last_pipeline(
t_cmdlist_builder *builder);
void cmdlist_builder_next_word(
t_cmdlist_builder *builder,
t_wordlist **words);
#endif // CMDLIST_BUILDER_H

View file

@ -1,57 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdlist_debug.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/18 15:52:28 by khais #+# #+# */
/* Updated: 2025/03/19 16:43:14 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "cmdlist.h"
#include "../../treedrawing.h"
#include "libft.h"
#include "cmdlist_item.h"
static void cmdlist_array_debug(t_cmdlist *cmd,
int i, t_buffer *leader, bool is_last)
{
indent(leader, is_last);
ft_printf("cmd[%d]\n", i);
cmdlist_item_debug(cmd->cmds[i], leader, false);
operator_debug(cmd->operators[i], leader, true);
dedent(leader, is_last);
}
static void cmdlist_num_cmds_debug(int num_cmds, t_buffer *leader, bool is_last)
{
indent(leader, is_last);
ft_printf("num_cmds = %d\n", num_cmds);
dedent(leader, is_last);
}
void cmdlist_debug(t_cmdlist *cmd, t_buffer *leader, bool is_last)
{
int i;
bool last;
if (cmd == NULL)
return ;
indent(leader, is_last);
ft_printf("%s\n", "t_cmdlist");
i = 0;
last = false;
if (cmd->num_cmd == 0)
last = true;
cmdlist_num_cmds_debug(cmd->num_cmd, leader, last);
while (i < cmd->num_cmd)
{
if (i == cmd->num_cmd - 1)
last = true;
cmdlist_array_debug(cmd, i, leader, last);
i++;
}
dedent(leader, is_last);
}

View file

@ -1,57 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdlist_item.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/18 13:02:02 by khais #+# #+# */
/* Updated: 2025/03/19 14:11:27 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "cmdlist_item.h"
#include "cmdlist_item_type.h"
#include "libft.h"
#include "../../treedrawing.h"
void cmdlist_item_destroy(t_cmdlist_item *cmd)
{
if (cmd == NULL)
return ;
if (cmd->type == TYPE_CMDGROUP)
cmdgroup_destroy(cmd->inner.cmdgroup);
if (cmd->type == TYPE_PIPELINE)
pipeline_destroy(cmd->inner.pipeline);
free(cmd);
}
t_cmdlist_item *cmdlist_item_create(void)
{
t_cmdlist_item *item;
item = ft_calloc(1, sizeof(t_cmdlist_item));
if (item == NULL)
return (NULL);
item->type = TYPE_INVALID;
return (item);
}
void cmdlist_item_debug(t_cmdlist_item *item, t_buffer *leader, bool is_last)
{
if (item == NULL)
return ;
indent(leader, is_last);
ft_printf("%s\n", "t_cmdlist_item");
if (item->type == TYPE_INVALID)
{
indent(leader, true);
ft_printf("%s\n", "type = INVALID");
dedent(leader, true);
}
else if (item->type == TYPE_PIPELINE)
pipeline_debug(item->inner.pipeline, leader, true);
else if (item->type == TYPE_CMDGROUP)
cmdgroup_debug(item->inner.cmdgroup, leader, true);
dedent(leader, is_last);
}

View file

@ -0,0 +1,51 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* connec_cmd_debug.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 17:47:18 by khais #+# #+# */
/* Updated: 2025/04/09 17:49:02 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "connec_cmd_debug.h"
#include "../../treedrawing.h"
#include "libft.h"
#include "../cmd/cmd_debug.h"
static void connector_debug(t_connector connector, t_buffer *leader,
bool is_last)
{
indent(leader, is_last);
ft_printf("t_connector = ");
if (connector == FT_PIPE)
ft_printf("FT_PIPE\n");
if (connector == FT_AND)
ft_printf("FT_AND\n");
if (connector == FT_OR)
ft_printf("FT_OR\n");
dedent(leader, is_last);
}
void connec_cmd_debug(t_connec_cmd *cmd, t_buffer *leader, bool is_last)
{
indent(leader, is_last);
if (cmd == NULL)
ft_printf("t_connec_cmd = NULL\n");
else
{
ft_printf("t_connec_cmd\n");
indent(leader, false);
ft_printf("first\n");
cmd_debug(cmd->first, leader, true);
dedent(leader, false);
indent(leader, false);
ft_printf("second\n");
cmd_debug(cmd->second, leader, true);
dedent(leader, false);
connector_debug(cmd->connector, leader, true);
}
dedent(leader, is_last);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* connec_cmd_debug.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 17:46:51 by khais #+# #+# */
/* Updated: 2025/04/09 17:48:30 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CONNEC_CMD_DEBUG_H
# define CONNEC_CMD_DEBUG_H
# include "../../minishell.h"
void connec_cmd_debug(t_connec_cmd *cmd, t_buffer *leader, bool is_last);
#endif // CONNEC_CMD_DEBUG_H

View file

@ -0,0 +1,42 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* connec_reorient_subtree.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 11:37:35 by khais #+# #+# */
/* Updated: 2025/04/15 11:39:41 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "connec_reorient_subtree.h"
#include "../cmd/cmd.h"
#include "../cmd/cmd_destroy.h"
#include "../../ft_errno.h"
t_cmd *connec_reorient_subtree(t_cmd **list, t_cmd **subtree,
t_cmd **opt, t_connector connec)
{
*list = cmd_create(FT_CONNECTION);
if (!*list)
{
cmd_destroy(*subtree);
cmd_destroy(*opt);
return (NULL);
}
(*list)->value.connection = ft_calloc(1, sizeof(t_connec_cmd));
if (!(*list)->value.connection)
{
ft_errno(FT_ENOMEM);
cmd_destroy(*subtree);
cmd_destroy(*opt);
return (free(*list), NULL);
}
(*list)->type = FT_CONNECTION;
(*list)->value.connection->connector = connec;
(*list)->value.connection->first = *subtree;
(*list)->value.connection->second = *opt;
*subtree = *list;
return (*subtree);
}

View file

@ -0,0 +1,21 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* connec_reorient_subtree.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 11:37:48 by khais #+# #+# */
/* Updated: 2025/04/15 11:40:02 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CONNEC_REORIENT_SUBTREE_H
# define CONNEC_REORIENT_SUBTREE_H
# include "../../minishell.h"
t_cmd *connec_reorient_subtree(t_cmd **list, t_cmd **subtree,
t_cmd **opt, t_connector connec);
#endif // CONNEC_REORIENT_SUBTREE_H

View file

@ -1,43 +1,32 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* operator.c :+: :+: :+: */
/* group_cmd_debug.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/26 12:51:28 by khais #+# #+# */
/* Updated: 2025/03/19 14:44:03 by khais ### ########.fr */
/* Created: 2025/04/09 17:43:56 by khais #+# #+# */
/* Updated: 2025/04/14 15:06:36 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "operator.h"
#include "libft.h"
#include "group_cmd_debug.h"
#include "../../treedrawing.h"
#include "libft.h"
#include "../cmd/cmd_debug.h"
#include "../redirect/redirect_debug.h"
/*
** Match a string to an operator
**
** If the string does not match any operator, return OP_INVALID
*/
t_operator match_op(char *op)
{
if (ft_strcmp("&&", op) == 0)
return (OP_AND);
if (ft_strcmp("||", op) == 0)
return (OP_OR);
return (OP_INVALID);
}
void operator_debug(t_operator op, t_buffer *leader,
bool is_last)
void group_cmd_debug(t_group_cmd *cmd, t_buffer *leader, bool is_last)
{
indent(leader, is_last);
ft_printf("t_operator = ");
if (op == OP_AND)
ft_printf("%s\n", "AND");
if (op == OP_OR)
ft_printf("%s\n", "OR");
if (op == OP_END)
ft_printf("%s\n", "END");
ft_printf("t_group_cmd\n");
indent(leader, false);
ft_printf("cmd\n");
cmd_debug(cmd->cmd, leader, false);
dedent(leader, false);
indent(leader, true);
ft_printf("redirect\n");
redirect_debug(cmd->redirects, leader, true);
dedent(leader, true);
dedent(leader, is_last);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* group_cmd_debug.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 17:43:25 by khais #+# #+# */
/* Updated: 2025/04/09 17:45:20 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef GROUP_CMD_DEBUG_H
# define GROUP_CMD_DEBUG_H
# include "../../minishell.h"
void group_cmd_debug(t_group_cmd *cmd, t_buffer *leader, bool is_last);
#endif // GROUP_CMD_DEBUG_H

View file

@ -0,0 +1,43 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* group_cmd_parse.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 10:46:28 by khais #+# #+# */
/* Updated: 2025/04/15 10:47:19 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "group_cmd_parse.h"
#include "../cmd_parsing.h"
#include "../cmd/cmd_destroy.h"
#include "../cmd/cmd.h"
#include "../redirect/redirect_parse.h"
t_cmd *minishell_group_cmd_parse(t_minishell *app, t_wordlist *tokens)
{
t_cmd *subtree;
t_cmd *group;
subtree = minishell_cmds_parse(app, tokens);
if (!subtree
|| tokens->word->token_type != CLOSE_PARENTH_TOKEN)
{
ft_errno(FT_EERRNO);
if (tokens->word->token_type != CLOSE_PARENTH_TOKEN)
parse_error(app, tokens->word);
return (cmd_destroy(subtree), NULL);
}
worddesc_destroy(wordlist_pop(&tokens));
group = cmd_create(FT_GROUP);
if (!group)
return (cmd_destroy(subtree), NULL);
group->value.group->cmd = subtree;
group->value.group->redirects = minishell_redirect_parse(app, tokens);
if (group->value.group->redirects == NULL
&& ft_errno_get() != FT_ESUCCESS)
return (cmd_destroy(group), NULL);
return (group);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* group_cmd_parse.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 10:45:49 by khais #+# #+# */
/* Updated: 2025/04/15 10:46:15 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef GROUP_CMD_PARSE_H
# define GROUP_CMD_PARSE_H
# include "../../minishell.h"
t_cmd *minishell_group_cmd_parse(t_minishell *app, t_wordlist *tokens);
#endif // GROUP_CMD_PARSE_H

View file

@ -0,0 +1,33 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* optional_pipeline_parse.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 10:43:04 by khais #+# #+# */
/* Updated: 2025/04/15 10:43:40 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "optional_pipeline_parse.h"
#include "../../ft_errno.h"
#include "../cmd_parsing.h"
t_cmd *minishell_optional_pipeline_parse(t_minishell *app, t_wordlist *tokens)
{
t_cmd *opt;
t_worddesc *token;
token = tokens->word;
if (token->token_type == PIPE_TOKEN)
{
token = wordlist_pop(&tokens);
worddesc_destroy(token);
opt = minishell_group_or_simple_parse(app, tokens);
if (!opt)
ft_errno(FT_EERRNO);
return (opt);
}
return (NULL);
}

View file

@ -0,0 +1,21 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* optional_pipeline_parse.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 10:42:34 by khais #+# #+# */
/* Updated: 2025/04/15 10:42:57 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef OPTIONAL_PIPELINE_PARSE_H
# define OPTIONAL_PIPELINE_PARSE_H
# include "../../minishell.h"
t_cmd *minishell_optional_pipeline_parse(t_minishell *app,
t_wordlist *tokens);
#endif // OPTIONAL_PIPELINE_PARSE_H

View file

@ -1,77 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* pipeline.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/21 13:23:50 by khais #+# #+# */
/* Updated: 2025/03/19 14:38:58 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "pipeline.h"
#include "pipeline_parse.h"
#include "libft.h"
/*
** Parse a pipeline from the given wordlist.
**
** Return NULL and set ft_errno in case of error.
**
** Makes a copy of words
*/
t_pipeline *pipeline_from_wordlist(const t_wordlist *words)
{
t_pipeline *pipeline;
t_pipeline_builder builder;
if (words == NULL)
return (NULL);
pipeline = ft_calloc(1, sizeof(t_pipeline));
if (pipeline == NULL)
return (NULL);
pipeline->num_cmd = pipeline_count_cmds(words);
pipeline->cmds = ft_calloc(pipeline->num_cmd, sizeof(t_simple_cmd *));
if (pipeline->cmds == NULL)
return (NULL);
ft_bzero(&builder, sizeof(t_pipeline_builder));
builder.words = wordlist_copy(words);
builder.pipeline = pipeline;
pipeline = pipeline_parse_wordlist(&builder);
return (pipeline);
}
/*
** Destroy the given pipeline, freeing all memory.
*/
void pipeline_destroy(t_pipeline *pipeline)
{
int i;
if (pipeline == NULL)
return ;
i = 0;
while (i < pipeline->num_cmd)
{
simple_cmd_destroy(pipeline->cmds[i]);
i++;
}
free(pipeline->cmds);
free(pipeline);
}
/*
** Free all memory for the given builder, set freed variables to NULL, and set
** error to true
*/
void builder_destroy(t_pipeline_builder *builder)
{
pipeline_destroy(builder->pipeline);
builder->pipeline = NULL;
worddesc_destroy(builder->current_word);
builder->current_word = NULL;
wordlist_destroy(builder->words);
builder->words = NULL;
builder->error = true;
}

View file

@ -1,101 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* pipeline.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/21 13:19:51 by khais #+# #+# */
/* Updated: 2025/03/19 14:40:08 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef PIPELINE_H
# define PIPELINE_H
# include "../simple_cmd/simple_cmd.h"
# include "../../buffer/buffer.h"
# include <stdbool.h>
/*
** cf. 3.2.3 Pipelines
**
** A pipeline is a sequence of one or more commands separated by the control
** operator '|'.
**
** The output of each command in the pipeline is connected via a pipe to the
** input of the next command.
**
** That is, each command reads the previous commands output.
**
** This connection is performed before any redirections specified by the first
** command.
**
** The shell waits for all commands in the pipeline to complete before reading
** the next command.
**
** Each command in a multi-command pipeline, where pipes are created, is
** executed in its own _subshell_, which is a separate process.
**
** e.g.
**
** ```shell
** export TT=1 | echo $TT
** ```
**
** prints an empty string, because TT is unset in the second subshell.
**
** The exit status of a pipeline is the exit status of the last command in the
** pipeline.
**
** The shell waits for all commands in the pipeline to terminate before
** returning a value.
*/
typedef struct s_pipeline
{
/*
** The commands in the pipeline
*/
t_simple_cmd **cmds;
/*
** The number of commands in this pipeline
*/
int num_cmd;
} t_pipeline;
typedef struct s_pipeline_builder
{
/*
** index of the command we are currently working on
*/
int idx;
/*
** wordlist that is being built up to be the wordlist of the command being
** parsed
*/
t_wordlist *current_wordlist;
/*
** word that is currently being considered
*/
t_worddesc *current_word;
/*
** linked list of words that are being parsed
*/
t_wordlist *words;
/*
** pipeline that is currently being constructed
*/
t_pipeline *pipeline;
/*
** true if an error occured, which should interrupt processing
*/
bool error;
} t_pipeline_builder;
t_pipeline *pipeline_from_wordlist(const t_wordlist *words);
void pipeline_destroy(t_pipeline *pipeline);
void builder_destroy(t_pipeline_builder *builder);
void pipeline_debug(t_pipeline *pipeline, t_buffer *leader,
bool is_last);
#endif

View file

@ -1,56 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* pipeline_debug.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/19 14:38:51 by khais #+# #+# */
/* Updated: 2025/03/19 14:39:55 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "libft.h"
#include "../../treedrawing.h"
#include "pipeline.h"
static void pipeline_num_cmds_debug(int num_cmds, t_buffer *leader,
bool is_last)
{
indent(leader, is_last);
ft_printf("num_cmd = %d\n", num_cmds);
dedent(leader, is_last);
}
static void pipeline_array_debug(t_pipeline *pipeline,
int i, t_buffer *leader, bool is_last)
{
indent(leader, is_last);
ft_printf("cmd[%d]\n", i);
simple_cmd_debug(pipeline->cmds[i], leader, true);
dedent(leader, is_last);
}
void pipeline_debug(t_pipeline *pipeline, t_buffer *leader, bool is_last)
{
int i;
bool last;
if (pipeline == NULL)
return ;
indent(leader, is_last);
ft_printf("%s\n", "t_pipeline");
i = 0;
last = false;
if (pipeline->num_cmd == 0)
last = true;
pipeline_num_cmds_debug(pipeline->num_cmd, leader, last);
while (i < pipeline->num_cmd)
{
if (i == pipeline->num_cmd - 1)
last = true;
pipeline_array_debug(pipeline, i, leader, last);
i++;
}
dedent(leader, is_last);
}

View file

@ -5,123 +5,41 @@
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/24 14:36:43 by khais #+# #+# */
/* Updated: 2025/03/04 13:19:46 by khais ### ########.fr */
/* Created: 2025/04/15 11:35:08 by khais #+# #+# */
/* Updated: 2025/04/15 11:38:39 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "pipeline_parse.h"
#include "ft_printf.h"
#include "pipeline.h"
#include "pipeline_parse_baseops.h"
#include "../../ft_errno.h"
#include "../matchers/pipe.h"
#include "unistd.h"
#include "libft.h"
#include <stdlib.h>
#include "optional_pipeline_parse.h"
#include "../cmd_parsing.h"
#include "../cmd/cmd_destroy.h"
#include "../connec_cmd/connec_reorient_subtree.h"
/*
** count the number of commands in a pipeline from the given words.
**
** A command is separated from the next by a pipe token.
**
** Note that this does not handle errors such as repeated pipe tokens, or pipe
** tokens at the wrong place.
*/
int pipeline_count_cmds(const t_wordlist *words)
t_cmd *minishell_pipeline_parse(t_minishell *app, t_wordlist *tokens)
{
int count;
t_cmd *subtree;
t_cmd *opt;
t_cmd *pipeline;
if (words == NULL)
return (0);
count = 1;
while (words != NULL)
subtree = minishell_group_or_simple_parse(app, tokens);
if (!subtree)
return (NULL);
opt = minishell_optional_pipeline_parse(app, tokens);
if (!opt && ft_errno_get() != FT_ESUCCESS)
return (NULL);
if (!opt)
return (subtree);
while (opt)
{
if (is_pipe(words->word))
count++;
words = words->next;
}
return (count);
}
/*
** Delimit a command in the pipeline.
**
** - Create a new simple command from the wordlist that has been accumulated
** thus far.
** - Reset the current wordlist.
** - Ignore the current word, which is assumed to be a pipe token.
** - Check if the new current word is a pipe token, and if it is, signal
** FT_EUNEXPECTED_PIPE.
**
** Destroys the builder and sets error in case of error.
*/
static void pipeline_delimit(t_pipeline_builder *builder)
{
builder->pipeline->cmds[builder->idx]
= simple_cmd_from_wordlist(builder->current_wordlist);
if (builder->pipeline->cmds[builder->idx] == NULL)
{
builder_destroy(builder);
return ;
}
builder->current_wordlist = NULL;
ignore_word(builder);
if (current_is_pipe(builder))
{
builder_destroy(builder);
ft_errno(FT_EUNEXPECTED_PIPE);
return ;
}
builder->idx++;
}
/*
** Parse a pipeline from a wordlist using the given builder.
**
** - Assumes that current word is unset.
** - Load the next word.
** - If the current word is a pipe, it means there was a pipe on the start of
** the command, which is disalowed. set ft_errno and return NULL.
** - While there are still words remaining in the input stream
** - If we have a pipe character
** - Delimit the current command
** - If there was an error
** - Return NULL
** - If we reached the end of input stream
** - This means that there is a hanging pipe at the end of the pipeline.
** Destroy the pipeline, set ft_errno, and return NULL.
** - Push the current word to the accumulator for the next command
** - Advance the state to the next word
** - Since there is no pipe token at the end to delimit the last command,
** delimit it now
** - Return the pipeline. Since all input tokens have been consumed, no
** additional memory freeing is required.
** in case of error in pipeline_delimit, it will call destroy, which will set
** builder->pipeline to NULL
*/
t_pipeline *pipeline_parse_wordlist(t_pipeline_builder *builder)
{
next_word(builder);
if (current_is_pipe(builder))
return (builder_destroy(builder), ft_errno(FT_EUNEXPECTED_PIPE), NULL);
while (!eof(builder))
{
if (current_is_pipe(builder))
if (connec_reorient_subtree(&pipeline, &subtree, &opt, FT_PIPE) == NULL)
{
pipeline_delimit(builder);
if (builder->error)
return (NULL);
if (eof(builder))
{
builder_destroy(builder);
ft_errno(FT_EUNEXPECTED_PIPE);
return (NULL);
}
app->last_return_value = 1;
return (ft_perror("minishell_pipeline_parse"), NULL);
}
push_word(builder);
next_word(builder);
opt = minishell_optional_pipeline_parse(app, tokens);
}
pipeline_delimit(builder);
return (builder->pipeline);
if (ft_errno_get() != FT_ESUCCESS)
return (cmd_destroy(subtree), NULL);
return (pipeline);
}

View file

@ -5,17 +5,16 @@
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/24 14:34:55 by khais #+# #+# */
/* Updated: 2025/03/04 12:07:12 by khais ### ########.fr */
/* Created: 2025/04/15 11:34:38 by khais #+# #+# */
/* Updated: 2025/04/15 11:35:00 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef PIPELINE_PARSE_H
# define PIPELINE_PARSE_H
# include "pipeline.h"
# include "../../minishell.h"
int pipeline_count_cmds(const t_wordlist *words);
t_pipeline *pipeline_parse_wordlist(t_pipeline_builder *builder);
t_cmd *minishell_pipeline_parse(t_minishell *app, t_wordlist *tokens);
#endif // PIPELINE_PARSE_H

View file

@ -1,65 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* pipeline_parse_baseops.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/24 15:06:33 by khais #+# #+# */
/* Updated: 2025/02/24 15:10:09 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "pipeline_parse_baseops.h"
#include "../matchers/pipe.h"
#include <stdlib.h>
/*
** Advance the state to the next word
*/
void next_word(t_pipeline_builder *builder)
{
builder->current_word = wordlist_pop(&builder->words);
}
/*
** If the current word is NULL, return true.
**
** Used to detect end of word stream.
**
** Assumes that next_word was previously called.
*/
bool eof(t_pipeline_builder *builder)
{
return (builder->current_word == NULL);
}
/*
** return true if the given current word is a pipe operator "|"
*/
bool current_is_pipe(t_pipeline_builder *builder)
{
return (builder->current_word != NULL && is_pipe(builder->current_word));
}
/*
** Remove the current word from the wordstream and advance to the next word.
*/
void ignore_word(t_pipeline_builder *builder)
{
worddesc_destroy(builder->current_word);
next_word(builder);
}
/*
** push the current word to the wordlist that will be used for the next simple
** command.
**
** Resets current word to NULL.
*/
void push_word(t_pipeline_builder *builder)
{
builder->current_wordlist
= wordlist_push(builder->current_wordlist, builder->current_word);
builder->current_word = NULL;
}

View file

@ -1,34 +1,37 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* cmdlist_item.h :+: :+: :+: */
/* redirect.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/18 12:54:31 by khais #+# #+# */
/* Updated: 2025/03/19 14:11:16 by khais ### ########.fr */
/* Created: 2025/04/14 17:27:53 by khais #+# #+# */
/* Updated: 2025/04/14 17:33:09 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CMDLIST_ITEM_H
# define CMDLIST_ITEM_H
#include "redirect.h"
# include "../cmdgroup/cmdgroup.h"
# include "cmdlist_item_type.h"
typedef struct s_cmdlist_item
t_redirect *t_redirect_add_back(t_redirect **init, t_redirect *back)
{
enum e_cmdlist_item_type type;
union u_cmdlist_item_inner
t_redirect *tmp;
if (!(*init))
{
t_cmdgroup *cmdgroup;
struct s_pipeline *pipeline;
} inner;
} t_cmdlist_item;
*init = back;
return (*init);
}
tmp = *init;
while (tmp->next)
tmp = tmp->next;
tmp->next = back;
return (*init);
}
void cmdlist_item_destroy(t_cmdlist_item *cmd);
t_cmdlist_item *cmdlist_item_create(void);
void cmdlist_item_debug(t_cmdlist_item *item, t_buffer *leader,
bool is_last);
#endif // CMDLIST_ITEM_H
bool is_redir(t_worddesc *token)
{
return (token->token_type == OUT_APPEND_REDIR_TOKEN
|| token->token_type == OUT_TRUNC_REDIR_TOKEN
|| token->token_type == IN_REDIR_TOKEN
|| token->token_type == HERE_DOC_REDIR_TOKEN);
}

View file

@ -0,0 +1,21 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* redirect.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/14 17:27:27 by khais #+# #+# */
/* Updated: 2025/04/14 17:32:01 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef REDIRECT_H
# define REDIRECT_H
# include "../../minishell.h"
t_redirect *t_redirect_add_back(t_redirect **init, t_redirect *back);
bool is_redir(t_worddesc *token);
#endif // REDIRECT_H

View file

@ -0,0 +1,94 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* redirect_debug.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 15:07:58 by khais #+# #+# */
/* Updated: 2025/04/14 17:23:33 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "redirect_debug.h"
#include "../../treedrawing.h"
#include "libft.h"
static void redir_type_debug(t_redir_type type, t_buffer *leader, bool is_last)
{
indent(leader, is_last);
if (type == FT_INVALID_REDIR)
ft_printf("redir_type = %s\n", "FT_INVALID_REDIR");
if (type == FT_OUTPUT_TRUNC_REDIR)
ft_printf("redir_type = %s\n", "FT_OUTPUT_TRUNC_REDIR");
if (type == FT_OUTPUT_APPEND_REDIR)
ft_printf("redir_type = %s\n", "FT_OUTPUT_APPEND_REDIR");
if (type == FT_INPUT_REDIR)
ft_printf("redir_type = %s\n", "FT_INPUT_REDIR");
if (type == FT_HEREDOC)
ft_printf("redir_type = %s\n", "FT_HEREDOC");
dedent(leader, is_last);
}
static void redirectee_debug(t_redirectee redirectee, t_buffer *leader,
bool is_last)
{
indent(leader, is_last);
ft_printf("t_redirectee\n");
indent(leader, false);
ft_printf("dest = %d\n", redirectee.dest);
dedent(leader, false);
worddesc_debug(redirectee.filename, leader, true);
dedent(leader, is_last);
}
static void single_redirect_debug(t_redirect *redirect, t_buffer *leader,
bool is_last)
{
if (redirect == NULL)
return ;
indent(leader, is_last);
ft_printf("t_redirect\n");
redir_type_debug(redirect->type, leader, false);
indent(leader, false);
ft_printf("source = %d\n", redirect->source);
dedent(leader, false);
indent(leader, false);
ft_printf("open_flags = %d\n", redirect->open_flags);
dedent(leader, false);
indent(leader, false);
ft_printf("c_flags = %d\n", redirect->c_flags);
dedent(leader, false);
redirectee_debug(redirect->redirectee, leader, false);
indent(leader, true);
ft_printf("c_flags = [%s]\n", redirect->here_doc_eof);
ft_printf("here_doc_eof = [%s]\n", redirect->here_doc_eof);
dedent(leader, true);
dedent(leader, is_last);
}
void redirect_debug(t_redirect *redirect, t_buffer *leader, bool is_last)
{
int i;
indent(leader, is_last);
if (redirect == NULL)
ft_printf("redirections = (empty redir list)\n");
else
{
single_redirect_debug(redirect, leader, redirect->next == NULL);
redirect = redirect->next;
ft_printf("redirections\n");
i = 0;
while (redirect != NULL)
{
indent(leader, redirect->next == NULL);
ft_printf("redirections[%d]\n", i);
single_redirect_debug(redirect, leader, true);
dedent(leader, redirect->next == NULL);
redirect = redirect->next;
i++;
}
}
dedent(leader, is_last);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* redirect_debug.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 15:06:43 by khais #+# #+# */
/* Updated: 2025/04/14 17:22:50 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef REDIRECT_DEBUG_H
# define REDIRECT_DEBUG_H
# include "../../minishell.h"
void redirect_debug(t_redirect *redirect, t_buffer *leader, bool is_last);
#endif // REDIRECT_DEBUG_H

View file

@ -0,0 +1,73 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* redirect_from_words.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/14 17:31:35 by khais #+# #+# */
/* Updated: 2025/04/14 17:32:47 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "redirect_from_words.h"
#include "../../ft_errno.h"
#include "../../executing/here_doc/here_doc.h"
static t_redir_type redir_type_from_token_type(t_token_type type)
{
if (type == OUT_APPEND_REDIR_TOKEN)
return (FT_OUTPUT_APPEND_REDIR);
if (type == OUT_TRUNC_REDIR_TOKEN)
return (FT_OUTPUT_TRUNC_REDIR);
if (type == IN_REDIR_TOKEN)
return (FT_INPUT_REDIR);
if (type == HERE_DOC_REDIR_TOKEN)
return (FT_HEREDOC);
return (FT_INVALID_REDIR);
}
static int redir_source_from_type(t_redir_type type)
{
if (type == FT_OUTPUT_APPEND_REDIR || type == FT_OUTPUT_TRUNC_REDIR)
return (STDOUT_FILENO);
if (type == FT_INPUT_REDIR)
return (STDIN_FILENO);
return (-1);
}
static int redir_open_flags_from_type(t_redir_type type)
{
if (type == FT_OUTPUT_APPEND_REDIR)
return (O_CREAT | O_APPEND | O_WRONLY);
if (type == FT_OUTPUT_TRUNC_REDIR)
return (O_CREAT | O_TRUNC | O_WRONLY);
return (0);
}
t_redirect *redir_from_words(t_worddesc *operator,
t_worddesc *specifier, t_minishell *app)
{
t_redirect *redir;
if (operator == NULL || specifier == NULL)
return (NULL);
redir = ft_calloc(1, sizeof(t_redirect));
if (redir == NULL)
return (ft_errno(FT_EERRNO), NULL);
redir->type = redir_type_from_token_type(operator->token_type);
redir->source = redir_source_from_type(redir->type);
redir->open_flags = redir_open_flags_from_type(redir->type);
redir->c_flags = 0644;
redir->redirectee.dest = -1;
if (redir->type == FT_HEREDOC)
{
redir->here_doc_eof = ft_strdup(specifier->word);
redir->redirectee.dest = here_doc(specifier, STDIN_FILENO, app);
worddesc_destroy(specifier);
}
else
redir->redirectee.filename = specifier;
worddesc_destroy(operator);
return (redir);
}

View file

@ -0,0 +1,21 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* redirect_from_words.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/14 17:31:23 by khais #+# #+# */
/* Updated: 2025/04/14 17:32:12 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef REDIRECT_FROM_WORDS_H
# define REDIRECT_FROM_WORDS_H
# include "../../minishell.h"
t_redirect *redir_from_words(t_worddesc *operator,
t_worddesc *specifier, t_minishell *app);
#endif // REDIRECT_FROM_WORDS_H

View file

@ -0,0 +1,41 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* redirect_parse.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 10:13:58 by khais #+# #+# */
/* Updated: 2025/04/15 10:15:08 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "redirect_parse.h"
#include "redirect_from_words.h"
#include "redirect.h"
#include "../../ft_errno.h"
#include "../cmd/cmd_destroy.h"
t_redirect *minishell_redirect_parse(t_minishell *app, t_wordlist *tokens)
{
t_redirect *redir_list;
t_worddesc *redir_operator;
t_worddesc *redir_specifier;
t_redirect *new_redir;
redir_list = NULL;
while (is_redir(tokens->word))
{
if (tokens->next == NULL)
{
ft_errno(FT_EERRNO);
return (redirect_destroy(redir_list), NULL);
}
redir_operator = wordlist_pop(&tokens);
redir_specifier = wordlist_pop(&tokens);
new_redir = redir_from_words(redir_operator, redir_specifier, app);
t_redirect_add_back(&redir_list, new_redir);
}
ft_errno(FT_ESUCCESS);
return (redir_list);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* redirect_parse.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 10:13:28 by khais #+# #+# */
/* Updated: 2025/04/15 10:14:13 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef REDIRECT_PARSE_H
# define REDIRECT_PARSE_H
# include "../../minishell.h"
t_redirect *minishell_redirect_parse(t_minishell *app, t_wordlist *tokens);
#endif // REDIRECT_PARSE_H

View file

@ -6,12 +6,11 @@
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/20 17:36:20 by khais #+# #+# */
/* Updated: 2025/03/27 14:27:14 by khais ### ########.fr */
/* Updated: 2025/04/15 11:12:11 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "remove_quotes.h"
#include "../cmdlist/cmdlist_item.h"
/*
** do quote removal on all words of this simple cmd
@ -38,75 +37,3 @@ t_simple_cmd *simple_cmd_remove_quotes(t_simple_cmd *cmd)
cmd->words = new;
return (cmd);
}
/*
** do quote removal on all simple_cmds within this pipeline
*/
static t_pipeline *pipeline_remove_quotes(t_pipeline *pipeline)
{
int i;
if (pipeline == NULL)
return (NULL);
i = 0;
while (i < pipeline->num_cmd)
{
if (simple_cmd_remove_quotes(pipeline->cmds[i]) == NULL)
return (NULL);
i++;
}
return (pipeline);
}
/*
** do quote removal on the contained pipeline or cmdgroup
*/
static t_cmdlist_item *cmdlist_item_remove_quotes(t_cmdlist_item *item)
{
if (item == NULL)
return (NULL);
if (item->type == TYPE_PIPELINE)
{
if (pipeline_remove_quotes(item->inner.pipeline) == NULL)
return (NULL);
}
if (item->type == TYPE_CMDGROUP)
{
if (cmdgroup_remove_quotes(item->inner.cmdgroup) == NULL)
return (NULL);
}
return (item);
}
/*
** iterate over all cmdlist_items within this cmdlist, and do quote removal on
** them
*/
static t_cmdlist *cmdlist_remove_quotes(t_cmdlist *cmd)
{
int i;
if (cmd == NULL)
return (NULL);
i = 0;
while (i < cmd->num_cmd)
{
if (cmdlist_item_remove_quotes(cmd->cmds[i]) == NULL)
return (NULL);
i++;
}
return (cmd);
}
/*
** Iterate over all simple_cmd contained within this cmdgroup, and do quote
** removal on them.
*/
t_cmdgroup *cmdgroup_remove_quotes(t_cmdgroup *cmd)
{
if (cmd == NULL)
return (NULL);
if (cmdlist_remove_quotes(cmd->item) == NULL)
return (NULL);
return (cmd);
}

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* remove_quotes.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/28 13:52:02 by khais #+# #+# */
/* Updated: 2025/03/07 11:20:48 by khais ### ########.fr */
/* Updated: 2025/03/28 16:05:25 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -15,6 +15,13 @@
#include "../matchers/quote.h"
#include <stdlib.h>
static bool is_ignore_mark(char c)
{
if (c == '\'' || c == '"' || c == '$' || c == '&')
return (true);
return (false);
}
/*
** use the marker in the given worddesc to create a new worddesc by removing
** unquoted quotes.
@ -37,7 +44,7 @@ t_worddesc *remove_quotes(t_worddesc *word)
i = 0;
while (word->word[i] != '\0')
{
if (!is_quote(word->word[i]) || is_quote(word->marker[i]))
if (!is_quote(word->word[i]) || is_ignore_mark(word->marker[i]))
buf = ft_buffer_pushchar(buf, word->word[i]);
i++;
}

View file

@ -6,7 +6,7 @@
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/28 13:51:03 by khais #+# #+# */
/* Updated: 2025/03/27 14:27:27 by khais ### ########.fr */
/* Updated: 2025/04/15 11:10:31 by khais ### ########.fr */
/* */
/* ************************************************************************** */
@ -14,10 +14,9 @@
# define REMOVE_QUOTES_H
# include "../worddesc/worddesc.h"
# include "../cmdgroup/cmdgroup.h"
# include "../../minishell.h"
t_worddesc *remove_quotes(t_worddesc *word);
t_cmdgroup *cmdgroup_remove_quotes(t_cmdgroup *cmd);
t_simple_cmd *simple_cmd_remove_quotes(t_simple_cmd *cmd);
#endif // REMOVE_QUOTES_H

View file

@ -6,13 +6,15 @@
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/21 12:30:07 by khais #+# #+# */
/* Updated: 2025/03/28 15:02:17 by khais ### ########.fr */
/* Updated: 2025/04/15 14:37:57 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "simple_cmd.h"
#include "libft.h"
#include "../../treedrawing.h"
#include "../cmd/cmd_destroy.h"
#include "../redirect/redirect_debug.h"
/*
** parse a wordlist and yield a simple command.
@ -35,6 +37,7 @@ void simple_cmd_destroy(t_simple_cmd *cmd)
if (cmd == NULL)
return ;
wordlist_destroy(cmd->words);
redirect_destroy(cmd->redirections);
free(cmd);
}
@ -44,9 +47,7 @@ void simple_cmd_debug(t_simple_cmd *cmd, t_buffer *leader, bool is_last)
return ;
indent(leader, is_last);
ft_printf("%s\n", "t_simple_cmd");
indent(leader, false);
ft_printf("words = ");
wordlist_debug(cmd->words);
dedent(leader, true);
redirect_debug(cmd->redirections, leader, false);
wordlist_debug(cmd->words, leader, true);
dedent(leader, is_last);
}

View file

@ -0,0 +1,47 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* simple_cmd_parse.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 10:38:47 by khais #+# #+# */
/* Updated: 2025/04/15 10:40:08 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "simple_cmd_parse.h"
#include "../../ft_errno.h"
#include "../cmd/cmd.h"
#include "../redirect/redirect_parse.h"
#include "../cmd/cmd_destroy.h"
#include "../cmd_parsing.h"
t_cmd *minishell_simple_cmd_parse(t_minishell *app, t_wordlist *tokens)
{
t_cmd *simple;
t_redirect *redir;
simple = cmd_create(FT_SIMPLE);
if (simple == NULL)
return (NULL);
simple->value.simple = ft_calloc(1, sizeof(t_simple_cmd));
if (simple->value.simple == NULL)
return (ft_errno(FT_ENOMEM), free(simple), NULL);
redir = minishell_redirect_parse(app, tokens);
t_redirect_add_back(&simple->value.simple->redirections, redir);
if (ft_errno_get() != FT_ESUCCESS)
return (cmd_destroy(simple), NULL);
while (tokens && tokens->word->token_type == WORD_TOKEN)
{
simple->value.simple->words = wordlist_push(simple->value.simple->words,
wordlist_pop(&tokens));
if (!simple->value.simple->words)
return (ft_errno(FT_EERRNO), cmd_destroy(simple), NULL);
redir = minishell_redirect_parse(app, tokens);
t_redirect_add_back(&simple->value.simple->redirections, redir);
}
if (!simple->value.simple->words)
return (cmd_destroy(simple), parse_error(app, tokens->word), NULL);
return (simple);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* simple_cmd_parse.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 10:38:23 by khais #+# #+# */
/* Updated: 2025/04/15 10:38:41 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef SIMPLE_CMD_PARSE_H
# define SIMPLE_CMD_PARSE_H
# include "../../minishell.h"
t_cmd *minishell_simple_cmd_parse(t_minishell *app, t_wordlist *tokens);
#endif // SIMPLE_CMD_PARSE_H

View file

@ -3,16 +3,15 @@
/* ::: :::::::: */
/* worddesc.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/13 17:20:36 by khais #+# #+# */
/* Updated: 2025/03/27 13:58:41 by khais ### ########.fr */
/* Updated: 2025/04/07 17:40:03 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "worddesc.h"
#include "libft.h"
#include "libft/libft.h"
#include <stdlib.h>
#include "../../treedrawing.h"

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* worddesc.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/27 13:57:44 by khais #+# #+# */
/* Updated: 2025/03/27 13:57:49 by khais ### ########.fr */
/* Updated: 2025/04/08 16:25:37 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -25,6 +25,21 @@
# define W_HASQUOTEDNULL 0b10000 /* word contains a quoted null character */
# define W_DQUOTE 0b100000 /* word should be treated as if double-quoted */
typedef enum e_token_type
{
NOT_TOKEN,
WORD_TOKEN,
OPEN_PARENTH_TOKEN,
CLOSE_PARENTH_TOKEN,
PIPE_TOKEN,
OR_TOKEN,
AND_TOKEN,
OUT_TRUNC_REDIR_TOKEN,
OUT_APPEND_REDIR_TOKEN,
IN_REDIR_TOKEN,
HERE_DOC_REDIR_TOKEN,
} t_token_type;
/*
** A logical word for the parser.
**
@ -37,25 +52,26 @@ typedef struct s_worddesc
/*
** The word itself
*/
char *word;
char *word;
/*
** flags for this word
**
**s_worddesc
** See above for flag definitions
*/
char flags;
char flags;
/*
** a character mask for word to designate the status
** of its characters and wether or not they are subject to modifications
**
** Possible flags are ''', '"', '$', ' '
** Possible flags are ''', '"', '$', '&', ' '
**
** ' ' the default, no flag, no meaning
** ''' corresponding character is single-quoted
** '"' corresponding character is double-quoted
** '$' corresponding character is a result of $var expansion
*/
char *marker;
char *marker;
t_token_type token_type;
} t_worddesc;
t_worddesc *worddesc_create(char *word, char flags, char *marker);

View file

@ -6,12 +6,11 @@
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/13 17:07:01 by khais #+# #+# */
/* Updated: 2025/03/21 10:43:41 by jguelen ### ########.fr */
/* Updated: 2025/04/07 17:40:30 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "wordlist.h"
#include "ft_printf.h"
#include "libft.h"
#include "unistd.h"
#include <stdlib.h>

View file

@ -5,8 +5,8 @@
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/21 13:48/52 by khais #+# #+# */
/* Updated: 2025/03/21 13:48:52 by khais ### ########.fr */
/* Created: 2025/04/14 17:23/51 by khais #+# #+# */
/* Updated: 2025/04/14 17:23:51 by khais ### ########.fr */
/* */
/* ************************************************************************** */
@ -40,7 +40,8 @@ void wordlist_destroy(t_wordlist *wordlist);
t_worddesc *wordlist_get(t_wordlist *wordlist, int idx);
t_wordlist *wordlist_push(t_wordlist *wordlist, t_worddesc *worddesc);
t_worddesc *wordlist_pop(t_wordlist **wordlist);
void wordlist_debug(t_wordlist *wordlist);
void wordlist_debug(t_wordlist *wordlist, t_buffer *leader,
bool is_last);
t_wordlist *wordlist_copy(const t_wordlist *wordlist);
void wordlist_destroy_idx(t_wordlist **wordlist, int idx);
t_worddesc *wordlist_pop_idx(t_wordlist **wordlist, int idx);

View file

@ -6,24 +6,30 @@
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/24 18:20:00 by khais #+# #+# */
/* Updated: 2025/03/19 14:35:24 by khais ### ########.fr */
/* Updated: 2025/04/15 14:38:39 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "wordlist.h"
#include "libft.h"
#include "../../treedrawing.h"
/*
** Debug-print a wordlist
*/
void wordlist_debug(t_wordlist *wordlist)
void wordlist_debug(t_wordlist *wordlist, t_buffer *leader, bool is_last)
{
indent(leader, is_last);
if (wordlist == NULL)
ft_printf("(empty wordlist)");
while (wordlist != NULL)
ft_printf("t_wordlist = (empty wordlist)\n");
else
{
ft_printf("[%s]", wordlist->word->word);
wordlist = wordlist->next;
ft_printf("t_wordlist\n");
while (wordlist != NULL)
{
worddesc_debug(wordlist->word, leader, wordlist->next == NULL);
wordlist = wordlist->next;
}
}
ft_printf("\n");
dedent(leader, is_last);
}

View file

@ -0,0 +1,41 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* expand_wildcard.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/03 19:28:28 by khais #+# #+# */
/* Updated: 2025/04/03 19:52:29 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "expand_wildcard.h"
#include "../../subst/subst.h"
t_simple_cmd *simple_cmd_expand_wildcards(t_simple_cmd *cmd)
{
t_wordlist *outlist;
t_wordlist *inlist;
t_worddesc *current;
t_wordlist *expansion_result;
outlist = NULL;
inlist = cmd->words;
while (inlist != NULL)
{
current = wordlist_pop(&inlist);
expansion_result = expand_star(current);
if (expansion_result == NULL)
outlist = wordlist_push(outlist, current);
else
{
while (expansion_result != NULL)
outlist = wordlist_push(outlist,
wordlist_pop(&expansion_result));
worddesc_destroy(current);
}
}
cmd->words = outlist;
return (cmd);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* expand_wildcard.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/03 19:22:10 by khais #+# #+# */
/* Updated: 2025/04/03 19:23:24 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef EXPAND_WILDCARD_H
# define EXPAND_WILDCARD_H
# include "../../minishell.h"
t_simple_cmd *simple_cmd_expand_wildcards(t_simple_cmd *cmd);
#endif // EXPAND_WILDCARD_H

View file

@ -0,0 +1,96 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* fieldsplit.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/03 15:48:52 by khais #+# #+# */
/* Updated: 2025/04/15 11:51:33 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "fieldsplit.h"
#include "../../buffer/buffer.h"
#include "../../parser/matchers/blank.h"
#include <stdlib.h>
static bool should_delimit(t_worddesc *original, size_t i)
{
if (is_blank(original->word[i]) && original->marker[i] == '$')
return (true);
return (false);
}
static void fieldsplit_delimit(t_buffer *word, t_buffer *marker,
t_worddesc *original, t_wordlist **outlist)
{
t_worddesc *out;
out = worddesc_create(word->buffer, original->flags, marker->buffer);
(*outlist) = wordlist_push(*outlist, out);
free(word);
free(marker);
}
static t_wordlist *minishell_fieldsplit(t_worddesc *original,
t_wordlist **outlist)
{
size_t i;
t_buffer *word;
t_buffer *marker;
i = 0;
word = ft_buffer_new();
marker = ft_buffer_new();
while (original->word[i] != '\0')
{
if (should_delimit(original, i))
{
fieldsplit_delimit(word, marker, original, outlist);
word = ft_buffer_new();
marker = ft_buffer_new();
while (should_delimit(original, i))
i++;
}
else
{
word = ft_buffer_pushchar(word, original->word[i]);
marker = ft_buffer_pushchar(marker, original->marker[i++]);
}
}
fieldsplit_delimit(word, marker, original, outlist);
return (*outlist);
}
static t_wordlist *wordlist_fieldsplit(t_wordlist **original)
{
t_worddesc *current;
t_wordlist *out;
if (original == NULL || *original == NULL)
return (NULL);
out = NULL;
current = wordlist_pop(original);
while (current != NULL)
{
if (minishell_fieldsplit(current, &out) == NULL)
{
wordlist_destroy(out);
return (NULL);
}
worddesc_destroy(current);
current = wordlist_pop(original);
}
(*original) = out;
return (*original);
}
t_simple_cmd *simple_cmd_fieldsplit(t_simple_cmd *cmd)
{
if (cmd == NULL)
return (NULL);
if (wordlist_fieldsplit(&cmd->words) == NULL)
return (NULL);
return (cmd);
}

View file

@ -0,0 +1,20 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* fieldsplit.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/03 15:46:56 by khais #+# #+# */
/* Updated: 2025/04/03 16:06:23 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef FIELDSPLIT_H
# define FIELDSPLIT_H
# include "../../parser/simple_cmd/simple_cmd.h"
t_simple_cmd *simple_cmd_fieldsplit(t_simple_cmd *cmd);
#endif // FIELDSPLIT_H

View file

@ -6,11 +6,15 @@
/* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/20 10:26:05 by jguelen #+# #+# */
/* Updated: 2025/02/21 17:02:33 by jguelen ### ########.fr */
/* Updated: 2025/04/04 17:05:32 by khais ### ########.fr */
/* */
/* ************************************************************************** */
// stdio must be included before readline
#include <stdio.h>
#include "readline/readline.h"
#include "sig.h"
#include "signal.h"
/*
** g_signum exists to store the value of a signal if relevant for later
@ -21,24 +25,33 @@
int g_signum = 0;
/*
** Stores the signal number corresponding to the caught signal into g_signum
** for later processing.
** redisplay prompt
*/
static void sig_interactive(int signum)
{
g_signum = signum;
if (signum == SIGINT)
{
rl_replace_line("", 0);
ft_printf("\n");
rl_on_new_line();
rl_redisplay();
}
}
void readline_reset(void)
{
rl_replace_line("", 0);
ft_printf("\n");
rl_redisplay();
}
/*
** Ignores the signal if it was sent by a kill command.
** Otherwise stores the value of the caught signal in g_signum for later
** processing.
** Stores the value of the caught signal in g_signum for later processing.
*/
static void ignoreif_sent_by_kill(int signum, siginfo_t *siginfo, void *context)
static void sig_cmd(int signum, siginfo_t *siginfo, void *context)
{
(void)context;
if (siginfo->si_code == SI_USER)
return ;
(void)siginfo;
g_signum = signum;
}
@ -53,11 +66,11 @@ int set_interactive_mode_sig_handling(void)
ft_bzero(&sig_act, sizeof(struct sigaction));
sig_act.sa_handler = &sig_interactive;
sig_act.sa_flags |= SA_RESTART;
if (sigemptyset(&sig_act.sa_mask) == -1)
return (-1);
if (sigaction(SIGINT, &sig_act, NULL) == -1)
return (-1);
sig_act.sa_handler = SIG_IGN;
if (sigaction(SIGQUIT, &sig_act, NULL) == -1)
return (-1);
return (0);
@ -75,36 +88,12 @@ int set_exec_mode_sig_handling(void)
ft_bzero(&sig_act, sizeof(struct sigaction));
if (sigemptyset(&sig_act.sa_mask) == -1)
return (-1);
sig_act.sa_sigaction = &ignoreif_sent_by_kill;
sig_act.sa_sigaction = &sig_cmd;
sig_act.sa_flags |= SA_SIGINFO;
sig_act.sa_flags |= SA_RESTART;
if (sigaction(SIGINT, &sig_act, NULL) == -1)
return (-1);
if (sigaction(SIGQUIT, &sig_act, NULL) == -1)
return (-1);
return (0);
}
/*
** Can be used at the start of a child process.
** Check if necessary since execve is executed.
** @RETURN Return 0 i case of success, -1 in case of error and errno is set
** to indicate the error.
*/
int set_default_sig_handling(void)
{
struct sigaction sig;
int i;
ft_bzero(&sig, sizeof(struct sigaction));
sig.sa_handler = SIG_DFL;
if (sigemptyset(&sig.sa_mask) == -1)
return (-1);
i = 0;
while (i < NSIG)
{
if (sigaction(i, &sig, NULL) == -1)
return (-1);
i++;
}
return (0);
}

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* sig.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/19 18:21:55 by jguelen #+# #+# */
/* Updated: 2025/03/11 17:36:07 by jguelen ### ########.fr */
/* Updated: 2025/04/04 16:58:49 by khais ### ########.fr */
/* */
/* ************************************************************************** */
@ -31,6 +31,6 @@ extern int g_signum;
int set_interactive_mode_sig_handling(void);
int set_exec_mode_sig_handling(void);
int set_default_sig_handling(void);
void readline_reset(void);
#endif

View file

@ -6,7 +6,7 @@
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/02 13:40:10 by jguelen #+# #+# */
/* Updated: 2025/03/27 18:39:23 by khais ### ########.fr */
/* Updated: 2025/04/01 18:38:35 by khais ### ########.fr */
/* */
/* ************************************************************************** */
@ -77,8 +77,6 @@ static char *select_path(char *filepath, char **oldpath, char **path,
else
return (free(*oldpath), path_split_destroy(path), filepath);
}
if (*oldpath != filepath)
free(filepath);
return (NULL);
}

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* wildcard_exp.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/20 15:01:38 by jguelen #+# #+# */
/* Updated: 2025/03/21 18:42:04 by jguelen ### ########.fr */
/* Updated: 2025/04/03 19:58:36 by khais ### ########.fr */
/* */
/* ************************************************************************** */
@ -104,20 +104,17 @@ static t_wordlist *expand_star_core(t_worddesc *file_pattern)
if (current_dir == NULL)
return (NULL);
file_wordlist = NULL;
errno = 0;
new = readdir(current_dir);
while (new)
{
if (fits_pattern(new->d_name, file_pattern))
{
if (add_file_to_list(&file_wordlist, new->d_name) == NULL)
return (NULL);
return (closedir(current_dir), NULL);
}
errno = 0;
new = readdir(current_dir);
}
if (errno)
return (wordlist_destroy(file_wordlist), NULL);
closedir(current_dir);
if (!file_wordlist)
file_wordlist = wordlist_independant_create(file_pattern);
return (wordlist_quicksort_full(file_wordlist));

188
test.sh
View file

@ -305,6 +305,7 @@ EOF
EXTRAENV=-i
when_run <<EOF "export with strange inputs"
export ENV=$(which env)
export PATH=$PATH
export var
echo status=\$?
@ -312,7 +313,7 @@ echo var=[\$var]
export blue=
echo status=\$?
echo blue=[\$blue]
env -u PATH env
\$ENV -u PATH -u ENV env
EOF
expecting <<EOF
status=0
@ -335,6 +336,191 @@ status=1
hello=hi
EOF
when_run <<EOF "empty command is not found"
""
EOF
expecting <<EOF
minishell: : command not found
EOF
when_run <<EOF "exit status of last command is preserved"
echo \$?
ls nonexist
echo \$?
echo hi
echo \$?
EOF
expecting <<EOF
0
ls: cannot access 'nonexist': No such file or directory
2
hi
0
EOF
when_run <<EOF "exit works"
$MINISHELL
exit
echo \$?
EOF
expecting <<EOF
0
EOF
when_run <<EOF "exit works with custom exit code"
$MINISHELL
exit 99
echo \$?
EOF
expecting <<EOF
99
EOF
when_run <<EOF "exit works with extreme exit codes"
$MINISHELL
exit 1234567890
echo \$?
$MINISHELL
exit -1234567890
echo \$?
EOF
expecting <<EOF
210
46
EOF
when_run <<EOF "exit works with nan input"
$MINISHELL
exit not-a-number
echo \$?
EOF
expecting <<EOF
minishell: exit: not-a-number: numeric argument required
2
EOF
when_run <<EOF "exit works with too many arguments"
$MINISHELL
exit 1 2
echo \$?
exit
echo \$?
$MINISHELL
exit not-a-number 3
echo \$?
$MINISHELL
exit 3 not-a-number
echo \$?
exit
echo \$?
EOF
expecting <<EOF
minishell: exit: too many arguments
1
0
minishell: exit: not-a-number: numeric argument required
2
minishell: exit: too many arguments
1
0
EOF
when_run <<EOF "echo basic tests"
echo hello world
echo "Hello World"
EOF
expecting <<EOF
hello world
Hello World
EOF
when_run <<EOF "echo -n tests"
echo -n hello world
echo
echo -nnnnnnnnnnnnnnnn hello world
echo
echo -nnnntnnnnnnnnnnnn hello world
echo hi -n hello
echo -n -n -nnn -n goodbye
echo
EOF
expecting <<EOF
hello world
hello world
-nnnntnnnnnnnnnnnn hello world
hi -n hello
goodbye
EOF
when_run <<EOF "echo - n"
echo - n hi
EOF
expecting <<EOF
- n hi
EOF
EXTRAENV="-i"
when_run <<EOF "env works"
echo this should be empty:
env
echo
export var=VALUE hi=hello bye=goodbye
echo this contains some values
env
unset var bye
echo some vars were unset
env
EOF
expecting <<EOF
this should be empty:
this contains some values
var=VALUE
hi=hello
bye=goodbye
some vars were unset
hi=hello
EOF
EXTRAENV="-i"
when_run <<EOF "env ignores additional arguments"
export var=VALUE hi=hello bye=goodbye
env -i name="The Other" ls
EOF
expecting <<EOF
minishell: env: ignoring arguments
var=VALUE
hi=hello
bye=goodbye
EOF
when_run <<EOF "echo field splitting tests"
export val=" hi there "
echo -\$val-
echo -"\$val"-
export val="hello there"
echo -\$val-
echo -"\$val"-
EOF
expecting <<EOF
- hi there -
- hi there -
-hello there-
-hello there-
EOF
when_run <<EOF "wildcards"
echo *
touch hi there hello
echo *
echo h*
EOF
expecting <<EOF
*
hello hi there
hello hi
EOF
when_run <<EOF "quoted parentheses are not operators"
echo unclosed '('
EOF

View file

@ -44,7 +44,7 @@ run: $(run_tests)
%.o: %.c
$(CC) -c $(CFLAGS) $(IFLAGS) -o $*.o $*.c
$(CC) -MM $(CFLAGS) $(IFLAGS) $*.c > $*.d
$(CC) -MM $(CFLAGS) $(IFLAGS) -MT $*.o $*.c > $*.d
test_%: %.o $(objs)
$(CC) $(CFLAGS) -rdynamic -o $@ $*.o $(objs) $(LINCLUDE) $(LDLIBS)