flowchart TD
classDef cmd fill:#9f9,color:#000;
classDef msg fill:#bcd,color:#000;
classDef file fill:#f9f,color:#000;
classDef signal fill:#f57,color:#000;
classDef important fill:#f50,color:#000;
A[MiniShell]:::important ==> B[Main]:::important
B ==> readline[Readline]:::important
readline --> readline
readline ==> lexer[Lexing]:::important
lexer ==> parser["Parsing<br> '' '' : allow $var<br>' '<br> |<br> < > <br><< >> "]:::important
readline -.- |"if empty line with Ctrl-D"|exit_shell
readline -.- |"if text on line with Ctrl-D"|nothing
lexer -.-x |"Error:<br>if quotes doesnt end"| lexer_error[Lexical Error<br>message]:::msg
parser <--> list[Listing]
parser --> |"<< EOF"|here_doc[Here Doc]
here_doc --> readline2[Readline]
readline2 --> here_doc_file[Here Doc File]:::file
here_doc_file --> list
parser ==> start_command{"Start<br>Commands"}:::important
start_command -->|1| single[Single Command]:::cmd
start_command -->|"> 1"| multi[Multi Commands]:::cmd
single --> exec
single --> builtin[Builtin]:::cmd
multi --> loop((Loop))
loop -->|"- 1"| loop
loop --> exec{Execution}:::important
loop --> builtin
exec -.- direction[Direction]
direction -.- file[File]:::file
file -.- access[Access]
direction -.- pipe[Pipe]:::file
exec -.- env[Env]
env -.- path[Path]
path -.- access
exec -.- execve:::cmd
Sig_handler_interactive --o signal
Sig_handler_not_interactive --o signal
Sig_handler_heredoc --o signal
signal[Signal]
signal --> SIGQUIT[Ctrl-\]:::signal -.- nothing[Do Nothing]
signal --> SIGEOF[Ctrl-D]:::signal -.- exit_shell[Exit Shell]
signal --> SIGINT[Ctrl-C]:::signal -.- kill[Send SIGINT]
SIGEOF -.-|"End of file"| here_doc
SIGINT -.-|"here_doc_interrupt"| readline
kill -.-x|"if SIGINT in here_doc"|start_command
Here is a list of prompts to see if your shell is working correctly (Not all prompts are mandatory, but it's a good way to test your shell):
To suppress readline leaks, use the following command (with readline.supp in the same directory as the executable):
valgrind --leak-check=full --show-leak-kinds=all --track-fds=yes --suppressions=readline.supp ./minishell- types:
- pipe
| - redirection
- input
< - output
> - append
>> - here_doc
<<followed LIMITER
- input
- file
- infile
- outfile
- command
- list of args[0];
- args
- list of args
- pipe
graph LR;
redir_in["<"] --- in_file[File] --> cmd --- args --> redir_out[">"] --- out_file[File]
- input
- link to std_in of the command before the next pipe
- in case multiple
<input with the same command, dup2 in order of appearance:- dup2(fd, 0)
- dup2(fd2, 0)
- output
- append
- here_doc
Example of redir << here_doc and input:
$> << EOF cat > test | << EOF cat | wc
> jf
> jf
> EOF
> df
> df
> df
> EOF
3 3 9
$> cat test
jf
jfExample of redir > output:
<< EOF cat | cat > test_output | < test cat > test_output2
> test1
> test1
> test1
> EOF
$> cat test_output
test1
test1
test1
$> cat test_output2
jf
jfSearch for << and LIMITER and replace the line with the content until LIMITER is found.
Launch readline but do not save the line in the history.
line = readline("here_doc> ");Store content in a heredoc file. This heredo file will be stored as an infile and also stored in a list of heredoc files to be deleted at the end of the program.
variable rl_catch_signals = 0;Handle Ctrl-C and Ctrl-D signals. When signals are caught, the stdin is closed and the heredoc file is deleted. To prevent error of display with readline, we stop readline to listen to signals. Here preventing two new lines instead of one.
When LIMITER is found, the heredoc file is closed and the heredoc event is stopped.
Or when Ctrl-D is pressed, the heredoc file is closed and the heredoc event is stopped.
To close it we use close(stdin).
In bash:
If Ctrl-C is pressed, the heredoc file is deleted and the heredoc event is stop, the shell however must continue and the input is saved in history.
if Ctrl-D is pressed, it's like using the LIMITER in the heredoc.
Ctrl-\ is SIGQUIT. Ignored in interactive mode.
The signal handler sig_handler_interactive in interactive mode display new prompt and clear rl_line_buffer. We can ignore this signal handler by setting rl_catch_signals to 0. Ctrl-\ is ignored.
In non interactive mode, Ctrl-C is restored as default handler (in child process, SIG_DFL which is a macro to restore handler) and Ctrl-D is handled as EOF. Ctrl-\ is like Ctrl-C but write "Quit (core dumped)\n" to stderr.
Exit status is 128 + signal number.
With exit builtin.
The exit code should be coded on 8 bits. If the exit code is greater than 255, the shell should return the exit code modulo 256. If the exit code is less than 0, the shell should return the exit code modulo 256 -> + 256 -> modulo 256.
Particular cases with the format of LIMITER :
When double or simple quotes are present and closed, expansions does not happen. Quotes are not part of the LIMITER.
$> << "LIM" cat
> $PATH
> LIM
$PATHClosed quotes are ignored and expansions does not happen.
$> << LIM''"" cat
> hey
> LIM
heyAnother case with quotes : quotes ignored in LIMITER and no expansions.
$> << L"I"M cat
> $PATH
> LIM
$PATHAs we should not interpret backslash character (\\) and unclosed quotes, in bash LIMITER would be LI"M but we will ask LI\"M as a limiter.
$> << LI\"M cat
> hey
> LI\"M
heyUnclosed quotes should not be interpreted so expansions happen.
$> << LIM"' cat
>> hey
>> LIM
hey$> < infile > outfileThis input is valid and the redirections are applied to the shell. In this case, the shell will not execute any command and will only apply the redirections.