Compare commits

...

8 commits

103 changed files with 148 additions and 5527 deletions

View file

@ -17,7 +17,7 @@ LINCLUDE = -L$(LIBFTDIR)
ifeq ($(CFLAGS),)
CFLAGS = -Wall -Wextra -Werror \
$(DEBUG) \
$(DEBUG) \
endif
export CFLAGS
@ -157,7 +157,6 @@ clean:
fclean: clean
$(MAKE) -C $(LIBFTDIR) fclean
rm -f $(NAME)
+make -C tests fclean
re:
+make fclean

744
NOTES.md
View file

@ -1,744 +0,0 @@
# Notes relatives au projet
cf. [Bash Reference Manual](https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html)
Comparative testing with bash should be done with bash --norc.
In case of difference between regular bash and posix bash, we decide to follow regular bash.
## Ideas for testing
* use prysk or shellspec with shell=./minishell
### Prysk
Seems like it would work, but only after we implement execution of simple commands, and the $? variable, since prysk (and cram as well) relie on it to collect the exit status.
For it to work, the shell must be able to execute the following command:
```shell
$ echo PRYSK12345 2 $?
PRYSK12345 2 0
```
## Usefull resources
[Bash Reference Manual](https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html)
[Internal parsing & flow](https://mail.gnu.org/archive/html/help-bash/2014-01/msg00000.html)
[Python parser for bash](https://github.com/idank/bashlex)
[The Stages of Word Expansion](https://www.gnu.org/software/libc/manual/html_node/Expansion-Stages.html)
[Diagram of bash parse flow](https://web.archive.org/web/20160308045823/https://stuff.lhunath.com/parser.png)
[The Bash Parser](http://mywiki.wooledge.org/BashParser)
[The Architecture of Open Source Applications (Volume 1) The Bourne-Again Shell](https://aosabook.org/en/v1/bash.html)
[IEEE Open Group Base Specification Issue 7: Shell Command Language](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_01)
[The Bash Hackers Wiki: Basic grammar rules of Bash (see also other pages on this website)](https://bash-hackers.gabe565.com/syntax/basicgrammar/)
## Shell Operation
cf. 3.1.1 Shell Operation
Reads the input. When reading input in non-interactive mode, it MUST do so one
character at a time, so that it does not accidentally read a character that is
intended for a called program.
Breaks the input into **words** and **operators**, obeying the *Quoting Rules*.
These tokens are delimited by **metacharacters**.
Parses the tokens into simple and compound commands (see *Shell Commands*)
Performs the various _Shell Expansions_, breaking the expanded tokens into lists
of filenames and commands and arguments.
Performs any necessary _redirections_ and removes the **redirection operators**
and their operands from the argument list.
Executes the command (see _Command Execution_);
Waits for the command to complete and collects its exit status (see _Exit Status_).
### Quoting Rules
cf. 3.1.2 Quoting
Quoting escapes metacharacters.
The quoting mechanisms we have to implement are:
cf. Subject
* Single quotes, which prevent metacharacters interpretation.
* Double quotes, which prevent metacharacters interpretation except for '$' (See
_Shell Parameter Expansion_).
In the Bash Reference Manual, these are defined as follows (keeping only the parts we have to implement):
cf. 3.1.2.2 Single Quotes
Preserves the literal value of each character within the quotes.
cf. 3.1.2.3 Double Quotes
Preserves the literal value of all characters within the quotes, with the exception of '$'.
Per the subject: minishell should not interpret unclosed quotes
### Shell Commands
cf. 3.2 Shell Commands
A Shell Command may be either a *Simple Command*, a *Pipeline*, a *List of
Commands* (composed of one or more *Pipelines*), or a *Grouped Command*
(composed of one or more *List of Commands*).
#### Simple Commands
cf. 3.2.2 Simple Commands
Its just a sequence of words separated by **blanks**, terminated by one of the
shells **control operators**.
The first **word** specifies a command to be executed, with the rest of the
**words** being that commands arguments.
The return status (see _Exit Status_) of a simple command is its exit status as
provided by the POSIX 1003.1 waitpid function, or 128+n if the command was
terminated by signal n.
#### Pipelines
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.
#### Lists of Commands
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 execute if and only if A has an exit status of 0 (succes).
An OR list has the form
```shell
A || B
```
B is execute 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.
#### Group of Commands
cf. 3.2.5 Compound Commands
Each group begins with the **control operator** '(' and ends with the
**control operator** ')'.
Any redirections associated with a _group of commands_ apply to all commands
within that _group of commands_ unless explicitly overridden.
cf. 3.2.5.3 Grouping Commands
When commands are grouped, redirections may be applied to the entire command
list. For example, the output of all the commands in the list may be redirected
to a single stream.
( LIST )
The parentheses are operators, and are recognized as separate tokens by the
shell even if they are not separated from the LIST by whitespace.
Placing a list of commands between parentheses forces the shell to create a
_subshell, and each of the commands in LIST is executed in that subshell
environment. Since the LIST is executed in a subshell, variable assignments do
not remain in effect after the subshell completes.
The exit status of this construct is the exit status of LIST.
```c
typedef enum e_cmdlist_item_type
{
TYPE_INVALID,
TYPE_CMDGROUP,
TYPE_PIPELINE,
} t_cmdlist_item_type;
typedef struct s_cmdlist_item
{
enum e_cmdlist_item_type type;
union u_cmdlist_item_inner
{
t_cmdgroup *cmdgroup;
struct s_pipeline *pipeline;
} inner;
} t_cmdlist_item;
typedef s_cmdlist
{
struct s_cmdlist_item *cmds;
int num_cmds;
struct s_operators *operators;
} t_cmdlist;
typedef struct s_cmdgroup
{
struct s_cmdlist item;
struct s_redirection_list *redirections;
} t_cmdgroup;
```
```c
// (cmd1) && (cmd2)
t_cmdgroup
{
item = t_cmdlist
{
cmds = [
t_cmdlist_item
{
type = TYPE_CMDGROUP
inner = t_cmdgroup
{
item = t_cmdlist
{
cmds = [
t_cmdlist_item
{
type = TYPE_PIPELINE
inner = t_pipeline "cmd1"
}
]
num_cmds = 1
operators = [OP_END]
}
redirections = NULL
}
}
t_cmdlist_item
{
type = TYPE_CMDGROUP
inner = t_cmdgroup
{
item = t_cmdlist
{
cmds = [
t_cmdlist_item
{
type = TYPE_PIPELINE
inner = t_pipeline "cmd2"
}
]
num_cmds = 1
operators = [OP_END]
}
redirections = NULL
}
}
]
num_cmd = 2
operators = [OP_AND, OP_END]
}
redirections = NULL
}
```
### Shell Expansion
cf. 3.5 Shell Expansions
Expansion is performed on the command line after it has been split into
**token**'s. There are seven kinds of expansion performed. in the following
order:
* brace expansion
* tilde expansion
* parameter and variable expansion
* arithmetic expansion
* command substitution (left to right)
* word splitting
* filename expansion
We only have to implement the following kinds:
* parameter expansion
* word splitting
* filename expansion
After these expansions are performed, quote characters present in the original
word are removed unless they have been quoted themselves ("_quote removal_").
Only brace expansion, word splitting, and filename expansion can increase the
number of words of the expansion; other expansions expand a single word to a
single word.
#### Shell Parameter Expansion
cf. 3.5.3 Shell Parameter Expansion
The '$' character introduces parameter expansion, command substitution, or
arithmetic expansion.
The form is $VAR, where VAR may only contain the following characters:
* a-z
* A-Z
* _
* 0-9 (not in the first character)
Just noticed an interesting case:
```shell
bash-5.2$ VAR=hello # we set a shell variable (NOT environment variable)
bash-5.2$ VAR=hi env | grep VAR=; env | grep VAR= # here VAR is an environment variable, which is only valid for the next command (the second env returns nothing, confirming that it is not valid for that command)
VAR=hi
bash-5.2$ env | grep VAR= # var is not an environment variable
bash-5.2$ echo $VAR # but it is a shell variable
hello
```
Luckily for us, we don't have to handle shell variables, nor do we have to handle `VAR=value` or `VAR=value cmd`.
#### Word Splitting
cf. 3.5.7 Word Splitting
The shell scans the results of parameter expansion that did not occur within
double quotes for word splitting.
The shell splits the results of the other expansions into **words**.
The shell treats the following characters as a delimiter:
* (space)
* (tab)
* (newline)
Explicit null arguments ('""' or '''') are retained and passed to commands as
empty strings. Unquoted implicit null arguments, resulting from the expansion
of parameters that have no values, are removed. If a parameter with no value is
expanded within double quotes, a null argument results and is retained and
passed to a command as an empty string. When a quoted null argument appears as
part of a word whose expansion is non-null, the null argument is removed. That
is, the word '-d''' becomes '-d' after word splitting and null argument removal.
Note that if no expansion occurs, no splitting is performed.
#### Filename Expansion
cf. 3.5.8 Filename Expansion
Bash scans each word for the character '\*'.
If one of these characters appears, and is not quoted, then the word is regarded
as a PATTERN, and replaced with an alphabetically sorted list of filenames
matching the pattern (see: _Pattern Matching_). If no matching filenames are
found, the word is left unchanged.
When a pattern is used for filename expansion, the character '.' at the start of
a filename or immediately following a slash must be matched explicitly. In order
to match the filenames '.' and '..', the pattern must begin with '.'
When matching a filename, the slash character must always be matched explicitly
by a slash in the pattern.
##### Pattern Matching
cf. 3.5.8.1 Pattern Matching
Any character that appears in a pattern, other than the special pattern
characters described below, matches itself. The NUL character may not occur in
a pattern.
The special pattern characters have the following meanings:
'\*'
Matches any string, including the null string.
The special pattern characters must be quoted if they are to be matched
literally.
e.g. this is the required behaviour
```shell
bash-5.1$ ls *there
'hello*there' 'hi*there' noonethere
bash-5.1$ ls *'*'there
'hello*there' 'hi*there'
```
#### Quote Removal
cf. 3.5.9 Quote Removal
After the preceding expansions, all unquoted occurrences of the characters '''
and '"' that did not result from one of the above expansions are removed.
### Redirection
cf. 3.6 Redirections
Before a command is executed, its input and output may be "redirected" using a
special notation interpreted by the shell. "Redirection" allows commands' file
handles to be made to refer to different files, and can change the files the
command reads from and writes to.
The redirection operators may precede or appear anywhere within a simple command
or may follow a command.
e.g. this is the correct behaviour
```shell
bash-5.1$ ls > hello.txt *here
bash-5.1$ cat hello.txt
hello*there
hi*there
noonethere
```
Redirections are processed in the order they appear, from left to right.
e.g. this is the correct behaviour
```shell
bash-5.1$ ls > hello.txt share > here.txt *.txt
bash-5.1$ ls -l hello.txt here.txt
-rw-r--r-- 1 kcolin 2024_le-havre 0 Feb 7 15:54 hello.txt
-rw-r--r-- 1 kcolin 2024_le-havre 68 Feb 7 15:54 here.txt
bash-5.1$ cat here.txt
hello.txt
here.txt
log.txt
newlog-strict.txt
newlog.txt
share:
man
```
'<' refers to the standard input (fd 0, STDIN\_FILENO)
'>' refers to the standard output (fd 1, STDOUT\_FILENO)
The word following the redirection operator, unless the redirection operator is
'<<', is subjected to parameter expansion, filename expansion, word splitting,
and quote removal.
If it expands to more than one word, Bash reports an error.
This is the correct behaviour:
```shell
bash-5.1$ var="file1 file2"
bash-5.1$ echo "hello world" > $var
bash: $var: ambiguous redirect
```
If the variable is not defined, bash prints the following error:
```shell
bash-5.1$ echo "hello world" > $nonexist
bash: $nonexist: ambiguous redirect
```
Interesting cases:
command group must handle redirections at the scale of the group
pipelines handle piplines
simple commands handle its own file redirections
group redirections may not appear at the start, except for here_doc, which will give a parse error *afterwards*
```shell
bash-5.2$ (echo hello | cat -e > outcat && echo hi) > outgroup
bash-5.2$ cat outgroup
hi
bash-5.2$ cat outcat
hello$
bash-5.2$ (echo hello | cat -e && echo hi) > outgroup2
bash-5.2$ cat outgroup2
hello$
hi
bash-5.2$ (echo hello > outhello | cat -e && echo hi) > outgroup3
bash-5.2$ cat outhello
hello
bash-5.2$ cat outgroup3
hi
bash-5.2$ > outgroup4 (echo hello > outhello | cat -e && echo hi)
bash: syntax error near unexpected token `('
bash-5.2$ echo bonjour > infile
bash-5.2$ < infile (cat - > outhello | cat -e && echo hi)
bash: syntax error near unexpected token `('
bash-5.2$ << EOF (cat - > outhello | cat -e && echo hi)
> hello
> EOF
bash: syntax error near unexpected token `('
bash-5.2$ (cat - > outhello | cat -e && echo hi) < infile
hi
bash-5.2$ (cat - > outhello | cat -e && echo hi) << EOF
> helllo
> EOF
hi
bash-5.2$ cat outhello
helllo
bash-5.2$ (echo coucou | echo hello) > outfile
bash-5.2$ cat outfile
hello
```
#### Here Documents
cf. Bash Reference Manual 3.6.6 Here Documents
This type of redirection instructs the shell to read input from the current
source until a line containing only word (with no trailing blanks) is seen. All
of the lines read up to that point are then used as the standard input for a
command.
No parameter and variable expansion, command substitution, arithmetic expansion,
or filename expansion is performed on word.
If any part of word is quoted, the delimiter is the result of quote removal on
word, and the lines in the here-document are not expanded. If word is unquoted,
all lines of the here-document are subjected to parameter expansion.
This is the correct behaviour for quoting and parameter expansion:
```shell
bash-5.2$ cat << EOF
> hello
> $$
> EOF
hello
1491742
bash-5.2$ cat << "EOF"
> hello
> $$
> EOF
hello
$$
bash-5.2$ cat << 'E'OF
> hello
> $$
> EOF
hello
$$
bash-5.2$ cat << $USER
> hello
> khais
> $USER
hello
khais
bash-5.2$ echo $USER
khais
bash-5.2$ cat << "$USER"
> $USER
```
Subject says \\ is not required, so this behaviour we will not implement:
```shell
bash-5.2$ cat << EOF
> hello \
world
> EOF
hello world
```
### Executing Commands
cf. 3.7 Executing Commands
#### Simple Command Execution
cf. 3.7.1 Simple Command Expansion
When a simple command is executed, the shell performs the following
expansions, assignments, and redirections, from left to right, in the
following order.
1. The words that the parser has marked as redirections are saved for later
processing.
2. The words that are not redirections are expanded (see _Shell Expansions_).
If any words remain after expansion, the first word is taken to be the name
of the command and the remaining words are the arguments.
3. Redirections are performed as described above (see _Redirections_).
If no command name results, redirections are performed, but do not affect the
current shell environment. A redirection error causes the command to exit with
a non-zero status.
If there is a command name left after expansion, execution proceeds as described
below. Otherwise, the command exits with a status of zero.
#### Command Search and Execution
cf. 3.7.2 Command Search and Execution
After a command has been split into words, if it results in a simple command and
an optional list of arguments, the following actions are taken.
1. The shell searches for it in the list of shell builtins. If a match is
found, that builtin is invoked.
2. If the name is not a builtin, and contains no slashes, Bash searches each
element of '$PATH' for a directory containing an executable file by that
name. If the search is unsuccessful, the shell prints an error message and
returns an exit status of 127.
3. If the search is successful, or if the command name contains one or more
slashes, the shell executes the named program in a separate execution
environment. Argument 0 is set to the name given, and the remaining
arguments to the command are set to the arguments supplied, if any.
4. If this execution fails because the file is not in executable format, and
the file is not a directory, it is assumed to be a "shell script" and the
shell executes it as described in _Shell Scripts_.
NOTE: we will _maybe_ implement this, we will see. It does not seem to be
required.
5. The shell waits for the command to complete and collects its exit status.
#### Subshell
cf. 3.7.3 Command Execution Environment
The shell has an execution environment, which consists of the following:
open files inherited by the shell at invocation, as modified by redirections
the current working directory as set by cd or inherited by the shell at invocation
shell variables, passed in the environment
A command invoked in this separate environment cannot affect the shells
execution environment.
A subshell is a copy of the shell process.
#### Environment
cf. 3.7.4 Environment
When a program is invoked it is given an array of strings called the
"environment". This is a list of name-value pairs, of the form 'name=value'.
Bash provides several ways to manipulate the environment. On invocation, the
shell scans its own environment and creates a parameter for each name found,
automatically marking it for 'export' to child processes. Executed commands
inherit the environment. The 'export' and 'unset' builtins allow parameters to
be added to and deleted from the environment. If the value of a parameter in
the environment is modified using the 'export' builtin, the new value becomes
part of the environment, replacing the old. The environment inherited by any
executed command consists of the shell's initial environment, whose values may
be modified in the shell, less any pairs removed by the 'unset' builtin, plus
any additions via the 'export' command.
#### Exit Status
cf. 3.7.5 Exit Status
The exit status of an executed command is the value returned by the 'waitpid'
system call or equivalent function. Exit statuses fall between 0 and 255,
though, as explained below, the shell may use values above 125 specially. Exit
statuses from shell builtins and compound commands are also limited to this
range. Under certain circumstances, the shell will use special values to
indicate specific failure modes.
For the shell's purposes, a command which exits with a zero exit status has
succeeded. A non-zero exit status indicates failure. This seemingly
counter-intuitive scheme is used so there is one well-defined way to indicate
success and a variety of ways to indicate various failure modes. When a command
terminates on a fatal signal whose number is N, Bash uses the value 128+N as the
exit status.
If a command is not found, the child process created to execute it returns a
status of 127. If a command is found but is not executable, the return status
is 126.
If a command fails because of an error during expansion or redirection, the exit
status is greater than zero.
All of the Bash builtins return an exit status of zero if they succeed and a
non-zero status on failure, so they may be used by the conditional and list
constructs. All builtins return an exit status of 2 to indicate incorrect
usage, generally invalid options or missing arguments.
The exit status of the last command is available in the special parameter $?.
#### Signals
cf. 3.7.6 Signals
When Bash is interactive, it ignores 'SIGTERM' (so that 'kill 0' does not kill
an interactive shell), and 'SIGINT' is caught and handled. When Bash receives a
'SIGINT', it breaks out of any executing loops. In all cases, Bash ignores
'SIGQUIT'. Bash ignores 'SIGTTIN', 'SIGTTOU', and 'SIGTSTP'.
NOTE: The behaviour on when ^C is printed seems strange, investigate further
once we implement this
TODO: investigate this further, this seems very complicated
## Definitions
cf. [Bash Reference Manual](https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Definitions)
cf. 2 Definitions
**token**
A sequence of characters considered a single unit by the shell. It is either a
word or an operator
**word**
A sequence of characters treated as a unit by the shell. Words may not include
unquoted metacharacters.
**operator**
A **control operator** or a **redirection operator**.
Operators contain at least one unquoted **metacharacter**.
**control operator**
A token that performs a control function.
It is a newline or one of the following: '|', ||, &&, (, or ).
**redirection operator**
For our project:
'<' redirects input
'>' redirects output
'<<' is here_doc with delimiter.
delimiter is a **word**.
Does not have to update history
'>>' redirects output in append mode
**blank**
A space or tab character

Binary file not shown.

View file

@ -13,7 +13,7 @@ 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 -> REDIR SIMPLE_LST
SIMPLE_LST -> word REDIR SIMPLE_LST
SIMPLE_LST -> ε
REDIR -> > word REDIR
@ -41,9 +41,9 @@ OPT_PIPELINE -> | GROUP_OR_SIMPLE OPT_PIPELINE
OPT_PIPELINE -> ε
GROUP_OR_SIMPLE -> (CMDS) REDIR
GROUP_OR_SIMPLE -> SIMPLE
SIMPLE -> REDIR SIMPLE_TAIL
SIMPLE_TAIL -> word REDIR SIMPLE_TAIL
SIMPLE_TAIL -> ε
SIMPLE -> REDIR SIMPLE_LST
SIMPLE_LST -> word REDIR SIMPLE_LST
SIMPLE_LST -> ε
REDIR -> > word REDIR
REDIR -> >> word REDIR
REDIR -> < word REDIR

View file

@ -1,28 +0,0 @@
jguelen@c2r12p1 ~()% bash
jguelen@c2r12p1:~$ export test=flagada$PATH
jguelen@c2r12p1:~$ echo $test
flagada/home/jguelen/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin
jguelen@c2r12p1:~$ export test="flagada"$PATH
jguelen@c2r12p1:~$ echo $test
flagada/home/jguelen/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin
jguelen@c2r12p1:~$ export test=flagada"$PATHhihi"
jguelen@c2r12p1:~$ echo $test
flagada
jguelen@c2r12p1:~$ export test=flagada"$PATH hihi"
jguelen@c2r12p1:~$ echo $test
flagada/home/jguelen/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin hihi
jguelen@c2r12p1:~$ export test=flagada"$PATH"hihi
jguelen@c2r12p1:~$ echo $test
flagada/home/jguelen/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/binhihi
jguelen@c2r12p1:~$ export test=flagada"$'PA'TH"hihi
jguelen@c2r12p1:~$ echo $test
flagada$'PA'THhihi
jguelen@c2r12p1:~$ export test=flagada$'P'ATH
jguelen@c2r12p1:~$ echo $test
flagadaPATH
jguelen@c2r12p1:~$ export test=flagada$P'A'TH hihi
jguelen@c2r12p1:~$ echo $test
flagadaATH
jguelen@c2r12p1:~/Common_Core/minishell$ echo "tests$"PATH""
tests$PATH
jguelen@c2r12p1:~$

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* buffer.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <marvin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/12/12 12:39:58 by kcolin #+# #+# */
/* Updated: 2025/04/09 17:26:24 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:44:10 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -39,12 +39,6 @@ void ft_buffer_free(t_buffer *buffer)
free(buffer);
}
/*
** grows the buffer such that the new capacity is 1.5 times the old.
**
** in case of error, free all memory and return null.
** else return buffer.
*/
t_buffer *ft_buffer_grow(t_buffer *buffer)
{
char *newbuffer;
@ -81,15 +75,6 @@ t_buffer *ft_buffer_pushchar(t_buffer *buffer, char c)
return (buffer);
}
/*
** push buf to the end of buffer, growing buffer if needed.
**
** also adds an additional null byte to terminate the buffer.
**
** the number of bytes to copy to buffer is n.
** returns buffer.
** in case of error, all memory is freed and null is returned.
*/
t_buffer *ft_buffer_push_buf(t_buffer *buffer, char *buf, size_t n)
{
if (buffer == NULL)

View file

@ -3,22 +3,16 @@
/* ::: :::::::: */
/* buffer_charptr.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/10 18:45:49 by kcolin #+# #+# */
/* Updated: 2025/03/10 18:46:35 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:43:47 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "buffer.h"
#include <stdlib.h>
/*
** Free this buffer, but return a pointer to the internal string, which is not
** freed.
**
** If buffer is null, return null.
*/
char *ft_buffer_to_charptr(t_buffer *buffer)
{
char *out;

13
src/env/env.c vendored
View file

@ -3,19 +3,16 @@
/* ::: :::::::: */
/* env.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/19 13:59:20 by kcolin #+# #+# */
/* Updated: 2025/02/19 14:42:35 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:39:37 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "env.h"
#include <stdlib.h>
/*
** Free the given env node.
*/
void env_destroy_node(t_env *env)
{
if (!env)
@ -25,9 +22,6 @@ void env_destroy_node(t_env *env)
free(env);
}
/*
** Free the given env linked list.
*/
void env_destroy(t_env *env)
{
t_env *next;
@ -42,9 +36,6 @@ void env_destroy(t_env *env)
}
}
/*
** Get the number of mappings in the givne env linked list
*/
size_t env_get_size(t_env *env)
{
size_t nb_elem;

22
src/env/env_convert.c vendored
View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* env_convert.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/19 14:39:57 by kcolin #+# #+# */
/* Updated: 2025/02/19 16:41:08 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:44:33 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -15,13 +15,6 @@
#include "envp.h"
#include "libft.h"
/*
** Create an envp structure from the given env linked list.
**
** The linked list is not destroyed.
**
** in case of allocation error, all memory is freed and NULL is returned.
*/
char **envp_from_env(t_env *env)
{
char **new_envp;
@ -48,17 +41,6 @@ char **envp_from_env(t_env *env)
return (new_envp);
}
/*
** read an envp structure, and create a t_env linked list containing the same
** information.
**
** the envp structure is not freed.
**
** in case of error, all memory is freed and null is returned.
**
** no checks additional checks than those of envp_get_key and envp_get_val are
** performed
*/
t_env *env_from_envp(char **envp)
{
t_env *env;

61
src/env/env_manip.c vendored
View file

@ -6,7 +6,7 @@
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/19 17:55:24 by kcolin #+# #+# */
/* Updated: 2025/03/21 18:49:56 by jguelen ### ########.fr */
/* Updated: 2025/05/02 12:45:01 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -16,11 +16,6 @@
#include "libft.h"
#include "../ft_errno.h"
/*
** Get the value corresponding to a given key in the given environment structure
**
** If not found, return null
*/
char *env_get_val(t_env *env, char *key)
{
t_env *node;
@ -31,17 +26,6 @@ char *env_get_val(t_env *env, char *key)
return (NULL);
}
/*
** Remove the first env node of the given linked list where the key matches the
** given key.
**
** In any given env linked list, there should only be one entry for a given key.
**
** If the linked list is empty or the pointer is NULL, nothing is done.
**
** If the linked list does not contain an element matching the given key,
** nothing is done.
*/
void env_rm_entry(t_env **env, char *key)
{
t_env *current;
@ -70,9 +54,6 @@ void env_rm_entry(t_env **env, char *key)
}
}
/*
** Add the given element to the end of the given linked list.
*/
static void env_add_back(t_env **env, t_env *new)
{
t_env *last;
@ -88,37 +69,6 @@ static void env_add_back(t_env **env, t_env *new)
last->next = new;
}
/*
** In the given env linked list, if an element with the given key exist, set its
** value to the one provided. If no such element exist, create a new one with
** the provided value.
**
** The provided key and value must be allocated. In case of error, they will be
** freed. In case a node matching the given key is found the provided key value
** is freed.
**
** If key or value is null, both key and value are freed and NULL is returned.
** ft_errno is set to FT_EINVAL. We therefore allow for a value to be NULL.
**
** If key is an empty string, key and value are freed, ft_errno is set to
** FT_EBADID, and NULL is returned.
**
** If there is a failure allocating a new node, NULL is returned and ft_errno is
** set to FT_EERRNO (malloc would have set it to ENOMEM).
**
** Returns a pointer to the first element in the linked list, or NULL on error.
**
** Warning: does not check for validity of a key beyond what is described above.
**
** Implementation notes: free2 always returns NULL
**
** Note: once you pass a key to this function, if you pass it a second time, it
** will cause bad behaviour.
**
** Once you passed key and/or value to this function, env is the owner of these
** values and is responsible for freeing them. Do not pass multiple times the
** same pointers to this function!
*/
t_env *env_set_entry(t_env **env, char *key, char *value)
{
t_env *node;
@ -146,15 +96,6 @@ t_env *env_set_entry(t_env **env, char *key, char *value)
return (*env);
}
/*
** Find and return a pointer to the node in the given env linked list where the
** key matches the given key.
**
** If the node is not found, return NULL.
**
** Note that this is a pointer to the middle of the linked list, the node is not
** copied.
*/
t_env *env_find_node_bykey(t_env *env, char *key)
{
while (env)

31
src/env/envp.c vendored
View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* envp.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/19 13:35:44 by kcolin #+# #+# */
/* Updated: 2025/02/19 13:43:19 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:40:06 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -14,18 +14,6 @@
#include "libft.h"
#include <stddef.h>
/*
** Get the key part of a line of envp
**
** given that line is of the form key=value, return a string containing key,
** which is allocated and must be freed.
**
** if line is null or is empty, return NULL
**
** if line contains no '=', return an copy of line
**
** if allocation fail, return NULL
*/
char *envp_get_key(char *line)
{
char *key;
@ -44,18 +32,6 @@ char *envp_get_key(char *line)
return (key);
}
/*
** Get the value part of a line of envp
**
** given that line is of the form key=value, return a string containing value,
** which is allocated and must be freed.
**
** if line is null or is empty, return NULL
**
** if line contains no '=', return an empty string
**
** if allocation fail, return NULL
*/
char *envp_get_val(char *line)
{
char *val_pointer;
@ -69,9 +45,6 @@ char *envp_get_val(char *line)
return (ft_strdup(val_pointer));
}
/*
** Free all memory related to a given envp structure
*/
void envp_destroy(char **envp)
{
int i;

View file

@ -6,7 +6,7 @@
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/04 19:55:22 by kcolin #+# #+# */
/* Updated: 2025/04/28 12:29:50 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:19:51 by kcolin ### ########.fr */
/* */
/* ************************************************************************** */
@ -29,9 +29,5 @@ void do_waitpid(t_minishell *app, int pid)
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

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* connec_pipe_cmd_execute.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/11 12:01:29 by kcolin #+# #+# */
/* Updated: 2025/04/30 11:49:59 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:46:28 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -38,8 +38,8 @@ static void fork_failure(t_minishell *app, int pipefd[2])
}
/*
** return the pid returned by fork. values greater than 0 indicate parentprocess
*/
** return the pid returned by fork.
*/
static int exec_pid1(t_minishell *app, int pipefd[2], t_cmd *cmd)
{
int pid1;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* here_doc.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/07 11:42:29 by kcolin #+# #+# */
/* Updated: 2025/04/29 16:56:08 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:47:33 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -22,19 +22,6 @@
char *expand_line(char *line, t_minishell *app);
void failed_to_open_tmp_file(void);
/*
** Read from infd into some file until a line that is exactly equal to the
** marker is read.
**
** Then unlink the file, and return a filedescriptor to it, seeked to the start.
**
** If the marker is not quoted, perform variable expansion on each line. If app
** is NULL, no variable expansions are performed.
**
** If NULL is given as argument, or marker->word is null, or infd is negative,
** or getting a random filename fails, or we are unable to create the file, or
** the file already exists, or any other error occurs, return NULL.
*/
char *here_doc(t_worddesc *marker, int infd, t_minishell *app)
{
int outfd;

View file

@ -5,8 +5,8 @@
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/17 11:50:08 by kcolin #+# #+# */
/* Updated: 2025/04/29 16:39:22 by kcolin ### ########.fr */
/* Created: 2025/05/02 14:36:33 by kcolin #+# #+# */
/* Updated: 2025/05/02 14:36:33 by kcolin ### ########.fr */
/* */
/* ************************************************************************** */
@ -16,13 +16,6 @@
#include "here_doc.h"
#include "../../sig/sig.h"
/*
** - check that arguments are not invalid
** - create a random filename
** - create the file
**
** do these operations, with error checking
*/
int setup_here_doc(t_worddesc *marker, int infd, char **filename)
{
int outfd;
@ -38,9 +31,6 @@ int setup_here_doc(t_worddesc *marker, int infd, char **filename)
return (outfd);
}
/*
** output line to outfd, get the next line from infd, and return it
*/
char *output_line_and_next(int infd, int outfd, char *line)
{
ft_dprintf(outfd, "%s", line);
@ -49,9 +39,6 @@ char *output_line_and_next(int infd, int outfd, char *line)
return (line);
}
/*
** closes outfd, reset correct sig mode handling
*/
char *finalize(int outfd, char *filename)
{
close(outfd);
@ -61,7 +48,6 @@ char *finalize(int outfd, char *filename)
int interupted(int outfd, char *filename)
{
ft_printf("\n");
close(outfd);
unlink(filename);
free(filename);
@ -70,11 +56,6 @@ int interupted(int outfd, char *filename)
return (-1);
}
/*
** check if line corresponds to marker
**
** if true is returned, line has been freed
*/
bool is_marker(char *line, t_worddesc *marker)
{
char *cleanline;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* random_filename.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/11 13:29:32 by kcolin #+# #+# */
/* Updated: 2025/03/11 13:30:33 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:48:11 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -17,17 +17,6 @@
#include <unistd.h>
#include "libft.h"
/*
** Genereate a random filename for use by here_doc
**
** The filename will be of the form
** /tmp/minishell_here_doc_XXXXXXXXXXX
** where each X is a random alphanumerical char
**
** Random bytes are generated by reading from /dev/urandom
**
** Return NULL and sets ft_errno on error
*/
char *here_doc_random_filename(void)
{
int randomfd;

View file

@ -3,20 +3,15 @@
/* ::: :::::::: */
/* strip_newline.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/11 13:31:22 by kcolin #+# #+# */
/* Updated: 2025/03/11 13:31:31 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:48:08 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "libft.h"
/*
** create a new string without the terminating newline of the given string.
**
** If the given string has no terminating newline, create a copy of the input
*/
char *strip_newline(char *str)
{
size_t last_char_idx;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* simple_cmd_execute.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/27 16:21:56 by kcolin #+# #+# */
/* Updated: 2025/04/30 15:08:01 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:49:22 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -49,9 +49,6 @@ static void command_not_found(t_simple_cmd *cmd, t_minishell *app)
app->last_return_value = 127;
}
/*
** Do all the post-processing steps relating to a command.
*/
static t_simple_cmd *post_process_command(t_simple_cmd *cmd, t_minishell *app)
{
simple_cmd_post_process_debug(cmd, app);

View file

@ -6,7 +6,7 @@
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/21 12:40:46 by kcolin #+# #+# */
/* Updated: 2025/04/24 16:49:17 by jguelen ### ########.fr */
/* Updated: 2025/05/02 13:09:45 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -15,13 +15,6 @@
#include <stdio.h>
#include <unistd.h>
/*
** get or set ft_errno
**
** if errno is FT_EGET, return ft_errno and do not set it
**
** else set ft_errno to errno, and return the previous value of ft_errno
*/
t_errno ft_errno(t_errno errno)
{
static t_errno ft_errno = FT_ESUCCESS;
@ -36,9 +29,6 @@ t_errno ft_errno(t_errno errno)
return (ft_errno);
}
/*
** get the current value of ft_errno
*/
t_errno ft_errno_get(void)
{
return (ft_errno(FT_EGET));

View file

@ -1,39 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* fuzz.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/30 16:01:18 by kcolin #+# #+# */
/* Updated: 2025/04/30 17:13:17 by kcolin ### ########.fr */
/* */
/* ************************************************************************** */
#include "fcntl.h"
#include "minishell.h"
#include "parser/cmd/cmd_destroy.h"
#include "parser/cmd_parsing.h"
#include "unistd.h"
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
t_minishell app;
bzero(&app, sizeof(t_minishell));
int null = open("/dev/null", O_RDONLY, 0);
char *line = (char *)calloc(size + 1, sizeof(char));
memcpy(line, data, size);
dup2(null, STDIN_FILENO);
close(null);
t_cmd *cmd = minishell_parse(&app, line);
cmd_destroy(cmd);
free(line);
return (0); // Values other than 0 and -1 are reserved for future use.
}

View file

@ -1,48 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* fuzz_hand_tester.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/30 17:30:53 by kcolin #+# #+# */
/* Updated: 2025/04/30 17:36:33 by kcolin ### ########.fr */
/* */
/* ************************************************************************** */
#include "fcntl.h"
#include "minishell.h"
#include "parser/cmd/cmd_destroy.h"
#include "parser/cmd_parsing.h"
#include "unistd.h"
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
t_minishell app;
bzero(&app, sizeof(t_minishell));
int null = open("/dev/null", O_RDONLY, 0);
if (argc != 2)
return (1);
FILE *in = fopen(argv[1], "rb");
fseek(in, 0, SEEK_END);
long fsize = ftell(in);
fseek(in, 0, SEEK_SET); /* same as rewind(f); */
char *line = malloc(fsize + 1);
fread(line, fsize, 1, in);
fclose(in);
line[fsize] = 0;
dup2(null, STDIN_FILENO);
close(null);
t_cmd *cmd = minishell_parse(&app, line);
cmd_destroy(cmd);
free(line);
return (0);
}

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* get_command.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/19 18:03:11 by kcolin #+# #+# */
/* Updated: 2025/04/29 15:04:59 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:10:04 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -20,9 +20,6 @@
#include "libft.h"
#include "get_command.h"
/*
** remove one '\n' from the end of the string, if it exists
*/
static char *strip_newline(char *str)
{
size_t last_char_idx;
@ -54,14 +51,6 @@ static char *handle_special_command(char *line, t_minishell *app)
return (line);
}
/*
** get a command line using readline.
**
** returned buffer must be freed by caller.
** will add command to history if appropriate.
**
** Also handles special commands, which are not further processed.
*/
char *get_command(t_minishell *app)
{
char *line;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* minishell.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/29 15:13/45 by kcolin #+# #+# */
/* Updated: 2025/04/29 15:13:45 by kcolin ### ########.fr */
/* Created: 2025/05/02 11:53:53 by kcolin #+# #+# */
/* Updated: 2025/05/02 13:10:24 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -24,11 +24,9 @@
#include "postprocess/fieldsplit/fieldsplit.h"
#include "postprocess/expansion/expand_wildcard.h"
#include "sig/sig.h"
#include "sig/sig_handlers.h"
#include "parser/cmd/cmd_debug.h"
/*
** execute command
*/
static t_subprocess execute_command(t_cmd *cmd, t_minishell *app)
{
int retvalue;
@ -49,24 +47,6 @@ static void debug_command(t_cmd *cmd, t_minishell *app)
cmd_root_debug(cmd);
}
/*
** Do all the post-processing steps relating to a simple command.
*/
/* static t_simple_cmd
* *post_process_command(t_simple_cmd *cmd, t_minishell *app) */
/* { */
/* if (simple_cmd_expand_vars(cmd, app) == NULL) */
/* return (simple_cmd_destroy(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); */
/* } */
/*
** Initialize main app structure
*/
static void app_init(t_minishell *app, char **envp)
{
ft_bzero(app, sizeof(t_minishell));
@ -88,6 +68,8 @@ int main(int argc, char *argv[], char **envp)
line = get_command(&app);
while (line != NULL)
{
if (g_signum != 0)
app.last_return_value = get_sig_retvalue();
cmd = minishell_parse(&app, line);
free(line);
debug_command(cmd, &app);
@ -95,8 +77,6 @@ int main(int argc, char *argv[], char **envp)
cmd_destroy(cmd);
if (retvalue == SUBPROCESS)
break ;
if (g_signum != 0)
readline_reset();
line = get_command(&app);
}
env_destroy(app.env);

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* minishell.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 14:02:47 by kcolin #+# #+# */
/* Updated: 2025/04/30 11:28:11 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:11:18 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -56,7 +56,6 @@ typedef struct s_redirect
int open_flags;
int c_flags; // flags for third arg of open (case O_CREAT).
t_redirectee redirectee; // fd or filename where to redirect source.
// used between var expansion and fieldsplit
char *unexpanded_filename;
char *here_doc_eof; // The here-document limiter if relevant.
} t_redirect;
@ -74,13 +73,7 @@ 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;
union u_value
{
@ -98,10 +91,6 @@ typedef enum e_connector
FT_OR,
} t_connector;
/*
** This exists to represent pipelines and AND or OR lists.
** does include left associativity.
*/
typedef struct s_connec_cmd
{
t_cmd *first;
@ -109,12 +98,6 @@ typedef struct s_connec_cmd
t_connector connector;
} t_connec_cmd;
/*
** We do not deal with { list; } grouping. This is therefore roughly
** equivalent to a subshell_com structure in bash. And therefore we
** define this only since it will still be a grouping regarding redirections
** AND require a subshell every time in the case of minishell.
*/
typedef struct s_group_cmd
{
t_cmd *cmd;
@ -123,7 +106,7 @@ typedef struct s_group_cmd
typedef struct s_simple_cmd
{
int line; //Probably unused.
int line;
t_wordlist *words; //argv in list form
t_redirect *redirections; //redirections to perform
} t_simple_cmd;

View file

@ -6,7 +6,7 @@
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 16:53:02 by kcolin #+# #+# */
/* Updated: 2025/04/30 18:00:28 by kcolin ### ########.fr */
/* Updated: 2025/05/02 14:35:54 by kcolin ### ########.fr */
/* */
/* ************************************************************************** */
@ -23,7 +23,8 @@ void redirect_destroy(t_redirect *redirect)
{
next = redirect->next;
free(redirect->here_doc_eof);
if (redirect->type == FT_HEREDOC)
if (redirect->type == FT_HEREDOC && redirect->redirectee.filename != NULL
&& redirect->redirectee.filename->word != NULL)
unlink(redirect->redirectee.filename->word);
worddesc_destroy(redirect->redirectee.filename);
free(redirect->unexpanded_filename);

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* cmds_parse.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/16 13:11:33 by kcolin #+# #+# */
/* Updated: 2025/04/16 13:12:15 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:49:54 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -17,9 +17,6 @@
#include "cmd_destroy.h"
#include "../connec_cmd/connec_reorient_subtree.h"
/*
** Parse list of commands or pipeline.
*/
t_cmd *minishell_cmds_parse(t_minishell *app, t_wordlist **tokens)
{
t_cmd *subtree;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* cmd_parsing.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/31 10:28:28 by jguelen #+# #+# */
/* Updated: 2025/04/25 13:17:56 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:00:27 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -50,9 +50,6 @@ static t_cmd *expecting_quote_error(t_minishell *app)
return (NULL);
}
/*
** 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;

View file

@ -3,23 +3,16 @@
/* ::: :::::::: */
/* identifier.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/19 14:43:57 by kcolin #+# #+# */
/* Updated: 2025/02/19 14:47:00 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:50:55 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "identifier.h"
#include "libft.h"
/*
** Return true if id is a valid bash identifier, false otherwise.
**
** A valid identifier starts with one of a-zA-Z_
** It can contain any char a-zA-Z0-9_
** It must contain at least one char
*/
bool is_identifier(char *id)
{
if (id == NULL || id[0] == '\0')

View file

@ -3,23 +3,15 @@
/* ::: :::::::: */
/* metacharacter.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <marvin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/06 15:23:31 by kcolin #+# #+# */
/* Updated: 2025/02/11 19:04:03 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:51:07 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "metacharacter.h"
// FIXME: use ft_strchr
/*
** return true if the character is a metacharacter
**
** Bash reference manual: A character that, when unquoted, separates words. A
** metacharacter is a space, tab, newline, or one of the following characters:
** |, &, (, ), <, or >.
*/
bool is_metacharacter(char c)
{
if (c == ' ' || c == '\t' || c == '\n' || c == '|' || c == '&'

View file

@ -3,25 +3,16 @@
/* ::: :::::::: */
/* operator_combo.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/17 16:30:54 by kcolin #+# #+# */
/* Updated: 2025/02/17 16:34:34 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:51:16 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include <stdbool.h>
#include "libft.h"
/*
** return true if c can be used as the next character for an operator in start
**
** recognized operators are:
** ||
** >>
** <<
** &&
*/
bool is_operator_combo(char *start, char c)
{
if (ft_strlen(start) != 1)

View file

@ -3,19 +3,16 @@
/* ::: :::::::: */
/* operator_start.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/17 16:21:03 by kcolin #+# #+# */
/* Updated: 2025/02/18 17:53:13 by jguelen ### ########.fr */
/* Updated: 2025/05/02 12:51:23 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "libft.h"
#include <stdbool.h>
/*
** Is the character the start of an operator?
*/
bool is_operator_start(char c)
{
if (ft_strchr("<>|&()", c) != NULL)

View file

@ -3,19 +3,16 @@
/* ::: :::::::: */
/* pipe.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/21 15:04:48 by kcolin #+# #+# */
/* Updated: 2025/02/21 15:06:00 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:51:30 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "pipe.h"
#include "libft.h"
/*
** return true if the given worddesc is a pipe operator "|"
*/
bool is_pipe(t_worddesc *word)
{
if (ft_strcmp("|", word->word) == 0)

View file

@ -3,18 +3,15 @@
/* ::: :::::::: */
/* quote.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/20 13:21:08 by kcolin #+# #+# */
/* Updated: 2025/02/20 13:22:12 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:51:44 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "quote.h"
/*
** return true if c is either ' or "
*/
bool is_quote(char c)
{
if (c == '\'' || c == '"')

View file

@ -5,8 +5,8 @@
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/30 10:35/43 by kcolin #+# #+# */
/* Updated: 2025/04/30 10:35:43 by kcolin ### ########.fr */
/* Created: 2025/05/02 14:40:34 by kcolin #+# #+# */
/* Updated: 2025/05/02 14:40:34 by kcolin ### ########.fr */
/* */
/* ************************************************************************** */
@ -83,7 +83,7 @@ t_redirect *redir_from_words(t_worddesc *operator,
here_doc(spec, STDIN_FILENO, app), 0, NULL, WORD_TOKEN);
worddesc_destroy(spec);
if (redir->redirectee.filename == NULL)
return (redirect_destroy(redir), ft_errno(FT_EERRNO), NULL);
return (redirect_destroy(redir), NULL);
}
else
redir->redirectee.filename = specifier;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* redirect_from_words.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/14 17:31:23 by kcolin #+# #+# */
/* Updated: 2025/04/14 17:32:12 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:52:22 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */

View file

@ -6,7 +6,7 @@
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/20 17:36:20 by kcolin #+# #+# */
/* Updated: 2025/04/28 16:43:53 by jguelen ### ########.fr */
/* Updated: 2025/05/02 12:53:03 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -44,9 +44,6 @@ static t_redirect *redirection_remove_quotes(t_redirect *in_list)
return (out_list);
}
/*
** do quote removal on all words of this simple cmd
*/
t_simple_cmd *simple_cmd_remove_quotes(t_simple_cmd *cmd)
{
t_wordlist *new;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* remove_quotes.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 12:00/08 by kcolin #+# #+# */
/* Updated: 2025/04/15 12:00:08 by kcolin ### ########.fr */
/* Created: 2025/04/15 12:00:08 by kcolin #+# #+# */
/* Updated: 2025/05/02 12:53:14 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -22,16 +22,6 @@ static bool is_ignore_mark(char c)
return (false);
}
/*
** use the marker in the given worddesc to create a new worddesc by removing
** unquoted quotes.
**
** The new worddesc will have the marker set to NULL.
**
** If word is null return null.
**
** In case of allocation failure, return null
*/
t_worddesc *remove_quotes(t_worddesc *word)
{
t_worddesc *output;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* simple_cmd.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/21 12:30:07 by kcolin #+# #+# */
/* Updated: 2025/04/29 13:09:43 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:53:31 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -16,11 +16,6 @@
#include "../redirect/redirect_debug.h"
#include "../cmd/cmd_destroy.h"
/*
** parse a wordlist and yield a simple command.
**
** takes ownership of words
*/
t_simple_cmd *simple_cmd_from_wordlist(t_wordlist *words)
{
t_simple_cmd *cmd;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* worddesc.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/13 17:20:36 by kcolin #+# #+# */
/* Updated: 2025/04/18 09:20:14 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:54:18 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -14,17 +14,6 @@
#include "libft.h"
#include <stdlib.h>
/*
** allocate a new worddesc with given flags, marker, and word.
**
** mark_string is initialized as a string of the same length as word, but filled
** with spaces
**
** return null in case of error, or if word is null
**
** In case of allocation error, word is freed, as well as any memory allocated
** in this function
*/
t_worddesc *worddesc_create(char *word, char flags, char *marker,
t_token_type type)
{
@ -42,9 +31,6 @@ t_worddesc *worddesc_create(char *word, char flags, char *marker,
return (retvalue);
}
/*
** free all memory associated with this worddesc
*/
void worddesc_destroy(t_worddesc *worddesc)
{
if (worddesc == NULL)
@ -54,9 +40,6 @@ void worddesc_destroy(t_worddesc *worddesc)
free(worddesc);
}
// copy the given worddesc, including all internal data
//
// return null on error
t_worddesc *worddesc_copy(t_worddesc *worddesc)
{
t_worddesc *out;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* worddesc.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/15 12:00:08 by kcolin #+# #+# */
/* Updated: 2025/04/17 14:30:48 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:54:56 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -49,27 +49,8 @@ typedef enum e_token_type
*/
typedef struct s_worddesc
{
/*
** The word itself
*/
char *word;
/*
** flags for this word
**s_worddesc
** See above for flag definitions
*/
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 ''', '"', '$', '&', ' '
**
** ' ' 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;
t_token_type token_type;
} t_worddesc;

View file

@ -6,7 +6,7 @@
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/13 17:07:01 by kcolin #+# #+# */
/* Updated: 2025/04/07 17:40:30 by jguelen ### ########.fr */
/* Updated: 2025/05/02 12:57:29 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -15,12 +15,6 @@
#include "unistd.h"
#include <stdlib.h>
/*
** create a new wordlist element, with next set to null and word set to the
** passed worddesc.
**
** in case of error, return null
*/
t_wordlist *wordlist_create(t_worddesc *word)
{
t_wordlist *retvalue;
@ -33,9 +27,6 @@ t_wordlist *wordlist_create(t_worddesc *word)
return (retvalue);
}
/*
** free all memory associated with this wordlist.
*/
void wordlist_destroy(t_wordlist *wordlist)
{
t_wordlist *prev;
@ -49,11 +40,6 @@ void wordlist_destroy(t_wordlist *wordlist)
}
}
/*
** get the worddesc at position idx in the given wordlist.
**
** return null if out of range or wordlist is null
*/
t_worddesc *wordlist_get(t_wordlist *wordlist, int idx)
{
if (wordlist == NULL || idx < 0)
@ -68,16 +54,6 @@ t_worddesc *wordlist_get(t_wordlist *wordlist, int idx)
return (wordlist->word);
}
/*
** push the given worddesc to the end of the given wordlist.
**
** if wordlist is null, create a new wordlist.
**
** returns a pointer to the first element in the wordlist.
**
** if malloc failure is encountered, all memory for this wordlist is freed, and
** null is returned.
*/
t_wordlist *wordlist_push(t_wordlist *wordlist, t_worddesc *worddesc)
{
t_wordlist *start;
@ -93,11 +69,6 @@ t_wordlist *wordlist_push(t_wordlist *wordlist, t_worddesc *worddesc)
return (start);
}
/*
** remove and return the first element in the given wordlist
**
** If wordlist is empty, return null.
*/
t_worddesc *wordlist_pop(t_wordlist **wordlist)
{
t_wordlist *first;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* wordlist.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 15:40:23 by kcolin #+# #+# */
/* Updated: 2025/04/16 15:10:29 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:58:06 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -15,22 +15,9 @@
# include "../worddesc/worddesc.h"
/*
** Linked-list of WORDS
**
** cf. section 3.2.4 of https://aosabook.org/en/v1/bash.html
**
** we are copying how bash does it.
*/
typedef struct s_wordlist
{
/*
** pointer to the next element in the list
*/
struct s_wordlist *next;
/*
** pointer to the word at this position in the list
*/
struct s_worddesc *word;
} t_wordlist;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* wordlist_copy.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/04 12:07:53 by kcolin #+# #+# */
/* Updated: 2025/03/10 15:05:51 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:55:06 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -14,9 +14,6 @@
#include "libft.h"
#include <stdlib.h>
// Make a copy of the given wordlist, copying all internal data as well.
//
// Return null in case of error.
t_wordlist *wordlist_copy(const t_wordlist *wordlist)
{
t_wordlist *outlist;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* wordlist_debug.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/24 18:20:00 by kcolin #+# #+# */
/* Updated: 2025/04/29 13:05:20 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:55:13 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -14,9 +14,6 @@
#include "libft.h"
#include "../../treedrawing.h"
/*
** Debug-print a wordlist
*/
void wordlist_debug(t_wordlist *wordlist, t_buffer *leader, bool is_last)
{
indent(leader, is_last);

View file

@ -3,21 +3,16 @@
/* ::: :::::::: */
/* wordlist_idx.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/09 15:14:27 by kcolin #+# #+# */
/* Updated: 2025/03/10 16:08:43 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:55:27 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "wordlist.h"
#include <stdlib.h>
/*
** Remove the element at the given index from the wordlist and return it.
**
** return null if out of range or wordlist is null
*/
t_worddesc *wordlist_pop_idx(t_wordlist **wordlist, int idx)
{
t_wordlist *iter;
@ -47,9 +42,6 @@ t_worddesc *wordlist_pop_idx(t_wordlist **wordlist, int idx)
return (free(temp), out);
}
/*
** Remove the element at the given index from the wordlist and destroy it.
*/
void wordlist_destroy_idx(t_wordlist **wordlist, int idx)
{
worddesc_destroy(wordlist_pop_idx(wordlist, idx));

View file

@ -6,7 +6,7 @@
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/08 17:29:05 by jguelen #+# #+# */
/* Updated: 2025/03/20 09:41:04 by jguelen ### ########.fr */
/* Updated: 2025/05/02 12:55:53 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -21,15 +21,6 @@ static void ft_swap_wordlist_contents(t_wordlist *list1, t_wordlist *list2)
list2->word = tmp_word;
}
/*
** The pivot is selected as in classic variations to be the last element of the
** list initially.
** The partitionning makes it so in the end all elements smaller than the pivot,
** which in this case will be determined by a simple order of growing ascii
** value on the word->word component of the wordlist, are on the left of the
** pivot value and all elements greater on the right.
** @RETURN the index at wich the pivot is now to be found.
*/
static int wordlist_quicksort_partition(t_wordlist *list, int start, int end)
{
int i;
@ -58,11 +49,6 @@ static int wordlist_quicksort_partition(t_wordlist *list, int start, int end)
return (i);
}
/*
** Returns the wordlist list sorted in ascending ascii order.
** Proceeds by directly swapping the inside contents and not by rewiring the
** nodes themselves.
*/
t_wordlist *wordlist_quicksort(t_wordlist *list, int start, int end)
{
int pivot;
@ -75,9 +61,6 @@ t_wordlist *wordlist_quicksort(t_wordlist *list, int start, int end)
return (list);
}
/*
** Is intended as a shortcut for practical use as a full list sort.
*/
t_wordlist *wordlist_quicksort_full(t_wordlist *list)
{
return (wordlist_quicksort(list, 0, wordlist_size(list) - 1));

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* wordlist_utils.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/10 09:51:34 by jguelen #+# #+# */
/* Updated: 2025/04/30 14:52:32 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:56:55 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -16,11 +16,6 @@
#include "../../../libft/libft.h"
#include "../../ft_errno.h"
/*
** Creates a new wordlist composed of only one element. Its next field is NULL
** and its word field is a copy of the worddesc passed as a parameter.
** Returns NULL in case of error.
*/
t_wordlist *wordlist_independant_create(t_worddesc *word)
{
t_wordlist *new;
@ -43,10 +38,6 @@ t_wordlist *wordlist_independant_create(t_worddesc *word)
return (new);
}
/*
** Returns the number of words present in the wordlist given as parameter.
** NOTE: Assumes list not to be circular.
*/
int wordlist_size(t_wordlist *wordlist)
{
int size;
@ -69,10 +60,6 @@ t_wordlist *wordlist_last(t_wordlist *list)
return (list);
}
/*
** Returns the node at index idx in list or NULL if idx is out
** of bounds.
*/
t_wordlist *wordlist_get_elem(t_wordlist *list, int idx)
{
if (list == NULL || idx < 0)

View file

@ -3,38 +3,26 @@
/* ::: :::::::: */
/* rule_utils.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/20 12:01:57 by kcolin #+# #+# */
/* Updated: 2025/02/20 13:18:22 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:58:26 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "rule_utils.h"
#include "../matchers/operator_combo.h"
/*
** return true if we are in an unquoted operator state
*/
bool unquoted_operator(t_token_build *builder)
{
return (unquoted(builder) && builder->currently_in_operator);
}
/*
** return true if we are in an unquoted state
*/
bool unquoted(t_token_build *builder)
{
return (builder->quote == '\0');
}
/*
** return true if the current char combines with the current token to form an
** operator.
**
** If current token is null, return false
*/
bool operator_combo(t_token_build *builder, char *original)
{
if (builder->cur_token == NULL)

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* tokenizing_1_5.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/19 13:20:01 by jguelen #+# #+# */
/* Updated: 2025/02/24 14:51:09 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:12:36 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -19,10 +19,6 @@
** https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
*/
/*
** 1. If the end of input is recognized, the current token (if any) shall be
** delimited.
*/
bool rule_eof(t_token_build *builder, char *original)
{
if (original[builder->idx] == '\0')
@ -33,11 +29,6 @@ bool rule_eof(t_token_build *builder, char *original)
return (false);
}
/*
** 2. If the previous character was used as part of an operator and the current
** character is not quoted and can be used with the previous characters to form
** an operator, it shall be used as part of that (operator) token.
*/
bool rule_combine_operator(t_token_build *builder, char *original)
{
if (unquoted_operator(builder) && operator_combo(builder, original))
@ -49,11 +40,6 @@ bool rule_combine_operator(t_token_build *builder, char *original)
return (false);
}
/*
** 3. If the previous character was used as part of an operator and the current
** character cannot be used with the previous characters to form an operator,
** the operator containing the previous character shall be delimited.
*/
bool rule_operator_end(t_token_build *builder, char *original)
{
if (unquoted_operator(builder) && !operator_combo(builder, original))
@ -64,15 +50,6 @@ bool rule_operator_end(t_token_build *builder, char *original)
return (false);
}
/*
** 4. If the current character is single-quote, or double-quote and it is not
** quoted, it shall affect quoting for subsequent characters up to the end of
** the quoted text. The rules for quoting are as described in Quoting . The
** result token shall contain exactly the characters that appear in the input,
** unmodified, including any embedded or enclosing quotes or substitution
** operators, between the <quotation-mark> and the end of the quoted text. The
** token shall not be delimited by the end of the quoted field.
*/
bool rule_quote(t_token_build *builder, char *original)
{
if (is_quote(original[builder->idx]))
@ -87,22 +64,6 @@ bool rule_quote(t_token_build *builder, char *original)
return (false);
}
/*
** 5. If the current character is an unquoted '$' or '`', the shell shall
** identify the start of any candidates for parameter expansion (Parameter
** Expansion), command substitution (Command Substitution), or arithmetic
** expansion (Arithmetic Expansion) from their introductory unquoted character
** sequences: '$' or "${", "$(" or '`', and "$((", respectively. The shell shall
** read sufficient input to determine the end of the unit to be expanded (as
** explained in the cited sections). While processing the characters, if
** instances of expansions or quoting are found nested within the substitution,
** the shell shall recursively process them in the manner specified for the
** construct that is found. The characters found from the beginning of the
** substitution to its end, allowing for any recursion necessary to recognize
** embedded constructs, shall be included unmodified in the result token,
** including any embedded or enclosing substitution operators or quotes. The
** token shall not be delimited by the end of the substitution.
*/
bool rule_var_substitution(t_token_build *builder, char *original)
{
if (original[builder->idx] == '$' && builder->quote != '\'')

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* tokenizing_6_10.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/19 13:21:18 by jguelen #+# #+# */
/* Updated: 2025/02/20 13:14:21 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:59:55 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -14,18 +14,11 @@
#include "rule_utils.h"
#include "../matchers/operator_start.h"
#include "../matchers/blank.h"
/*
** cf. Token Recognition section at
** https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
*/
/*
** 6. If the current character is not quoted and can be used as the first
** character of a new operator, the current token (if any) shall be delimited.
** The current character shall be used as the beginning of the next (operator)
** token.
*/
bool rule_new_operator(t_token_build *builder, char *original)
{
if (unquoted(builder) && is_operator_start(original[builder->idx]))
@ -37,10 +30,6 @@ bool rule_new_operator(t_token_build *builder, char *original)
return (false);
}
/*
** 7. If the current character is an unquoted <blank>, any token containing the
** previous character is delimited and the current character shall be discarded.
*/
bool rule_delimit_blank(t_token_build *builder, char *original)
{
if (unquoted(builder) && is_blank(original[builder->idx]))
@ -52,10 +41,6 @@ bool rule_delimit_blank(t_token_build *builder, char *original)
return (false);
}
/*
** 8. If the previous character was part of a word, the current character shall
** be appended to that word.
*/
bool rule_combine_word(t_token_build *builder, char *original)
{
if (builder->currently_in_word)
@ -67,9 +52,6 @@ bool rule_combine_word(t_token_build *builder, char *original)
return (false);
}
/*
** 10. The current character is used as the start of a new word.
*/
bool rule_new_word(t_token_build *builder, char *original)
{
new_word(builder, original[builder->idx]);

View file

@ -6,7 +6,7 @@
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/21 12:29:36 by kcolin #+# #+# */
/* Updated: 2025/04/24 16:48:19 by jguelen ### ########.fr */
/* Updated: 2025/05/02 13:00:12 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -22,16 +22,6 @@ static void set_quote_error(char quote)
ft_errno(FT_EUNCLOSED_SMPL_QUOTE);
}
/*
** split a string into words, respecting quotes etc.
**
** cf. Token Recognition section at
** https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
**
** words are separated with <blanks>, which are defined here
** https://pubs.opengroup.org/onlinepubs/9699919799/
** section 7.3.1 LC_CTYPE
*/
t_wordlist *minishell_wordsplit(char *original)
{
t_token_build token_build;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* expand_wildcard.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/03 19:28:28 by kcolin #+# #+# */
/* Updated: 2025/04/30 15:09:59 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:00:43 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -65,9 +65,6 @@ static t_redirect *redirect_expand_wildcards(t_redirect *in_list)
return (out_list);
}
/*
** returns null on error
*/
static t_wordlist *wordlist_expand_wildcards(t_wordlist *inlist)
{
t_wordlist *outlist;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* sig.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/20 10:26:05 by jguelen #+# #+# */
/* Updated: 2025/04/17 12:05:07 by kcolin ### ########.fr */
/* Created: 2025/05/02 12:16:37 by kcolin #+# #+# */
/* Updated: 2025/05/02 13:01:42 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -14,19 +14,8 @@
#include "signal.h"
#include "sig_handlers.h"
/*
** g_signum exists to store the value of a signal if relevant for later
** processing.
** NOTE: g_signum starts at 0 and should be set back to 0 after the information
** it stores has been properly processed.
*/
int g_signum = 0;
/*
** Catches SIGINT and SIGQUIT.
** Set to ignore SIGQUIT and catch SIGINT to set g_signum for
** further processing when in interactive mode.
*/
int set_interactive_mode_sig_handling(void)
{
struct sigaction sig_act;
@ -38,16 +27,12 @@ int set_interactive_mode_sig_handling(void)
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);
}
/*
** Set to ignore SIGINT and SIGQUIT signals if they are generated through
** a call to kill when the program is currently executing commands.
** Otherwise set g_signum to the number corresponding to the caught signal.
*/
int set_exec_mode_sig_handling(void)
{
struct sigaction sig_act;
@ -65,11 +50,6 @@ int set_exec_mode_sig_handling(void)
return (0);
}
/*
** Set to ignore SIGINT and SIGQUIT signals if they are generated through
** a call to kill when the program is currently executing commands.
** Otherwise set g_signum to the number corresponding to the caught signal.
*/
int set_here_doc_mode_sig_handling(void)
{
struct sigaction sig_act;

View file

@ -3,20 +3,16 @@
/* ::: :::::::: */
/* sig.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/19 18:21:55 by jguelen #+# #+# */
/* Updated: 2025/04/17 12:05:05 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:01:49 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef SIG_H
# define SIG_H
/*
** Needed to have the definitions relative to sigaction and siginfo
** correctly taken into account.
*/
# define _POSIX_C_SOURCE 200809L
# include "libft.h"
@ -32,6 +28,5 @@ extern int g_signum;
int set_interactive_mode_sig_handling(void);
int set_exec_mode_sig_handling(void);
int set_here_doc_mode_sig_handling(void);
void readline_reset(void);
#endif

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* sig_handlers.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/17 12:01:39 by kcolin #+# #+# */
/* Updated: 2025/04/29 15:13:42 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:01:15 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -17,9 +17,6 @@
#include "sig.h"
#include "libft.h"
/*
** redisplay prompt
*/
void sig_interactive(int signum)
{
if (signum == SIGINT)
@ -28,23 +25,30 @@ void sig_interactive(int signum)
ft_printf("\n");
rl_on_new_line();
rl_redisplay();
g_signum = signum;
}
}
void readline_reset(void)
int get_sig_retvalue(void)
{
rl_replace_line("", 0);
ft_printf("\n");
rl_redisplay();
int retvalue;
retvalue = 128 + g_signum;
g_signum = 0;
return (retvalue);
}
/*
** Stores the value of the caught signal in g_signum for later processing.
*/
void sig_cmd(int signum, siginfo_t *siginfo, void *context)
{
(void)context;
(void)siginfo;
if (signum == SIGQUIT)
{
ft_dprintf(STDERR_FILENO, "Quit (core dumped)\n");
}
else
{
ft_printf("\n");
}
g_signum = signum;
}

View file

@ -6,7 +6,7 @@
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/17 12:01:25 by kcolin #+# #+# */
/* Updated: 2025/04/17 12:03:07 by kcolin ### ########.fr */
/* Updated: 2025/05/02 12:03:53 by kcolin ### ########.fr */
/* */
/* ************************************************************************** */
@ -16,7 +16,7 @@
# include "signal.h"
void sig_interactive(int signum);
void readline_reset(void);
int get_sig_retvalue(void);
void sig_cmd(int signum, siginfo_t *siginfo, void *context);
#endif // SIG_HANDLERS_H

View file

@ -3,19 +3,15 @@
/* ::: :::::::: */
/* path_split.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/14 10:07:47 by jguelen #+# #+# */
/* Updated: 2025/03/14 12:54:49 by jguelen ### ########.fr */
/* Updated: 2025/05/02 13:02:15 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "path_split.h"
/*
** We do not ignore empty words here. The word count is therefore the number of
** times the separator appears + 1 or 0 if the string s is NULL.
*/
static size_t paths_count(const char *s, const char sep)
{
size_t count;
@ -46,13 +42,6 @@ void path_split_destroy(char **split)
free(split);
}
/*
** Copies and returns in a malloc-allocated string the first "word" of *s.
** the first word is defined here as the longest substring of *s starting at
** the beginning of *s and not including any c character.
** During this process it moves *s past the next c character.
** NOTE: '\0' should never be considered a valid separator.
*/
static char *ft_dup_firstword(char **s, const char c)
{
char *first_word;
@ -68,19 +57,6 @@ static char *ft_dup_firstword(char **s, const char c)
return (first_word);
}
/*
** A modified version of a common word-split but that does not ignore NULL
** entries i.e. if PATH is of the form
** PATH=/usr/bin:::/bin
** the result will be {"/usr/bin", "", "", "/bin", NULL}
** where all elements except the terminating NULL has been malloc-allocated to
** ensure an ease of both treatment and end-detection.
** The parameter separator is maintained even if in our specific case it is
** expected to simply be ':'.
** @Retun Returns a NULL-terminated array when each entry correspond to a
** the longest substring possible between separator characters or end of string
** in the normal case. Returns NULL in case of an allocation error.
*/
char **path_split(char const *s, const char separator)
{
char **split;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* path_split.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/14 10:11:29 by jguelen #+# #+# */
/* Updated: 2025/03/14 12:20:27 by jguelen ### ########.fr */
/* Updated: 2025/05/02 13:02:30 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -16,16 +16,6 @@
# include <stdlib.h>
# include "libft.h"
/*
** A modified version of a common word-split but that does not ignore NULL
** entries i.e. if PATH is of the form
** PATH=/usr/bin:::/bin
** the resul will be {"/usr/bin", "", "", "/bin", NULL}
** where all elements except the terminating NULL has been malloc-allocated to
** ensure an ease of both treatment and end-detection.
** The parameter separator is maintained even if in our specific case it is
** expected to simply be ':'.
*/
char **path_split(const char *s, char separator);
void path_split_destroy(char **split);

View file

@ -6,19 +6,12 @@
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/25 13:02:59 by jguelen #+# #+# */
/* Updated: 2025/03/14 09:55:09 by jguelen ### ########.fr */
/* Updated: 2025/05/02 13:02:37 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "replace_substr.h"
/*
** @RETURN Returns a new C-compliant malloc-allocated string corresponding to
** text where the substring starting in text at index index_start and ending
** at index_end is replaced in totality by the C-compliant string replacement
** excluding its NULL-byte.
** Returns NULL if an allocation error occurred.
*/
char *replace_in_str(const char *text, size_t index_start, size_t index_end,
const char *replacement)
{

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* simple_filename_exp.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/02 13:40:10 by jguelen #+# #+# */
/* Updated: 2025/04/24 17:58:39 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:03:26 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -18,13 +18,6 @@
#include <stdbool.h>
#include <sys/stat.h>
/*
** Returns a malloc-allocated string representing the full path to
** the command or executable corresponding to name or NULL in case of
** failure and sets ft_errno in accordance.
** NOTE: if name contains a '/' character then name is considered to
** be an absolute path.
*/
char *get_cmdpath(const char *name, t_minishell *app)
{
char *cmd_path;

View file

@ -6,7 +6,7 @@
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/18 18:40:14 by jguelen #+# #+# */
/* Updated: 2025/03/19 16:34:10 by jguelen ### ########.fr */
/* Updated: 2025/05/02 13:03:20 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -19,10 +19,6 @@
#include <stdlib.h>
#include <sys/stat.h>
/*
** Returns a malloc-allocated string corresponding to
** "path_dir/name"
*/
char *alloc_path(char *path_dir, char *name)
{
char *path;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* simple_filename_exp_utils_utils.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/24 17:56:47 by kcolin #+# #+# */
/* Updated: 2025/04/25 15:42:20 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:03:10 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -16,13 +16,6 @@
#include "subst.h"
#include "path_split.h"
/*
** The return value of this function is always safe to destroy with
** path_split_destroy.
** Returns a NULL-terminated array containing each possible path contained
** in the PATH environnement variable.
** Returns NULL in case of an allocation error.
*/
char **get_paths_array(t_env *env)
{
char **path_array;
@ -42,20 +35,6 @@ char **get_paths_array(t_env *env)
return (path_array);
}
/*
** Utility function that attempts to find a regular executable file for which
** the execution rights are granted. It disregards non-regular files and so in
** particular directories (note that using stat allows us to follow symlinks to
** their target if encountered). If a file is found here for which the execution
** permission is not granted and no elligible file was found before its path is
** stored inside oldpath so that in case no file is found to be executable it
** is the value stored in oldpath that will be used resulting in a Permission
** denied error. oldpath is to store the first occurrence of a regular file
** corresponding to the filepath but not executable.
**
** We disregard all stat(2) failures, since bash does the same and treats them
** all to mean that the file does not exist.
*/
static char *select_path(char *filepath, char **oldpath, char **path,
struct stat *fstat)
{
@ -77,19 +56,6 @@ static char *select_path(char *filepath, char **oldpath, char **path,
return (NULL);
}
/*
** This function exists to implement the bash behaviour specific to where
** the PATH variable has been set with a non-empty value.
** It checks the working directory if there is a void inside PATH and tries to
** find the executable command inside the path entry itself otherwise
** disregarding directories and selecting the first entry that both exists and
** is executable. If no such entry exists but one or more regular file(s)
** exist(s) with the correct name in one of the entries (it therefore lacks the
** execution permission) this function will return the first found.
** Returns NULL if an error occurred or nothing corresponds to filename
** in PATH. ft_errno is set in the course of this function to help distinguish
** the nature of the case.
*/
static char *deal_with_filled_path(char *filename, char **path,
struct stat *fstat)
{
@ -127,19 +93,6 @@ static char *filter_dir(struct stat fstat, char *filepath)
return (free(filepath), NULL);
}
/*
** This function searches the environment to resolve a full path for an
** executable file corresponding to filename. It deals with two types of bash
** behaviours: a PATH unset or corresponding to an empty value and a PATH set
** and with a non-empty value.
** In the first case it searches the current working directory for the file
** coorresponding to filename. If no regular file is found it returns NULL and
** distinguishes the case where a directory was found corresponding to filename
** by setting ft_errno to FT_ISDIR.
** The second case is dealt with using deal_with_filled_path (see the function
** itself for details).
** Returns NULL on error or if nothing is found.
*/
char *filepath_from_env(char *filename, t_minishell *app)
{
char *filepath;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* variable_subst.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/19 17:28:29 by kcolin #+# #+# */
/* Updated: 2025/04/18 09:22:10 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:05:15 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -16,35 +16,6 @@
#include "replace_substr.h"
#include "../env/env_manip.h"
/*
** @Param
** word: the worddesc to be modified
** i: the index at which the $ marking the start of expansion is located
** id_len: the length of the string that constitutes a valid bash
** identifier
** rep: a malloc-allocated string previously calculated to correspond to
** the actual value to set as the expansion
** This function exists to actually modify the worddesc word by modifying the
** word proper inside but also keep the marker string coherent with these
** modifications so that further steps can act properly with the type of
** expansion and properly field split (word split in the manual) the result or
** not. To this end if an expansion occurs outside quotes every character
** resulting from it is marked simply with a '$' character and is subject to
** future field splitting. If however, the expansion occurs within double quotes
** it is not to be subjected to field splitting in the future and every
** character resulting from such an expansion is marked with a '&'.
**
** cf. https://www.gnu.org/software/bash/manual/bash.html#Quoting
** section 3.5.7
** The shell scans the results of parameter expansion, command substitution, and
** arithmetic expansion that did not occur within double quotes for word
** splitting.
**
** NOTE: It frees the malloc-allocated string rep.
**
** @RETURN In case of allocation error returns NULL, oterwise returns word
** itself.
*/
static t_worddesc *word_update(t_worddesc *word, size_t i, size_t id_len,
char *rep)
{
@ -71,11 +42,6 @@ static t_worddesc *word_update(t_worddesc *word, size_t i, size_t id_len,
return (word);
}
/*
** Calculates the string corresponding to the value of the variable to be
** expanded in the word proper and returns it.
** The string returned is always malloc-allocated or NULL.
*/
static char *calculate_replacement(t_worddesc *word, t_minishell *app, size_t i,
size_t *id_len)
{
@ -106,22 +72,6 @@ static char *calculate_replacement(t_worddesc *word, t_minishell *app, size_t i,
return (free(id), rep);
}
/*
** @Param word should NOT be NULL.
** Replace the word field of the t_worddesc pointer word with a new string
** corresponding to the replacement of the '$' followed by a valid bash
** identifier with the corresponding value if present in env, the return value
** of the last foreground executed pipeline in the case of $? or nothing if
** the corresponding identifier does not have an entry in env.
** Similarly if the character following the '$' is neither '_' or
** alphanumerical the dollar is removed except for the appearance of a '\0'
** signaling the end of the word.
** Returns NULL and sets errno in case of error, or the worddesc pointer word
** itself if everything went normally.
**
** returns NULL and does not touch errno if this word should be deleted (because
** of the expansion of an unquoted empty variable).
*/
t_worddesc *word_var_expansion(t_worddesc *word, t_minishell *app)
{
size_t i;
@ -149,15 +99,6 @@ t_worddesc *word_var_expansion(t_worddesc *word, t_minishell *app)
return (word);
}
/*
** Returns the t_wordlist passed as a parameter where the words have been
** modified to contain strings that represent the result of parameter expansion
** where the introductory '$' character was not single quoted.
** We DO NOT take the '\' character into account as an escape character here
** under any circumstance per subject requirement.
**
** In case of error, all of list is freed.
*/
t_wordlist *wordlist_var_expansion(t_wordlist *list, t_minishell *app)
{
t_wordlist *in_list;

View file

@ -6,7 +6,7 @@
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/06 13:03:41 by kcolin #+# #+# */
/* Updated: 2025/03/19 09:03:31 by jguelen ### ########.fr */
/* Updated: 2025/05/02 13:03:56 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -16,11 +16,6 @@
#include "../env/env.h"
#include "../../libft/libft.h"
/*
** Returns a C-compliant malloc-allocated string of length len and composed
** only of the character c except for the terminating NULL-byte or NULL in
** case of an allocation error.
*/
char *construct_repeting_char_string(char c, size_t len)
{
char *new;
@ -32,24 +27,11 @@ char *construct_repeting_char_string(char c, size_t len)
return (new);
}
/*
** @RETURN Returns a malloc-allocated string representing the return value of
** the last foreground executed pipeline.
*/
char *expand_question_mark(t_minishell *app)
{
return (ft_itoa(app->last_return_value));
}
/*
** Returns the longest possible substring present in str starting at the
** beginning that follows the definition of a valid bash identifier.
** NOTE(reminder): a valid bash identifier (name) is a non-empty string
** starting with a '_' or an alphabetic character and composed only of '_'s or
** alphanumerical characters.
** The returned string is malloc-allocated.
** In case of an allocation error, NULL is returned.
*/
char *ft_get_longest_identifier(char *str)
{
char *key;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* wildcard_exp.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/20 15:01:38 by jguelen #+# #+# */
/* Updated: 2025/04/30 14:53:30 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:08:40 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -14,17 +14,12 @@
#include "../ft_errno.h"
#include "subst.h"
#include "replace_substr.h"
/******************************************************************************/
/* */
/* NOTE: The use of errno and the setting of it was OKed by Alexandru */
/* */
/******************************************************************************/
/******************************************************************************/
/* */
/* NOTE: The use of errno and the setting of it was OKed by Alexandru in this */
/* context. */
/* */
/******************************************************************************/
/*
** Returns a directory stream corresponding to the current directory.
**
*/
static DIR *open_current_dir(void)
{
char *cur_dir_path;
@ -51,13 +46,6 @@ static DIR *open_current_dir(void)
return (dir);
}
/*
** Adds filename to the end of the wordlist *list by creating a worddesc whose
** word is filename and marker makes filename be considered fully single
** quoted.
** NOTE: In case of error, this function destroys *list in a similar fashion
** as worddesc_create destroys its first parameter in case of failure.
*/
t_wordlist *add_file_to_list(t_wordlist **list, char *filename)
{
t_worddesc *file_desc;
@ -81,20 +69,6 @@ t_wordlist *add_file_to_list(t_wordlist **list, char *filename)
return (*list);
}
/*
** A function designed to present all possible * or ? filename expansions
** for the current directory. (? is not asked by the subject).
** Does not take into account any other wildcard and does only search the
** current working directory.
** @PARAM A C compliant character string representing a pattern for a filename.
** @RETURN Returns a wordlist for which each entry corresponds to a filename
** that matches pattern->word if any file matches in the current directory.
** Otherwise return file_pattern itself if nothing matches the perceived
** pattern. This list should be alphabetically sorted.
** Can return NULL only in case of error.
** NOTE: this function never becomes the owner of file_pattern to maintain
** coherency of use.
*/
static t_wordlist *expand_star_core(t_worddesc *file_pattern)
{
struct dirent *new;
@ -120,17 +94,6 @@ static t_wordlist *expand_star_core(t_worddesc *file_pattern)
return (wordlist_quicksort_full(file_wordlist));
}
/*
** Returns 1 if and only if filename is recognized by pattern, 0
** otherwise or -1 in case of error.
** Takes only into account the * wildcard or ?, those characters
** NOTE: for a pattern to accept '.' as the first character of a filename
** it must be explicitly matched (only for the first character though).
** Similarly, '/' is never to be matched except if given explicitly as per
** bash requirement. This is a note in case of future expansion as in our case
** here we do not have to deal with that since we concern ourselves only with
** the current working directory.
*/
char fits_pattern(char *str, t_worddesc *pattern)
{
char **pattern_check;

View file

@ -3,19 +3,16 @@
/* ::: :::::::: */
/* wildcard_exp_utils.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/21 13:46/14 by kcolin #+# #+# */
/* Updated: 2025/03/24 13:51:30 by jguelen ### ########.fr */
/* Created: 2025/03/21 13:46:14 by kcolin #+# #+# */
/* Updated: 2025/05/02 13:06:22 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "subst.h"
#include <stdbool.h>
/*
** Cleanly disposes of a pattern checker two dimensionnal array.
*/
void destroy_pattern_check(char **pattern_check, size_t len)
{
size_t i;
@ -29,17 +26,6 @@ void destroy_pattern_check(char **pattern_check, size_t len)
free(pattern_check);
}
/*
** Returns 1 if the currently examined characters in str and pattern can match
** allowing to preserve the validity of previous matches one "character" behind
** in both strings.
** i.e. serves to detect when the currently examined character of str in index i
** and the current character of pattern match exactly or the character in
** pattern under scrutiny is an unquoted '?'. There is one exception to wit
** the character '.' can only be matched exactly if it is the first character of
** both str and pattern and explicitly given. That is in this case ? cannot
** match it even if it can under all other circumstance.
*/
static int same_character_or_one_char_wild(char *str, t_worddesc *pattern,
size_t i, size_t j)
{
@ -50,9 +36,6 @@ static int same_character_or_one_char_wild(char *str, t_worddesc *pattern,
&& pattern->marker[j - 1] != '&'));
}
/*
** Condition to know if the wildcard '*' can catch something.
*/
static int at_star_in_pattern(char *str, t_worddesc *pattern,
size_t i, size_t j)
{
@ -64,10 +47,6 @@ static int at_star_in_pattern(char *str, t_worddesc *pattern,
&& pattern->marker[j - 1] != '&');
}
/*
** Sets true where the pattern would match an empty string (the most basic
** prefix of str).
*/
static void init_pattern_checker(t_worddesc *pattern, char **checker)
{
size_t i;
@ -84,48 +63,6 @@ static void init_pattern_checker(t_worddesc *pattern, char **checker)
}
}
/*
** Fills the table which contains in its most low and right cell 0 if
** str does not match the pattern and 1 otherwise.
** This construction is only done for the current diectory so no special
** treatment is to be considered for '/' characters which otherwise have
** to be matched explicitely. We do however consider the case where the '.'
** character cannot be matched except explicitely when in first position
** in str.
** This function has the goal of building an array of prefix recognition
** where each and every cell i,j in the array corresponds to the matching of the
** prefix str[0 .. i - 1] with pattern[0 .. j - 1].
** the array has the form:
** empty string will be noted ε.
** pattern | | p[0] | . . . | p[pattern_len - 1]
** _s_\_(p)_|___________________|__________|_______|____________________
** i \ j | 0 | 1 | . . . | pattern_len
** _____\___|___________________|__________|_______|____________________
** 0 | ε == ε | | | pattern == ε
** _________|___________________|__________|_______|____________________
** 1 | str[0..0] == ε |str[0..0] | |
** | |== p[0..0]| | pattern == p[0..0]
** _________|___________________|__________|_______|____________________
** . | | | |
** . | | | |
** . | | | |
** _________|___________________|__________|_______|____________________
** str len|| str == ε | str == | |
** [len - 1]| | p[0..0] | | pattern == str
**
** There are several ways to preserve a match while going forward either in str,
** in pattern or both: either you are matching corresponding characters or a '?'
** wildcard in pattern with str or you are considering a '*' wildcard in
** pattern. In those cases you can inherit from a match previously reached.
** Otherwise, no match can be found.
** When considering two characters that are identical (or '?' in pattern) you
** will match exactly if amd only if you matched one character before in both
** strings.
** In the case of the consideration of the '*' wildcard in pattern, since it
** matches both ε and any arbitrary string, you will match if before seeing the
** '*' in pattern you already matched (case ε i.e. checker[i-1][j]) or if you
** englobe the character currently considered in str (checker[i][j - 1]).
*/
void build_pattern_checks(char *str, t_worddesc *pattern,
char **checker)
{

View file

@ -6,7 +6,7 @@
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/15 15:09:56 by jguelen #+# #+# */
/* Updated: 2025/04/30 14:04:57 by jguelen ### ########.fr */
/* Updated: 2025/05/02 13:06:44 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -14,10 +14,6 @@
#include "subst.h"
#include "replace_substr.h"
/*
** Removes unquoted quotes and from file_pattern->word and keeps
** file_pattern->marker congruent.
*/
void clean_pattern(t_worddesc *file_pattern)
{
int i;
@ -42,13 +38,6 @@ void clean_pattern(t_worddesc *file_pattern)
}
}
/*
** Checks if a worddesc is to be considered a pattern as bash would in a
** reduced capacity, to wit if it contains at least one unquoted '?' or '*'
** character.
** Returns 1 if the worddesc pointed to by desc is to be considered a pattern,
** 0 otherwise.
*/
int ispattern(t_worddesc *desc)
{
size_t i;

View file

@ -3,10 +3,10 @@
/* ::: :::::::: */
/* treedrawing.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <kcolin@42.fr> +#+ +:+ +#+ */
/* By: jguelen <jguelen@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/18 12:24:25 by kcolin #+# #+# */
/* Updated: 2025/03/19 13:50:37 by kcolin ### ########.fr */
/* Updated: 2025/05/02 13:11:33 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
@ -21,11 +21,6 @@
# define VERTICAL " │ "
# define SPACE " "
/* # define CROSS " |- " */
/* # define CORNER " \\- " */
/* # define VERTICAL " | " */
/* # define SPACE " " */
void indent(t_buffer *indent, bool is_last);
void dedent(t_buffer *indent, bool is_last);

View file

@ -1,103 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* word_search.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/02 14:27:58 by jguelen #+# #+# */
/* Updated: 2025/03/02 18:12:00 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#include "word_search.h"
/*
** @PARAM needle_len must be the length of the string needle.
** Calculates the Knuth-Morris-Pratt array for the word needle to then determine
** what shifts to make later for the searching window of the searching of needle
** in a text in the future.
** Returns the KMP array for needle or NULL if an allocation error occurs or
** needle is either NULL or needle_len is 0.
*/
static int *create_kmp_array(char *needle, size_t needle_len)
{
int *kmp;
size_t i;
int j;
if (!needle || !needle_len)
return (NULL);
i = 0;
j = -1;
kmp = malloc((needle_len + 1) * sizeof(int));
if (!kmp)
return (NULL);
kmp[0] = -1;
while (i < needle_len)
{
while (j > -1 && needle[i] != needle[j])
j = kmp[j];
i++;
j++;
if (needle[i] == needle[j])
kmp[i] = kmp[j];
else
kmp[i] = j;
}
return (kmp);
}
/*
** @Param Should only be provided with arguments that are neither NULL or empty
** Could be extended to report all occurrences of needle in haystack
** but for now only reports the first.
** (cf http://monge.univ-mlv.fr/~lecroq/string/node8.html#SECTION0080)
*/
ssize_t word_search_kmp(char *haystack, char *needle)
{
int i;
int j;
int *kmp;
int needle_len;
int haystack_len;
needle_len = ft_strlen(needle);
kmp = create_kmp_array(needle, needle_len);
if (!kmp)
return (-2);
i = 0;
j = 0;
haystack_len = ft_strlen(haystack);
while (j < haystack_len)
{
while (i > -1 && needle[i] != haystack[j])
i = kmp[i];
j++;
if (++i >= needle_len)
return (free(kmp), j - i);
}
free(kmp);
return (-1);
}
/*
** Should not be given NULL or empty arguments.
*/
ssize_t word_search_brute(char *haystack, char *needle)
{
ssize_t i;
ssize_t needle_len;
ssize_t haystack_len;
needle_len = ft_strlen(needle);
haystack_len = ft_strlen(haystack);
i = 0;
while (i <= (haystack_len - needle_len))
{
if (ft_memcmp(haystack + i, needle, needle_len) == 0)
return (i);
i++;
}
return (-1);
}

View file

@ -1,69 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* word_search.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/02 14:10:36 by jguelen #+# #+# */
/* Updated: 2025/03/02 17:56:02 by jguelen ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef WORD_SEARCH_H
# define WORD_SEARCH_H
# include <stdlib.h>
# include "libft.h"
/*
** An implementation of the Knuth, Morris and Pratt algorithm for exact word
** searching in a text.
** cf. http://monge.univ-mlv.fr/~lecroq/string/node8.html#SECTION0080
**
** The design of the Knuth-Morris-Pratt algorithm follows a tight analysis of
** the Morris and Pratt algorithm. Let us look more closely at the Morris-Pratt
** algorithm. It is possible to improve the length of the shifts.
**
** Consider an attempt at a left position j, that is when the the window is
** positioned on the text factor y[j .. j + m - 1]. Assume that the first
** mismatch occurs between x[i] and y[i+j] with 0 < i < m. Then,
** x[0 .. i - 1] = y[j .. i + j - 1] = u and a = x[i] != y[i + j] = b.
**
** When shifting, it is reasonable to expect that a prefix v of the pattern
** matches some suffix of the portion u of the text. Moreover, if we want to
** avoid another immediate mismatch, the character following the prefix v in the
** pattern must be different from a. The longest such prefix v is called the
** tagged border of u (it occurs at both ends of u followed by different
** characters in x).
**
** This introduces the notation: let kmpNext[i] be the length of the longest
** border of x[0 .. i - 1] followed by a character c different from x[i] and -1
** if no such tagged border exits, for 0 < i leq m. Then, after a shift, the
** comparisons can resume between characters x[kmpNext[i]] and y[i+j] without
** missing any occurrence of x in y, and avoiding a backtrack on the text
** (see figure 7.1). The value of kmpNext[0] is set to -1.
** The table kmpNext can be computed in O(m) space and time before the searching
** phase, applying the same searching algorithm to the pattern itself, as if
** x = y.
**
** The searching phase can be performed in O(m + n) time. The Knuth-Morris-Pratt
** algorithm performs at most 2 * n - 1 text character comparisons during the
** searching phase. The delay (maximal number of comparisons for a single text
** character) is bounded by log_Phi(m) where Phi is the golden ratio.
*/
/*
** Uses the Knuth-Morris-Pratt algorithm.
** Returns the index where an occurrence of needle was found in haystack, or
** -1 if no such occurrence was found and -2 in case of allocation error.
*/
ssize_t word_search_kmp(char *haystack, char *needle);
/*
** The simple, familiar brute force searching algorithm of a word in a text.
** Returns the index where an occurrence of needle is found in haystack, or
** -1 if no such occurrence exists.
*/
ssize_t word_search_brute(char *haystack, char *needle);
#endif

1690
test.sh

File diff suppressed because it is too large Load diff

View file

@ -1,60 +0,0 @@
# make gets confused if a file with the same name exists in the sources, so some
# file are prefixed with test_
rawtests = \
test_here_doc \
expansion \
test_cmdlist_use_after_free \
test_wordlist_idx \
test_quote_removal \
test_metacharacters \
test_parse_cmdlists \
test_parse_pipelines \
test_parse_simple_cmds \
test_env_manip \
test_word_splitting \
ifeq ($(CFLAGS),)
CFLAGS = -Wall -Wextra -Werror -g
endif
tests = $(addprefix test_,$(rawtests))
run_tests = $(addprefix run_test_,$(rawtests))
test_objs = $(addsuffix .o,$(tests))
objs := $(addprefix ../,$(objs)) \
testutil.o \
parse_cmdlist.o \
parse_pipeline.o \
all_objs = $(objs) $(test_objs)
deps = $(all_objs:.o=.d)
LDLIBS = \
-lreadline \
-lft
LIBFTDIR = ../libft/
LIBFT = $(LIBFTDIR)libft.a
IFLAGS = -I$(LIBFTDIR)
LINCLUDE = -L$(LIBFTDIR)
.PHONY: run fclean run_test_%
.NOTPARALLEL: run
run: $(run_tests)
@echo "Finished running C tests"
-include $(deps)
%.o: %.c
$(CC) -c $(CFLAGS) $(IFLAGS) -o $*.o $*.c
$(CC) -MM $(CFLAGS) $(IFLAGS) -MT $*.o $*.c > $*.d
test_%: %.o $(objs)
$(CC) $(CFLAGS) -rdynamic -o $@ $*.o $(objs) $(LINCLUDE) $(LDLIBS)
run_test_%: test_%
@echo
@echo "====== Now running test: $* ======"
@echo
./test_$*
@echo "====== End of test: $* ======"
fclean:
rm -f $(tests)

View file

View file

View file

View file

@ -1 +0,0 @@
echo hello exp

View file

@ -1 +0,0 @@
echo hello

View file

@ -1 +0,0 @@
echo hello

View file

@ -1,265 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* expansion.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/04/09 15:50/06 by khais #+# #+# */
/* Updated: 2025/04/09 15:50:06 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include "testutil.h"
#include "../src/subst/replace_substr.h"
#include "../src/subst/subst.h"
#include "../src/env/env.h"
#include "../src/env/env_manip.h"
#include "../src/parser/wordsplit/wordsplit.h"
#include "../src/parser/wordlist/wordlist.h"
/*
** Test file for the different expansion/substitution types of minishell.
*/
static void test_replace_in_str_insert(void)
{
char *line;
// insertion is not supported, we must replace at least one char
line = replace_in_str("le canari qui fait cuicui", 3, 3, "petit ");
assert_strequal("le petit anari qui fait cuicui", line);
free(line);
}
static void test_insert_instr(void)
{
char *line;
line = replace_in_str("abcdefghijk", 1, 4, "souris");
assert_strequal("asourisfghijk", line);
free(line);
line = replace_in_str("abcdefgh" , 2, 2, "non ce n'est pas ma faute");
assert_strequal("abnon ce n'est pas ma fautedefgh", line);
free(line);
line = replace_in_str("le petit canari qui fait cuicui", 3, 8, "");
assert_strequal("le canari qui fait cuicui", line);
free(line);
line = replace_in_str("le petit canari qui fait cuicui", 3, 8, NULL);
assert_strequal("le canari qui fait cuicui", line);
free(line);
line = replace_in_str("le canari qui fait cuicui", 2, 2, " petit ");
assert_strequal("le petit canari qui fait cuicui", line);
free(line);
// premier charactere debut du remplacement
line = replace_in_str("le petit canari qui fait cuicui", 0, 1, "Le");
assert_strequal("Le petit canari qui fait cuicui", line);
free(line);
do_leak_check();
}
/*
** NOTE/REMINDER: I currently replace $0 to $9 with nothing but I can change
** this behavior at any point if you would rather we ignored them and therefore
** did not replace those.
*/
static void test_env_variable_expansion(void)
{
t_wordlist *list;
t_minishell *app;
char *value;
char *key;
value = ft_strdup("/home/jguelen/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin");
key = ft_strdup("PATH");
app = ft_calloc(1, sizeof(t_minishell));
app->env = env_set_entry(&(app->env), key, value);
key = ft_strdup("USER");
value = ft_strdup("jguelen");
app->env = env_set_entry(&(app->env), key, value);
list = minishell_wordsplit("$USER$USER $PATH \"'$USER'$USER\" a$test'b' '$USER'");
list = wordlist_var_expansion(list, app);
assert_strequal("jguelenjguelen", list->word->word);
assert_strequal("/home/jguelen/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin", list->next->word->word);
assert_strequal("\"'jguelen'jguelen\"", list->next->next->word->word);
assert_strequal("a'b'", list->next->next->next->word->word);
assert_strequal("'$USER'", list->next->next->next->next->word->word);
wordlist_destroy(list);
env_destroy(app->env);
free(app);
do_leak_check();
}
static void test_env_var_expansion_with_invalid_ident(void)
{
t_wordlist *list;
t_minishell *app;
char *value;
char *key;
value = ft_strdup("value");
key = ft_strdup("VAR");
app = ft_calloc(1, sizeof(t_minishell));
app->env = env_set_entry(&(app->env), key, value);
list = minishell_wordsplit("$''VAR");
list = wordlist_var_expansion(list, app);
assert_strequal("''VAR", list->word->word);
wordlist_destroy(list);
env_destroy(app->env);
free(app);
do_leak_check();
}
static void test_env_var_expansion_with_trailing_dollar(void)
{
t_wordlist *list;
t_minishell *app;
char *value;
char *key;
value = ft_strdup("value");
key = ft_strdup("VAR");
app = ft_calloc(1, sizeof(t_minishell));
app->env = env_set_entry(&(app->env), key, value);
list = minishell_wordsplit("$VAR$");
list = wordlist_var_expansion(list, app);
assert_strequal("value$", list->word->word);
wordlist_destroy(list);
env_destroy(app->env);
free(app);
do_leak_check();
}
static void test_cmd_path_expansion(void)
{
t_minishell *app;
char *key;
char *value;
char *cmdpath;
key = ft_strdup("PATH");
value = ft_strdup("./port:/usr/bin");
app = ft_calloc(1, sizeof(t_minishell));
app->env = env_set_entry(&(app->env), key, value);
cmdpath = get_cmdpath("ls", app);
assert_strequal("./port/ls", cmdpath);
free(cmdpath);
value = ft_strdup(":/usr/bin");
key = ft_strdup("PATH");
app->env = env_set_entry(&(app->env), key, value);
cmdpath = get_cmdpath("ls", app);
assert_strequal("./ls", cmdpath);
free(cmdpath);
cmdpath = get_cmdpath("peekaboo", app);
assert(cmdpath == NULL);
free(cmdpath);
env_destroy(app->env);
free(app);
do_leak_check();
}
static void test_filename_star_expansion(void)
{
t_worddesc *filepattern;
t_wordlist *expanded;
t_wordlist *tmp;
//test1 Everything except . and ..
ft_printf("test_filename_star_expansion\n");
filepattern = create_single_word("*");
expanded = expand_star(filepattern);
tmp = expanded;
assert_strequal("aba", tmp->word->word);
tmp = tmp->next;
assert(tmp);
assert_strequal("abcda", tmp->word->word);
tmp = tmp->next;
assert(tmp);
assert_strequal("aiia", tmp->word->word);
tmp = tmp->next;
assert(tmp);
assert_strequal("axr", tmp->word->word);
tmp = tmp->next;
assert(tmp);
assert_strequal("directory", tmp->word->word);
tmp = tmp->next;
assert(tmp);
assert_strequal("exp", tmp->word->word);
tmp = tmp->next;
assert(tmp);
assert_strequal("ls", tmp->word->word);
tmp = tmp->next;
assert(tmp);
assert_strequal("port", tmp->word->word);
tmp = tmp->next;
assert(tmp);
assert_strequal("yuhbqa", tmp->word->word);
tmp = tmp->next;
assert(!tmp);
wordlist_destroy(expanded);
worddesc_destroy(filepattern);
//test2
filepattern = create_single_word("**a*'b'*a*");
expanded = expand_star(filepattern);
assert(wordlist_size(expanded) == 2);
assert_strequal("aba", expanded->word->word);
assert_strequal("abcda", expanded->next->word->word);
worddesc_destroy(filepattern);
wordlist_destroy(expanded);
//test ., .. and .plop
filepattern = create_single_word(".*");
expanded = expand_star(filepattern);
assert(wordlist_size(expanded) == 3);
assert_strequal(".", expanded->word->word);
assert_strequal("..", expanded->next->word->word);
assert_strequal(".plop", expanded->next->next->word->word);
worddesc_destroy(filepattern);
wordlist_destroy(expanded);
//test zero result
filepattern = create_single_word("e*x***p*b");
expanded = expand_star(filepattern);
assert(expanded);
assert_strequal(filepattern->word, expanded->word->word);
worddesc_destroy(filepattern);
wordlist_destroy(expanded);
do_leak_check();
}
static void simple_sub_test(void)
{
t_wordlist *list;
t_minishell *app;
char *value;
char *key;
value = ft_strdup("val");
key = ft_strdup("KEY");
app = ft_calloc(1, sizeof(t_minishell));
app->env = env_set_entry(&(app->env), key, value);
list = minishell_wordsplit("v$KEY");
list = wordlist_var_expansion(list, app);
assert_strequal("vval", list->word->word);
wordlist_destroy(list);
env_destroy(app->env);
free(app);
do_leak_check();
}
int main(void)
{
test_env_var_expansion_with_trailing_dollar();
test_env_var_expansion_with_invalid_ident();
test_replace_in_str_insert();
if (chdir("./expand_test") == -1)
assert("chdir failure" && false);
simple_sub_test();
test_insert_instr();
test_env_variable_expansion();
test_cmd_path_expansion();
test_filename_star_expansion();
return (0);
}

View file

@ -1 +0,0 @@
input

View file

@ -1,2 +0,0 @@
input
EOF

View file

@ -1 +0,0 @@
EOF

View file

@ -1,3 +0,0 @@
hello
$USER
end

View file

@ -1,26 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* parse_cmdlist.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/11 15:54:43 by khais #+# #+# */
/* Updated: 2025/03/18 15:05:39 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "../src/parser/cmdlist/cmdlist.h"
#include "../src/parser/wordsplit/wordsplit.h"
#include "libft.h"
#include "unistd.h"
#include <assert.h>
t_cmdlist *parse_cmdlist(char *input)
{
ft_dprintf(STDERR_FILENO, "Now checking command list with input [%s]\n", input);
t_wordlist *words = minishell_wordsplit(input);
t_cmdlist *cmd = cmdlist_from_wordlist(words);
wordlist_destroy(words);
return (cmd);
}

View file

@ -1,20 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* parse_cmdlist.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/11 15:55:10 by khais #+# #+# */
/* Updated: 2025/03/18 15:05:25 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef PARSE_CMDLIST_H
# define PARSE_CMDLIST_H
# include "../src/parser/cmdlist/cmdlist.h"
t_cmdlist *parse_cmdlist(char *input);
#endif // PARSE_CMDLIST_H

View file

@ -1,23 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* parse_pipeline.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/11 16:01:48 by khais #+# #+# */
/* Updated: 2025/03/11 16:24:41 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "../src/parser/wordsplit/wordsplit.h"
#include "../src/parser/pipeline/pipeline.h"
#include <assert.h>
t_pipeline *parse_pipeline(char *input)
{
t_wordlist *words = minishell_wordsplit(input);
t_pipeline *pipeline = pipeline_from_wordlist(words);
wordlist_destroy(words);
return (pipeline);
}

View file

@ -1,20 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* parse_pipeline.h :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/11 16:01:00 by khais #+# #+# */
/* Updated: 2025/03/11 16:02:15 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef PARSE_PIPELINE_H
# define PARSE_PIPELINE_H
# include "../src/parser/pipeline/pipeline.h"
t_pipeline *parse_pipeline(char *input);
#endif // PARSE_PIPELINE_H

View file

@ -1,28 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* test_cmdlist_use_after_free.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/03 11:40:37 by khais #+# #+# */
/* Updated: 2025/03/18 15:05:18 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "../src/parser/wordsplit/wordsplit.h"
#include "../src/parser/cmdlist/cmdlist.h"
#include "libft.h"
#include <assert.h>
int main(void)
{
t_wordlist *words;
t_cmdlist *cmd;
cmd = NULL;
words = minishell_wordsplit("|");
cmd = cmdlist_from_wordlist(words);
wordlist_destroy(words);
assert(cmd == NULL);
return (0);
}

View file

@ -1,155 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* test_env_manip.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/18 15:11:14 by khais #+# #+# */
/* Updated: 2025/02/19 16:41:53 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "../src/env/env.h"
#include "../src/env/env_convert.h"
#include "../src/env/env_manip.h"
#include "../src/env/envp.h"
#include "../src/ft_errno.h"
#include "libft.h"
#include "unistd.h"
#include <assert.h>
static void test_envp_parsing(char *line, char *expected_key, char *expected_value)
{
char *got_key = envp_get_key(line);
char *got_value = envp_get_val(line);
ft_dprintf(STDERR_FILENO, "for envp value '%s', expecting key to eq '%s', and got '%s'\n", line, expected_key, got_key);
assert(expected_key == got_key || ft_strcmp(expected_key, got_key) == 0);
ft_dprintf(STDERR_FILENO, "for envp value '%s', expecting value to eq '%s', and got '%s'\n", line, expected_value, got_value);
assert(expected_value == got_value || ft_strcmp(expected_value, got_value) == 0);
free(got_key);
free(got_value);
}
static void assert_env_value(t_env *env, char *key, char *value)
{
char *got_value = env_get_val(env, key);
ft_dprintf(STDERR_FILENO, "expecting %s=%s to exist, and got %s=%s\n", key, value, key, got_value);
assert(ft_strcmp(value, got_value) == 0);
}
static void test_env_set_entry_empty_env(void)
{
t_env *env;
env = NULL;
assert(env_set_entry(&env, ft_strdup("VAR"), ft_strdup("hello")) != NULL);
assert_env_value(env, "VAR", "hello");
assert(1 == env_get_size(env));
env_destroy(env);
}
static void test_env_set_entry_existing_value(void)
{
t_env *env;
env = NULL;
assert(env_set_entry(&env, ft_strdup("VAR"), ft_strdup("hello")) != NULL);
assert(env_set_entry(&env, ft_strdup("VAR"), ft_strdup("there")) != NULL);
assert_env_value(env, "VAR", "there");
assert(1 == env_get_size(env));
env_destroy(env);
}
static void test_env_set_entry_multiple(void)
{
t_env *env;
env = NULL;
assert(env_set_entry(&env, ft_strdup("VAR"), ft_strdup("hello")) != NULL);
assert(env_set_entry(&env, ft_strdup("SHELL"), ft_strdup("/bin/bash")) != NULL);
assert(env_set_entry(&env, ft_strdup("TERM"), ft_strdup("xterm-kitty")) != NULL);
assert_env_value(env, "VAR", "hello");
assert_env_value(env, "SHELL", "/bin/bash");
assert_env_value(env, "TERM", "xterm-kitty");
assert(3 == env_get_size(env));
env_destroy(env);
}
static void test_env_set_entry_nullargs(void)
{
t_env *env;
env = NULL;
assert(env_set_entry(&env, ft_strdup("VAR"), ft_strdup("hello")) != NULL);
ft_errno(FT_ESUCCESS);
assert(env_set_entry(&env, NULL, ft_strdup("hello")) == NULL);
assert(ft_errno_get() == FT_EINVAL);
assert(env_set_entry(&env, ft_strdup("VAR"), NULL) == NULL);
ft_errno(FT_ESUCCESS);
assert(env_set_entry(&env, ft_strdup(""), ft_strdup("value")) == NULL);
assert(ft_errno_get() == FT_EBADID);
assert_env_value(env, "VAR", "hello");
assert(1 == env_get_size(env));
env_destroy(env);
}
static void test_env_from_envp(void)
{
t_env *env;
char *envp[] = {"VAR=hello", "SHELL=/bin/bash", "TERM=xterm-kitty", NULL};
env = env_from_envp(envp);
assert(env != NULL);
assert_env_value(env, "VAR", "hello");
assert_env_value(env, "SHELL", "/bin/bash");
assert_env_value(env, "TERM", "xterm-kitty");
assert(3 == env_get_size(env));
env_destroy(env);
}
static void test_env_from_envp_invalid(void)
{
t_env *env;
char *envp[] = {"VAR=hello", "", "TERM=xterm-kitty", NULL};
ft_dprintf(STDERR_FILENO, "test_env_from_envp\n");
env = env_from_envp(envp);
assert(env == NULL);
}
static void test_envp_from_env(void)
{
t_env *env;
char *envp[] = {"VAR=hello", "SHELL=/bin/bash", "TERM=xterm-kitty", NULL};
env = env_from_envp(envp);
char **got_envp = envp_from_env(env);
t_env *got_env = env_from_envp(got_envp);
assert(got_env != NULL);
assert_env_value(got_env, "VAR", "hello");
assert_env_value(got_env, "SHELL", "/bin/bash");
assert_env_value(got_env, "TERM", "xterm-kitty");
assert(3 == env_get_size(got_env));
env_destroy(env);
env_destroy(got_env);
envp_destroy(got_envp);
}
int main(void) {
test_envp_parsing("SHELL=/bin/fish", "SHELL", "/bin/fish");
test_envp_parsing("=/bin/fish", "", "/bin/fish");
test_envp_parsing(NULL, NULL, NULL);
test_envp_parsing("", NULL, NULL);
test_envp_parsing("VARNAME", "VARNAME", "");
test_env_set_entry_empty_env();
test_env_set_entry_existing_value();
test_env_set_entry_multiple();
test_env_set_entry_nullargs();
test_env_from_envp();
test_env_from_envp_invalid();
test_envp_from_env();
return (0);
}

View file

@ -1,169 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* test_here_doc.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/03/07 11:43:32 by khais #+# #+# */
/* Updated: 2025/04/08 16:34:50 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include <assert.h>
#include "testutil.h"
#include "libft.h"
#include <unistd.h>
#include <fcntl.h>
#include "../src/executing/here_doc/here_doc.h"
#include "../src/env/env_manip.h"
#include "../src/ft_errno.h"
static void test_here_doc_filename_generation(void)
{
char *filename1;
char *filename2;
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
ft_errno(FT_ESUCCESS);
filename1 = here_doc_random_filename();
filename2 = here_doc_random_filename();
assert(filename1 != NULL);
assert(filename2 != NULL);
ft_dprintf(STDERR_FILENO, "Got filename: [%s]\n", filename1);
ft_dprintf(STDERR_FILENO, "Got filename: [%s]\n", filename2);
assert(ft_strcmp(filename1, filename2) != 0);
free(filename1);
free(filename2);
do_leak_check();
}
static void test_here_doc_invalid_args(void)
{
t_worddesc *marker;
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
ft_errno(FT_ESUCCESS);
assert(-1 == here_doc(NULL, 0, NULL));
marker = worddesc_create(NULL, 0, NULL, WORD_TOKEN);
assert(-1 == here_doc(marker, 0, NULL));
worddesc_destroy(marker);
marker = worddesc_create(ft_strdup("EOF"), 0, NULL, WORD_TOKEN);
assert(-1 == here_doc(marker, -1, NULL));
worddesc_destroy(marker);
do_leak_check();
}
static void test_here_doc_only_end_marker(void)
{
t_worddesc *marker;
int infile;
int result;
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
ft_errno(FT_ESUCCESS);
marker = worddesc_create(ft_strdup("EOF"), 0, NULL, WORD_TOKEN);
infile = open("./here_doc_only_eof.input", O_RDONLY);
result = here_doc(marker, infile, NULL);
close(infile);
assert(result != -1);
assert(NULL == get_next_line(result));
close(result);
worddesc_destroy(marker);
do_leak_check();
}
static void test_here_doc_input_plus_end_marker(void)
{
t_worddesc *marker;
int infile;
int result;
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
ft_errno(FT_ESUCCESS);
marker = worddesc_create(ft_strdup("EOF"), 0, NULL, WORD_TOKEN);
infile = open("./here_doc_input_plus_eof.input", O_RDONLY);
result = here_doc(marker, infile, NULL);
close(infile);
worddesc_destroy(marker);
assert(result != -1);
assert_strequal("input\n", get_next_line(result));
close(result);
do_leak_check();
}
static void test_here_doc_input_no_end_marker(void)
{
t_worddesc *marker;
int infile;
int result;
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
marker = worddesc_create(ft_strdup("EOF"), 0, NULL, WORD_TOKEN);
infile = open("./here_doc_input_no_eof.input", O_RDONLY);
result = here_doc(marker, infile, NULL);
close(infile);
worddesc_destroy(marker);
assert(result != -1);
assert_strequal("input\n", get_next_line(result));
close(result);
do_leak_check();
}
static void test_here_doc_with_expansion(void)
{
t_worddesc *marker;
int infile;
int result;
t_minishell app;
ft_bzero(&app, sizeof(t_minishell));
app.env = env_set_entry(&app.env, ft_strdup("USER"), ft_strdup("kcolin"));
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
ft_errno(FT_ESUCCESS);
marker = worddesc_create(ft_strdup("EOF"), 0, NULL, WORD_TOKEN);
infile = open("./here_doc_with_expansion.input", O_RDONLY);
result = here_doc(marker, infile, &app);
close(infile);
worddesc_destroy(marker);
assert(result != -1);
assert_strequal("hello\n", get_next_line(result));
assert_strequal("kcolin\n", get_next_line(result));
assert_strequal("end\n", get_next_line(result));
close(result);
do_leak_check();
}
static void test_here_doc_with_no_expansion(void)
{
t_worddesc *marker;
int infile;
int result;
t_minishell app;
ft_bzero(&app, sizeof(t_minishell));
app.env = env_set_entry(&app.env, ft_strdup("USER"), ft_strdup("kcolin"));
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
marker = worddesc_create(ft_strdup("EOF"), W_QUOTED, NULL, WORD_TOKEN);
infile = open("./here_doc_with_expansion.input", O_RDONLY);
result = here_doc(marker, infile, &app);
close(infile);
worddesc_destroy(marker);
assert(result != -1);
assert_strequal("hello\n", get_next_line(result));
assert_strequal("$USER\n", get_next_line(result));
assert_strequal("end\n", get_next_line(result));
close(result);
do_leak_check();
}
int main(void) {
test_here_doc_filename_generation();
test_here_doc_invalid_args();
test_here_doc_only_end_marker();
test_here_doc_input_plus_end_marker();
test_here_doc_input_no_end_marker();
test_here_doc_with_expansion();
test_here_doc_with_no_expansion();
return (0);
}

View file

@ -1,67 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* metacharacters.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kcolin <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/06 15:21:00 by kcolin #+# #+# */
/* Updated: 2025/02/11 18:32:01 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "../src/parser/matchers/metacharacter.h"
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
void test_metacharacters(void)
{
dup2(STDERR_FILENO, STDIN_FILENO);
char c = 'a';
printf("not metachar:");
while (c != 'z')
{
printf("%c", c);
assert(!is_metacharacter(c));
c++;
}
c = 'A';
while (c != 'Z')
{
printf("%c", c);
assert(!is_metacharacter(c));
c++;
}
c = '0';
while (c != '9')
{
printf("%c", c);
assert(!is_metacharacter(c));
c++;
}
char *not_metachars = ";[]{}*+=_-";
int i = 0;
while (not_metachars[i] != '\0')
{
dprintf(STDERR_FILENO, "%c", not_metachars[i]);
assert(!is_metacharacter(not_metachars[i]));
i++;
}
char *metachars = " \t\n|&()<>";
i = 0;
printf("\nmetachar:");
while (metachars[i] != '\0')
{
dprintf(STDERR_FILENO, "%c (%d) ", metachars[i], metachars[i]);
assert(is_metacharacter(metachars[i]));
i++;
}
printf("\n");
}
int main(void)
{
test_metacharacters();
return (0);
}

View file

@ -1,149 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* test_parse_cmdlists.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/24 17:40:48 by khais #+# #+# */
/* Updated: 2025/03/20 11:57:26 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "ft_printf.h"
#include "../src/parser/cmdlist/cmdlist.h"
#include "testutil.h"
#include "unistd.h"
#include <assert.h>
#include "parse_cmdlist.h"
static void test_parse_cmdlist_empty(void)
{
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
t_cmdlist *cmd = parse_cmdlist("");
assert(cmd == NULL);
cmdlist_destroy(cmd);
}
static void test_parse_cmdlist_single_pipeline(void)
{
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
t_cmdlist *cmd = parse_cmdlist("echo this | cat -e");
assert(cmd != NULL);
assert_pipelineequal("echo this | cat -e", cmd, 0);
assert(cmd->operators[0] == OP_END);
assert(cmd->num_cmd == 1);
cmdlist_destroy(cmd);
}
static void test_parse_cmdlist_simple_and(void)
{
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
t_cmdlist *cmd = parse_cmdlist("echo this | cat -e && echo works | wc -c");
assert(cmd != NULL);
assert_pipelineequal("echo this | cat -e", cmd, 0);
assert(cmd->operators[0] == OP_AND);
assert_pipelineequal("echo works | wc -c", cmd, 1);
assert(cmd->num_cmd == 2);
cmdlist_destroy(cmd);
}
static void test_parse_cmdlist_simple_or(void)
{
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
t_cmdlist *cmd = parse_cmdlist("echo this | cat -e || echo works | wc -c");
assert(cmd != NULL);
assert_pipelineequal("echo this | cat -e", cmd, 0);
assert(cmd->operators[0] == OP_OR);
assert_pipelineequal("echo works | wc -c", cmd, 1);
assert(cmd->num_cmd == 2);
cmdlist_destroy(cmd);
}
static void test_parse_cmdlist_triple_or(void)
{
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
t_cmdlist *cmd = parse_cmdlist("echo this | cat -e || echo works | wc -c || echo as well | cut -d' ' -f1");
assert(cmd != NULL);
assert_pipelineequal("echo this | cat -e", cmd, 0);
assert(cmd->operators[0] == OP_OR);
assert_pipelineequal("echo works | wc -c", cmd, 1);
assert(cmd->operators[1] == OP_OR);
assert_pipelineequal("echo as well | cut -d' ' -f1", cmd, 2);
assert(cmd->num_cmd == 3);
cmdlist_destroy(cmd);
}
static void test_parse_cmdlist_triple_both_operators(void)
{
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
t_cmdlist *cmd = parse_cmdlist("echo this | cat -e || echo works | wc -c && echo as well | cut -d' ' -f1");
assert(cmd != NULL);
assert_pipelineequal("echo this | cat -e", cmd, 0);
assert(cmd->operators[0] == OP_OR);
assert_pipelineequal("echo works | wc -c", cmd, 1);
assert(cmd->operators[1] == OP_AND);
assert_pipelineequal("echo as well | cut -d' ' -f1", cmd, 2);
assert(cmd->operators[2] == OP_END);
assert(cmd->num_cmd == 3);
cmdlist_destroy(cmd);
}
static void test_parse_cmdlist_quad_both_operators(void)
{
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
t_cmdlist *cmd = parse_cmdlist("echo this | cat -e || echo works | wc -c && echo as well | cut -d' ' -f1 || echo final");
assert(cmd != NULL);
assert_pipelineequal("echo this | cat -e", cmd, 0);
assert(cmd->operators[0] == OP_OR);
assert_pipelineequal("echo works | wc -c", cmd, 1);
assert(cmd->operators[1] == OP_AND);
assert_pipelineequal("echo as well | cut -d' ' -f1", cmd, 2);
assert(cmd->operators[2] == OP_OR);
assert_pipelineequal("echo final", cmd, 3);
assert(cmd->operators[3] == OP_END);
cmdlist_destroy(cmd);
}
static void test_parse_cmdlist_invalid_pipeline(void)
{
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
t_cmdlist *cmd = parse_cmdlist("echo this | | cat -e || echo does not work");
assert(cmd == NULL);
cmdlist_destroy(cmd);
}
static void test_parse_cmdlist_simple_command(void)
{
ft_dprintf(STDERR_FILENO, "==> %s <==\n", __FUNCTION__);
t_cmdlist *cmd = parse_cmdlist("echo this");
assert(cmd != NULL);
assert_pipelineequal("echo this", cmd, 0);
assert(cmd->operators[0] == OP_END);
assert(cmd->num_cmd == 1);
cmdlist_destroy(cmd);
}
int main(void)
{
test_parse_cmdlist_empty();
do_leak_check();
test_parse_cmdlist_single_pipeline();
do_leak_check();
test_parse_cmdlist_simple_and();
do_leak_check();
test_parse_cmdlist_simple_or();
do_leak_check();
test_parse_cmdlist_triple_or();
do_leak_check();
test_parse_cmdlist_triple_both_operators();
do_leak_check();
test_parse_cmdlist_quad_both_operators();
do_leak_check();
test_parse_cmdlist_invalid_pipeline();
do_leak_check();
test_parse_cmdlist_simple_command();
do_leak_check();
return (0);
}

View file

@ -1,117 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* test_parse_pipelines.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/21 13:13:58 by khais #+# #+# */
/* Updated: 2025/03/11 16:33:11 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "../src/parser/pipeline/pipeline.h"
#include "../src/ft_errno.h"
#include <assert.h>
#include "testutil.h"
#include <limits.h>
#include "parse_pipeline.h"
#include <stdlib.h>
static void test_parse_empty_pipeline(void)
{
t_pipeline *pipeline = parse_pipeline("");
assert(pipeline == NULL);
pipeline_destroy(pipeline);
}
static void test_parse_pipeline_single_cmd(void)
{
t_pipeline *pipeline = parse_pipeline("echo hello world");
assert(pipeline->num_cmd == 1);
assert_pipeline_cmd_word(pipeline, "echo", 0, 0);
assert_pipeline_cmd_word(pipeline, "hello", 0, 1);
assert_pipeline_cmd_word(pipeline, "world", 0, 2);
pipeline_destroy(pipeline);
}
static void test_parse_pipeline_two_cmd(void)
{
t_pipeline *pipeline = parse_pipeline("echo hello world | tee output.txt");
assert_pipeline_cmd_word(pipeline, "echo", 0, 0);
assert_pipeline_cmd_word(pipeline, "hello", 0, 1);
assert_pipeline_cmd_word(pipeline, "world", 0, 2);
assert_pipeline_cmd_word(pipeline, "tee", 1, 0);
assert_pipeline_cmd_word(pipeline, "output.txt", 1, 1);
pipeline_destroy(pipeline);
}
static void test_parse_pipeline_three_cmd(void)
{
t_pipeline *pipeline = parse_pipeline("echo hello world | tee output.txt | cat -e");
assert_pipeline_cmd_word(pipeline, "echo", 0, 0);
assert_pipeline_cmd_word(pipeline, "hello", 0, 1);
assert_pipeline_cmd_word(pipeline, "world", 0, 2);
assert_pipeline_cmd_word(pipeline, "tee", 1, 0);
assert_pipeline_cmd_word(pipeline, "output.txt", 1, 1);
assert_pipeline_cmd_word(pipeline, "cat", 2, 0);
assert_pipeline_cmd_word(pipeline, "-e", 2, 1);
pipeline_destroy(pipeline);
}
static void test_parse_pipeline_four_cmd(void)
{
t_pipeline *pipeline = parse_pipeline("echo hello world | tee output.txt | cat -e | hexdump -C");
assert_pipeline_cmd_word(pipeline, "echo", 0, 0);
assert_pipeline_cmd_word(pipeline, "hello", 0, 1);
assert_pipeline_cmd_word(pipeline, "world", 0, 2);
assert_pipeline_cmd_word(pipeline, "tee", 1, 0);
assert_pipeline_cmd_word(pipeline, "output.txt", 1, 1);
assert_pipeline_cmd_word(pipeline, "cat", 2, 0);
assert_pipeline_cmd_word(pipeline, "-e", 2, 1);
assert_pipeline_cmd_word(pipeline, "hexdump", 3, 0);
assert_pipeline_cmd_word(pipeline, "-C", 3, 1);
pipeline_destroy(pipeline);
}
static void test_parse_pipeline_double_pipe_rejected(void)
{
ft_errno(FT_ESUCCESS);
assert(parse_pipeline("echo hello | | tee output.txt") == NULL);
assert(ft_errno_get() == FT_EUNEXPECTED_PIPE);
}
static void test_parse_pipeline_triple_pipe_rejected(void)
{
ft_errno(FT_ESUCCESS);
assert(parse_pipeline("echo hello | | | tee output.txt") == NULL);
assert(ft_errno_get() == FT_EUNEXPECTED_PIPE);
}
static void test_parse_pipeline_pipe_at_start_rejected(void)
{
ft_errno(FT_ESUCCESS);
assert(parse_pipeline("| echo hello | tee output.txt") == NULL);
assert(ft_errno_get() == FT_EUNEXPECTED_PIPE);
}
static void test_parse_pipeline_pipe_at_end_rejected(void)
{
ft_errno(FT_ESUCCESS);
assert(parse_pipeline("echo hello | tee output.txt |") == NULL);
assert(ft_errno_get() == FT_EUNEXPECTED_PIPE);
}
int main(void)
{
test_parse_empty_pipeline();
test_parse_pipeline_single_cmd();
test_parse_pipeline_two_cmd();
test_parse_pipeline_three_cmd();
test_parse_pipeline_four_cmd();
test_parse_pipeline_double_pipe_rejected();
test_parse_pipeline_triple_pipe_rejected();
test_parse_pipeline_pipe_at_start_rejected();
test_parse_pipeline_pipe_at_end_rejected();
return (0);
}

View file

@ -1,48 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* parse_simple_cmds.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/21 12:20:20 by khais #+# #+# */
/* Updated: 2025/03/07 12:32:04 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include <assert.h>
#include "../src/parser/simple_cmd/simple_cmd.h"
#include "../src/parser/wordsplit/wordsplit.h"
#include "../src/parser/wordlist/wordlist.h"
#include "testutil.h"
static t_simple_cmd *parse_simple_cmd(char *input)
{
t_wordlist *words = minishell_wordsplit(input);
t_simple_cmd *cmd = simple_cmd_from_wordlist(words);
return (cmd);
}
static void test_parse_empty_command(void)
{
t_simple_cmd *cmd = parse_simple_cmd("");
assert(cmd != NULL);
simple_cmd_destroy(cmd);
}
static void test_parse_nonempty_command(void)
{
t_simple_cmd *cmd = parse_simple_cmd("echo Hello World!");
assert(cmd != NULL);
assert_strequal("echo", wordlist_get(cmd->words, 0)->word);
assert_strequal("Hello", wordlist_get(cmd->words, 1)->word);
assert_strequal("World!", wordlist_get(cmd->words, 2)->word);
simple_cmd_destroy(cmd);
}
int main(void)
{
test_parse_empty_command();
test_parse_nonempty_command();
return (0);
}

View file

@ -1,106 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* test_quote_removal.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/28 13:46:56 by khais #+# #+# */
/* Updated: 2025/03/11 16:31:50 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include "../src/parser/remove_quotes/remove_quotes.h"
#include "testutil.h"
#include <assert.h>
#include <stdlib.h>
static void test_quote_removal_no_quotes_single_word(void)
{
t_worddesc *word = create_single_word("word");
t_worddesc *got_word = remove_quotes(word);
assert_strequal("word", got_word->word);
assert(got_word->marker == NULL);
worddesc_destroy(word);
worddesc_destroy(got_word);
}
static void test_quote_removal_null(void)
{
t_worddesc *word = NULL;
t_worddesc *got_word = remove_quotes(word);
assert(got_word == NULL);
}
static void test_quote_removal_single_quotes(void)
{
t_worddesc *word = create_single_word("'word'");
t_worddesc *got_word = remove_quotes(word);
assert_strequal("word", got_word->word);
assert(got_word->marker == NULL);
worddesc_destroy(word);
worddesc_destroy(got_word);
}
static void test_quote_removal_double_quotes(void)
{
t_worddesc *word = create_single_word("\"word\"");
t_worddesc *got_word = remove_quotes(word);
assert_strequal("word", got_word->word);
assert(got_word->marker == NULL);
worddesc_destroy(word);
worddesc_destroy(got_word);
}
static void test_quote_removal_mixed_single_in_double(void)
{
t_worddesc *word = create_single_word("\"'word'\"");
t_worddesc *got_word = remove_quotes(word);
assert_strequal("'word'", got_word->word);
assert(got_word->marker == NULL);
worddesc_destroy(word);
worddesc_destroy(got_word);
}
static void test_quote_removal_mixed_double_in_single(void)
{
t_worddesc *word = create_single_word("'\"word\"'");
t_worddesc *got_word = remove_quotes(word);
assert_strequal("\"word\"", got_word->word);
assert(got_word->marker == NULL);
worddesc_destroy(word);
worddesc_destroy(got_word);
}
static void test_quote_removal_middle_of_word(void)
{
t_worddesc *word = create_single_word("var='VALUE'here");
t_worddesc *got_word = remove_quotes(word);
assert_strequal("var=VALUEhere", got_word->word);
assert(got_word->marker == NULL);
worddesc_destroy(word);
worddesc_destroy(got_word);
}
static void test_quote_removal_nested_middle_of_word(void)
{
t_worddesc *word = create_single_word("var=\"'VALUE'here\"");
t_worddesc *got_word = remove_quotes(word);
assert_strequal("var='VALUE'here", got_word->word);
assert(got_word->marker == NULL);
worddesc_destroy(word);
worddesc_destroy(got_word);
}
int main(void) {
test_quote_removal_no_quotes_single_word();
test_quote_removal_null();
test_quote_removal_single_quotes();
test_quote_removal_double_quotes();
test_quote_removal_mixed_single_in_double();
test_quote_removal_mixed_double_in_single();
test_quote_removal_middle_of_word();
test_quote_removal_nested_middle_of_word();
return (0);
}

View file

@ -1,338 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* word_splitting.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/13 15:17:56 by khais #+# #+# */
/* Updated: 2025/03/06 16:50:56 by khais ### ########.fr */
/* */
/* ************************************************************************** */
#include <assert.h>
#include "testutil.h"
#include "../src/parser/wordsplit/wordsplit.h"
#include <unistd.h>
#include "libft.h"
#include <stdlib.h>
/*
** https://bash-hackers.gabe565.com/syntax/words/
*/
static void test_wordsplit_singleword(void)
{
t_wordlist *words;
words = minishell_wordsplit("echo");
assert_strequal("echo", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert(NULL == wordlist_get(words, 1));
wordlist_destroy(words);
}
static void test_wordsplit_singleword_with_blanks(void)
{
t_wordlist *words;
words = minishell_wordsplit("\t \t echo \t\t ");
assert_strequal("echo", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert(NULL == wordlist_get(words, 1));
wordlist_destroy(words);
}
static void test_wordsplit_multiword(void)
{
t_wordlist *words;
words = minishell_wordsplit("\t echo\tThe file is named $MYFILE \t");
assert_strequal("echo", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert_strequal("The", wordlist_get(words, 1)->word);
assert_strequal(" ", wordlist_get(words, 1)->marker);
assert_strequal("file", wordlist_get(words, 2)->word);
assert_strequal(" ", wordlist_get(words, 2)->marker);
assert_strequal("is", wordlist_get(words, 3)->word);
assert_strequal(" ", wordlist_get(words, 3)->marker);
assert_strequal("named", wordlist_get(words, 4)->word);
assert_strequal(" ", wordlist_get(words, 4)->marker);
assert_strequal("$MYFILE", wordlist_get(words, 5)->word);
assert_strequal(" ", wordlist_get(words, 5)->marker);
assert(NULL == wordlist_get(words, 6));
wordlist_destroy(words);
}
static void test_wordsplit_multiword_with_single_quotes(void)
{
t_wordlist *words;
words = minishell_wordsplit("\t echo\t' \t The file is named $MYFILE ' \t");
assert_strequal("echo", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert_strequal("' \t The file is named $MYFILE '", wordlist_get(words, 1)->word);
// This marker is one char shorter because the tab character above is
// represented here with two chars
ft_dprintf(STDERR_FILENO, "the marker below is shorter than the string above because the string above contains a tab, it is normal\n");
assert_strequal(" '''''''''''''''''''''''''''''''''' ", wordlist_get(words, 1)->marker);
assert(NULL == wordlist_get(words, 2));
wordlist_destroy(words);
}
static void test_wordsplit_multiword_with_double_quotes(void)
{
t_wordlist *words;
words = minishell_wordsplit("\t echo\t\" \t The file is named $MYFILE \" \t");
assert_strequal("echo", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert_strequal("\" \t The file is named $MYFILE \"", wordlist_get(words, 1)->word);
// This marker is one char shorter because the tab character above is
// represented here with two chars
ft_dprintf(STDERR_FILENO, "the marker below is shorter than the string above because the string above contains a tab, it is normal\n");
assert_strequal(" \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" ", wordlist_get(words, 1)->marker);
assert(NULL == wordlist_get(words, 2));
wordlist_destroy(words);
}
static void test_wordsplit_mixed_single_in_double(void)
{
t_wordlist *words;
words = minishell_wordsplit("hello \"mixed ' \tquotes \t'\" there");
assert_strequal("hello", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert_strequal("\"mixed ' \tquotes \t'\"", wordlist_get(words, 1)->word);
assert_strequal(" \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" ", wordlist_get(words, 1)->marker);
assert_strequal("there", wordlist_get(words, 2)->word);
assert_strequal(" ", wordlist_get(words, 2)->marker);
assert(NULL == wordlist_get(words, 3));
wordlist_destroy(words);
}
static void test_wordsplit_mixed_double_in_single(void)
{
t_wordlist *words;
words = minishell_wordsplit("hello 'mixed \" quotes \"' there");
assert_strequal("hello", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert_strequal("'mixed \" quotes \"'", wordlist_get(words, 1)->word);
assert_strequal(" '''''''''''''''' ", wordlist_get(words, 1)->marker);
assert_strequal("there", wordlist_get(words, 2)->word);
assert_strequal(" ", wordlist_get(words, 2)->marker);
assert(NULL == wordlist_get(words, 3));
wordlist_destroy(words);
}
static void test_wordsplit_mixed_broken(void)
{
t_wordlist *words;
words = minishell_wordsplit("hello '\"mixed 'quotes'\"' there");
assert_strequal("hello", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert_strequal("'\"mixed 'quotes'\"'", wordlist_get(words, 1)->word);
assert_strequal(" ''''''' ' ", wordlist_get(words, 1)->marker);
assert_strequal("there", wordlist_get(words, 2)->word);
assert_strequal(" ", wordlist_get(words, 2)->marker);
assert(NULL == wordlist_get(words, 3));
wordlist_destroy(words);
}
static void test_wordsplit_unclosed_single(void)
{
t_wordlist *words;
words = minishell_wordsplit("'hello");
assert(words == NULL);
}
static void test_wordsplit_unclosed_double(void)
{
t_wordlist *words;
words = minishell_wordsplit("\"hello");
assert(words == NULL);
}
static void test_wordsplit_operator_word(void)
{
t_wordlist *words;
words = minishell_wordsplit(">test");
assert_strequal(">", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert_strequal("test", wordlist_get(words, 1)->word);
assert_strequal(" ", wordlist_get(words, 1)->marker);
assert(NULL == wordlist_get(words, 2));
wordlist_destroy(words);
}
static void test_wordsplit_all_operators(void)
{
t_wordlist *words;
words = minishell_wordsplit("|&&||()<>><<>");
assert_strequal("|", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert_strequal("&&", wordlist_get(words, 1)->word);
assert_strequal(" ", wordlist_get(words, 1)->marker);
assert_strequal("||", wordlist_get(words, 2)->word);
assert_strequal(" ", wordlist_get(words, 2)->marker);
assert_strequal("(", wordlist_get(words, 3)->word);
assert_strequal(" ", wordlist_get(words, 3)->marker);
assert_strequal(")", wordlist_get(words, 4)->word);
assert_strequal(" ", wordlist_get(words, 4)->marker);
assert_strequal("<", wordlist_get(words, 5)->word);
assert_strequal(" ", wordlist_get(words, 5)->marker);
assert_strequal(">>", wordlist_get(words, 6)->word);
assert_strequal(" ", wordlist_get(words, 6)->marker);
assert_strequal("<<", wordlist_get(words, 7)->word);
assert_strequal(" ", wordlist_get(words, 7)->marker);
assert_strequal(">", wordlist_get(words, 8)->word);
assert_strequal(" ", wordlist_get(words, 8)->marker);
assert(NULL == wordlist_get(words, 9));
wordlist_destroy(words);
}
static void test_wordsplit_operator_combining(void)
{
t_wordlist *words;
words = minishell_wordsplit("|||>>><<<&&&");
assert_strequal("||", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert_strequal("|", wordlist_get(words, 1)->word);
assert_strequal(" ", wordlist_get(words, 1)->marker);
assert_strequal(">>", wordlist_get(words, 2)->word);
assert_strequal(" ", wordlist_get(words, 2)->marker);
assert_strequal(">", wordlist_get(words, 3)->word);
assert_strequal(" ", wordlist_get(words, 3)->marker);
assert_strequal("<<", wordlist_get(words, 4)->word);
assert_strequal(" ", wordlist_get(words, 4)->marker);
assert_strequal("<", wordlist_get(words, 5)->word);
assert_strequal(" ", wordlist_get(words, 5)->marker);
assert_strequal("&&", wordlist_get(words, 6)->word);
assert_strequal(" ", wordlist_get(words, 6)->marker);
assert_strequal("&", wordlist_get(words, 7)->word);
assert_strequal(" ", wordlist_get(words, 7)->marker);
assert(NULL == wordlist_get(words, 8));
wordlist_destroy(words);
}
static void test_wordsplit_var_substitution(void)
{
t_wordlist *words;
words = minishell_wordsplit("echo VAR=$VAR here");
assert_strequal("echo", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert(0 == wordlist_get(words, 0)->flags);
assert_strequal("VAR=$VAR", wordlist_get(words, 1)->word);
assert_strequal(" ", wordlist_get(words, 1)->marker);
assert(W_HASDOLLAR == wordlist_get(words, 1)->flags);
assert_strequal("here", wordlist_get(words, 2)->word);
assert_strequal(" ", wordlist_get(words, 2)->marker);
assert(0 == wordlist_get(words, 2)->flags);
wordlist_destroy(words);
}
static void test_wordsplit_var_substitution_quotes(void)
{
t_wordlist *words;
words = minishell_wordsplit("VAR=\"$VAR\"");
assert_strequal("VAR=\"$VAR\"", wordlist_get(words, 0)->word);
assert_strequal(" \"\"\"\" ", wordlist_get(words, 0)->marker);
assert(W_HASDOLLAR & wordlist_get(words, 0)->flags);
wordlist_destroy(words);
words = minishell_wordsplit("VAR='$VAR'");
assert_strequal("VAR='$VAR'", wordlist_get(words, 0)->word);
assert((W_HASDOLLAR & wordlist_get(words, 0)->flags) == 0);
assert_strequal(" '''' ", wordlist_get(words, 0)->marker);
wordlist_destroy(words);
}
static void test_wordsplit_quote_detection_nonnested(void)
{
t_wordlist *words;
words = minishell_wordsplit("echo 'single quotes' here \"double quotes\" here");
assert_strequal("echo", wordlist_get(words, 0)->word);
assert_strequal(" ", wordlist_get(words, 0)->marker);
assert(0 == wordlist_get(words, 0)->flags);
assert_strequal("'single quotes'", wordlist_get(words, 1)->word);
assert_strequal(" ''''''''''''' ", wordlist_get(words, 1)->marker);
assert(W_QUOTED == wordlist_get(words, 1)->flags);
assert_strequal("here", wordlist_get(words, 2)->word);
assert_strequal(" ", wordlist_get(words, 2)->marker);
assert(0 == wordlist_get(words, 2)->flags);
assert_strequal("\"double quotes\"", wordlist_get(words, 3)->word);
assert_strequal(" \"\"\"\"\"\"\"\"\"\"\"\"\" ", wordlist_get(words, 3)->marker);
assert((W_QUOTED | W_DQUOTE) == wordlist_get(words, 3)->flags);
assert_strequal("here", wordlist_get(words, 4)->word);
assert_strequal(" ", wordlist_get(words, 4)->marker);
assert(0 == wordlist_get(words, 4)->flags);
wordlist_destroy(words);
}
static void test_wordsplit_quote_detection_nested_double_in_simple(void)
{
t_wordlist *words;
char *str;
str = "'these are single quotes \"with double\" inside'";
words = minishell_wordsplit(str);
assert_strequal(str, wordlist_get(words, 0)->word);
assert_strequal(" '''''''''''''''''''''''''''''''''''''''''''' ", wordlist_get(words, 0)->marker);
assert(W_QUOTED == wordlist_get(words, 0)->flags);
wordlist_destroy(words);
str = "'\"these are single quotes with double inside\"'";
words = minishell_wordsplit(str);
assert_strequal(str, wordlist_get(words, 0)->word);
assert_strequal(" '''''''''''''''''''''''''''''''''''''''''''' ", wordlist_get(words, 0)->marker);
assert(W_QUOTED == wordlist_get(words, 0)->flags);
wordlist_destroy(words);
}
static void test_wordsplit_quote_detection_nested_single_in_double(void)
{
t_wordlist *words;
char *str;
str = "\"these are double quotes 'with single' inside\"";
words = minishell_wordsplit(str);
assert_strequal(str, wordlist_get(words, 0)->word);
assert((W_QUOTED | W_DQUOTE) == wordlist_get(words, 0)->flags);
assert_strequal(" \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" ", wordlist_get(words, 0)->marker);
wordlist_destroy(words);
str = "\"'these are double quotes with single inside'\"";
words = minishell_wordsplit(str);
assert_strequal(str, wordlist_get(words, 0)->word);
assert_strequal(" \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" ", wordlist_get(words, 0)->marker);
assert((W_QUOTED | W_DQUOTE) == wordlist_get(words, 0)->flags);
wordlist_destroy(words);
}
int main(void) {
test_wordsplit_singleword();
test_wordsplit_singleword_with_blanks();
test_wordsplit_multiword();
test_wordsplit_multiword_with_single_quotes();
test_wordsplit_multiword_with_double_quotes();
test_wordsplit_mixed_single_in_double();
test_wordsplit_mixed_double_in_single();
test_wordsplit_mixed_broken();
test_wordsplit_unclosed_single();
test_wordsplit_unclosed_double();
test_wordsplit_operator_word();
test_wordsplit_all_operators();
test_wordsplit_operator_combining();
test_wordsplit_var_substitution();
test_wordsplit_var_substitution_quotes();
test_wordsplit_quote_detection_nonnested();
test_wordsplit_quote_detection_nested_double_in_simple();
test_wordsplit_quote_detection_nested_single_in_double();
return (0);
}

Some files were not shown because too many files have changed in this diff Show more