- 1. Get Started
- 2. Understanding the project structure
- 3. Parser
- 4. Tokenizer
- 5. Env variable structure
- 6. Execution
sudo pacman -Syu readline
- add flags in Makefile
-lreadline
- install brew
curl -fsSL https://rawgit.com/kube/42homebrew/master/install.sh | zsh
- install readline
brew install readline
- change flags in makefile
"add when Create .o file"
FLAGS += -I$(HOME)/.brew/opt/readline/include
"add in compilation"
READLINE += -L$(HOME)/.brew/opt/readline/lib
void prompt(t_env *env, char *line)
{
parsing(env, line);
check_error_parsing(env);
if (doesnt_have_error_parsing)
{
processing_cmd(env);
processing_redirection(env);
if (doesnt_have_error_processing)
execution(env);
}
remove_all_token(env);
reset_counter_error(env);
free(line);
}
echo "salut$HOME"
if (is_word(env, line, start))
{
end = word_extraction(env, line, start);
content = malloc_substrcpy(line, start, end);
}
echo "salut$HOME"
^
|
index = 0
is_word()
definie quel attributs comporte un mot :- ne commence pas par une quote
' "
- ne commence pas par un characteres vide
space \n \t
- ne commence pas par une paranthese
(
- ne commence pas par un separateur
> < >> | && ||
- ne commence pas par une Variables d'environnement
- ne commence pas par un
$
- ne commence pas par une quote
- La fonction nous retourne 1 on peut alors recuperer le contenue du mot :
int is_word(t_env *env, char *line, int i)
{
if (!(is_quote(line[i])) && !(is_blank(line[i])) &&
!(is_paranthesis(line, i)) && !(is_separator(line, i)) &&
!(is_variable(env, line, i)) && line[i] != '$')
return (1);
return (0);
}
- On peut alors definir plein de fonction de detection de type pour
- faciliter sont parsing
- separer chaque concept en evitant d'influencer les autres :
int is_NULL(char *line);
int is_input_chevrons(char *line, int i);
int is_output_chevrons(char *line, int i);
int is_pipe(char *line, int index);
int is_append_chevrons(char *line, int i);
int is_heredoc(char *line, int i);
int is_file_redirection(char *line, int i);
int is_redirection(char *line, int i);
int is_boolean_operator(char *line, int i);
echo "salut$HOME"
^
|
start = 0
- Pour savoir quand s'arreter on doit definir ==un type de caracteres de separation==
- dans ce cas on utilise la fonction
is_delimiter()
int is_delimiter(t_env *env, char *line, int index)
{
if (is_blank(line[index]) || is_paranthesis(line, index) ||
is_separator(line, index) || is_variable(env, line, index))
return (1);
return (0);
}
- cette fonction retournera 1 lorsque le caracteres actuel sera un delimiteur de mot
- recuperer le dernier
index - 1
pour ne pas recuperer le caracters de separation - extraire le contenue avec l'index de debut et l'index de fin du mot
malloc_substrcpy(line, start, end)
echo "salut$HOME"
^ ^
| |
s e
int word_extraction(t_env *env, char *line, int index)
{
int start;
start = index;
while (line[index])
{
if (is_delimiter(env, line, index))
{
return (index - 1);
}
index++;
}
return (index);
}
Une fois Le Parser Extraction on doit classifier le contenue
content = echo
- Comme pour le Parser Type Detection on doit definir une fonction de detection de type
- Dans ce cas on doit classifier le mot :
- Il sera definit comme une command ou comme un simple mot
if (is_cmd(env, content))
token = command_tokenization(env, line, content, ++new_index);
else
token = tokenizer_word(content, TOKEN_WORD);
add_token_list(env, token);
is_cmd()
permet de Tester si le mot est un binaire ou un built_in :
int is_cmd(t_env *env, char *word)
{
if (is_bin(env, word) || is_built_in(word))
return (1);
return (0);
}
"echo is cmd"
is_bin()
test si le path absolue ou simple est valableis_built_in()
test tout simplement si lecontent
est le meme qu'un built_in
int is_bin(t_env *env, char *word)
{
char **bins;
bins = get_env_bins(env);
if (test_absolute_bin_access(word))
return (1);
else if (test_bin_access(bins, word))
return (1);
return (0);
}
int is_built_in(char *content)
{
if (same_str(content, "echo", ft_strlen(content)))
return (1);
if (same_str(content, "cd", ft_strlen(content)))
return (1);
if (same_str(content, "pwd", ft_strlen(content)))
return (1);
if (same_str(content, "env", ft_strlen(content)))
return (1);
if (same_str(content, "export", ft_strlen(content)))
return (1);
if (same_str(content, "unset", ft_strlen(content)))
return (1);
if (same_str(content, "exit", ft_strlen(content)))
return (1);
return (0);
}
-
-
- avec l'Attribut
class
de la structt_token
on peut stocker une autre struct avec differentes information : - On initialise une struct dans le
void *
- avec l'Attribut
-
token->class = init_cmd();
t_cmd *init_cmd(char *content, int id)
-
pour stocker une struct dans le
Void *
on retourne une struct malloc avec le stockage de toutes ses valeur :
t_token *create_token(char *content, int id)
{
t_token *token;
token = init_token();
token->id = id;
token->class = choose_class(content, id);
return (token);
}
void *choose_class(char *content, int id)
{
if (id == TOKEN_BLANK)
return (init_blank(content, id));
else if (id == TOKEN_WORD)
return (init_word(content, id));
return (NULL);
}
-
- Pour utiliser des Condition de teste de type et eviter les erreur
- on assigne un
id
qui defini le type de struct stocker :
# define TOKEN_NULL 0
# define TOKEN_BLANK 1
# define TOKEN_WORD 2
# define TOKEN_SINGLE_QUOTE 3
# define TOKEN_DOUBLE_QUOTE 4
# define TOKEN_PARANTHESIS 5
# define TOKEN_COMMAND 6
- Tester Avant de recuperer les information :
t_cmd *cmd;
if (token->id == TOKEN_CMD)
print_token_cmd(token)
-
- Pour recuperer les valeurs dans la struct qui est stocker dans un
Void \*
il faut la [[Cast]] avec le nom de la struct qui devrait lui etre assigner . - Pour savoir quel type est le void * on peut utiliser un
id
qui definit quel struct utiliser : - On cast directement le
void \*
avec le type de la struct((t_cmd *)token->class)->content
- Pour recuperer les valeurs dans la struct qui est stocker dans un
t_cmd *cmd;
if (token->id == TOKEN_CMD)
cmd = get_class(token);
void *get_class(t_token *token)
{
if (is_token_cmd(token))
{
return ((t_cmd *)token->class);
}
else if (is_token_redirection(token))
{
return ((t_redir *)token->class);
}
else if (is_token_file(token))
{
return ((t_file *)token->class);
}
else if (is_token_word(token))
{
return ((t_word *)token->class);
}
return (NULL);
}
-
- Grace a Token class qui peut stocker une structure variable d'information , on peut definir des tokens different
- Dans l'exemple :
echo salut tonton | wc -l
- On definie 3 class :
- 2 token de command
- 1 token de redirection
- Chaque Token class different on des attributs tres variable ce qui permet d'etre beaucoup plus organiser sur l'execution !
-
- Apres avoir Parser Classification on doit tokenizer le
content
avec sa class : - On creer un token avec sa struct assigner grace a la classification :
- Assigner l'ID du token
token->id
par apport auxcontent
- stocker la struct dans
class
- Assigner l'ID du token
t_token *token = create_token_command(content, TOKEN_CMD);
t_token *create_token_command(char *content, int id)
{
t_token *token;
token = init_token();
token->id = id;
token->class = init_cmd(content, id);
return (token);
}
t_cmd *init_cmd(char *content, int id)
{
t_cmd *cmd;
cmd = (t_cmd *)malloc(sizeof(t_cmd));
if (!cmd)
return (NULL);
cmd->id = id;
cmd->content = content;
cmd->flags = NULL;
cmd->bin = NULL;
cmd->arg = NULL;
cmd->args = NULL;
cmd->fd_in = STDIN_FILENO;
cmd->fd_out = STDOUT_FILENO;
cmd->first_arg = NULL;
cmd->pid = 0;
return (cmd);
}
-
- chaques token possede un pointeur sur le token precedent et sur le suivant
- Ce qui permet d'iterer dans les 2 sens
typedef struct s_token
{
void *class;
int id;
struct s_token *next;
struct s_token *prev;
} t_token;
- Recuperer le Premier node dans l'env
- Si il existe pas -> le creer
- Sinon iterer dans les token jusqu'aux dernier et le connecter avec le precedent
void add_token_list(t_env *env, t_token *token)
{
t_token *iter;
if (!(env->first_token))
env->first_token = token;
else
{
iter = env->first_token;
while (iter->next)
iter = iter->next;
connect_token(iter, token);
}
}
- Connecter dans les deux sens le
curr_token
void connect_token(t_token *curr_token, t_token *next_token)
{
curr_token->next = next_token;
next_token->prev = curr_token;
}
VARIABLE_NAME=VALUE
name = get_variable_name(env_variable[index]);
value = get_env_variable_value(env_variable[index]);
var = init_env_variable(name, value, VALUE);
"var numero 48" :
---[SHELL]---
| type : [1]
| index : [48]
| value : [/bin/bash]
----------------
- Extraire le nom a partir de l'egal
- definir la fin du nom
- a l'index de l'egal - 1
- ou a la fin de la string
VARIABLE_NAME=VALUE
^ ^
| |
"start" "end"
char *get_variable_name(char *variable)
{
int index;
int start;
int end;
char *name;
index = 0;
start = 0;
if (!variable)
return (NULL);
while (variable[index])
{
if (variable[index] == '=')
{
end = index - 1;
name = malloc_substrcpy(variable, start, end);
return (name);
}
index++;
}
end = index;
name = malloc_substrcpy(variable, start, end);
return (name);
}
- Extraire la valeur a partir de l'egal
- definir le debut de la valeur
- a l'index de l'egal + 1
- definir la fin de la valeur a la fin de la string
VARIABLE_NAME=VALUE
^ ^
| |
"start" "end"
char *get_env_variable_value(char *variable)
{
int i;
int start;
int end;
char *value;
i = 0;
if (!variable)
return (NULL);
while (variable[i])
{
if (variable[i] == '=' && !(is_blank(variable[i + 1])))
{
start = i + 1;
end = ft_strlen(variable);
value = malloc_substrcpy(variable, start, end);
if (is_value_null(value))
{
free(value);
return (NULL);
}
return (value);
}
i++;
}
return (NULL);
}
- apres avoir :
- Extraire le nom d une variable
- Extraire la valeur d une variable
- Definir un ID qui defini si il y a une valeur ou non
t_var *init_env_variable(char *name, char *value, int id)
{
t_var *var;
var = (t_var *)malloc(sizeof(t_var));
if (!var)
return (NULL);
var->index = -1;
var->id = id;
var->name = name;
var->value = value;
var->next = NULL;
var->prev = NULL;
return (var);
}
void add_variables_list(t_env *env, t_var *var)
{
t_var *iter;
int i;
i = 0;
if (!(env->first_var))
{
env->first_var = var;
var->index = i;
}
else
{
iter = env->first_var;
i++;
while (iter->next)
{
i++;
iter = iter->next;
}
var->index = i;
connect_var(iter, var);
}
}
- Iterez dans chacune des variable d'envrionnement
void create_chained_var(t_env *env, char **env_variable)
{
int index;
char *name;
char *value;
t_var *var;
index = 0;
while (env_variable[index])
{
name = get_variable_name(env_variable[index]);
value = get_env_variable_value(env_variable[index]);
var = init_env_variable(name, value, VALUE);
add_variables_list(env, var);
index++;
}
}
- Pour toute Theorie sur l'execution hesitez pas a checker ce repos Pipex
$> ls -la | wc
stock pid [ls]:[57253]
stock pid [wc]:[57254]
close [history.log]:[3]
close [ls]:[5]
close [wc]:[4]
------- Execution [ls process] --------
Wait [ls]:[57253]
DUP [ls]: fd_out [5], STDOUT_FILENO [1]
close [history.log]:[3]
close [ls]:[5]
close [wc]:[4]
------- Execution [wc process] --------
Wait [wc]:[57254]
DUP [wc] : fd_in [4], STDIN_FILENO [0]
close [history.log]:[3]
close [ls]:[5]
close [wc]:[4]
27 238 1586
cmd = get_first_cmd(env);
while (cmd)
{
bin_execution(env, cmd);
cmd = get_next_cmd(cmd);
}
close_all_fd(env);
wait_all_pid(env);
void bin_execution(t_env *env, t_cmd *cmd)
{
int pid;
pid = fork();
if (pid == 0)
{
redirect_cmd(cmd);
close_all_fd(env);
execve(path, args, variables);
}
else
{
stock_pid(cmd, id);
return ;
}
}
void wait_all_pid(t_env *env)
{
t_token *token;
t_cmd *cmd;
int status;
token = get_first_token_cmd(env);
while (token)
{
cmd = get_class(token);
waitpid(cmd->pid, &status, 0);
token = get_next_token_cmd(token);
}
}