sommaire :
1. todo list :
- global features :
promptshow a prompthistory? the command history ?binariesfetch and launch the right executable'";\? don't interpret special characters and unclosed quotes ?
- pipes : video vulgarisation
|pipes
- expensions :
$variable expension$?exit return of last executed process ressource stackoverflow
- quotes : ressource
'(ignore any special characters)"(ignore any special characters except '$')
- redirections & heredocs : ressource / heredocs for readability
<redirect input>redirect output<<read input until empty line -- ? doesn't update history ?>>redirect and append
- signals :
^Cclose process^Dexit minishell^\do nothing
- builtins (can't be executed in child process) :
cd <relative path>cd <absolute path>pwdexportunsetexit
- builtins (don't need to be) : source
envechoecho -n
- autres :
termcapmanreadlineman / second source
2. lexer (lexique analyser :
2.1 methode arbre binaire :
transformer arbre normal en arbre binaire
arbre lexical :
all
. pipes
. . redirections (program function or file)
. . . arguments (nom arg arg arg ...)
. . . .
EXEMPLE : . .
[< file ./prgrm 'arg1 '$VARIABLE" arg3" | grep "word.$EXTENSION" | wc -l > file]
. [< file ./prgrm 'arg1 'arg2" arg3"]
. . [file]
. . [./prgrm 'arg1 'arg2" arg3"]
. . . [./prgrm]
. . . ['arg1 'arg2" arg3"]
. [grep "word.md"]
. . . [grep]
. . . ["word.md"]
. [wc -l > file]
. . [file]
. . [wc -l]
. . . [wc]
. . . [-l]
export TEST="" ./test_argv $TEST foo argv[0] : [./test_argv] argv[1] : [foo]
export TEST=" bar " ./test_argv foo"$TEST"foo argv[0] : [./test_argv] argv[1] : [foo bar foo]
export TEST="bar" ./test_argv "foo "$TEST" foo" argv[0] : [./test_argv] argv[1] : [foo bar foo]
3. gerer les quotes et la separation des arguments :
3.1 tentative methode 1 :
3.1.1 pseudo code :
q = 0 // first quote
c = 0 // count
i = 0
while (str[i])
{
if (str[i] == ')
{
if (q == ')
{
c--;
if (c == 0)
{
q = 0;
create_new_substr;
}
}
else
{
c++;
if (q == 0)
q = ';
}
}
if (str[i] == ")
{
if (q == ")
{
c--;
if (c == 0)
{
q = 0;
create_new_substr;
}
}
else
{
c++;
if (q == 0)
q = ";
}
}
i++;
}
3.1.2 application :
['][a][r][g][1][ ]['][a][r][g][2]["][ ][a][r][g][3]["]
c = 0 . . . . . . . . . . . . . . . . . .
c = 1 . . . . . . . . . . . . . . . . . str[i] == ' ; q == 0 c == 0 -> c++; q = '
c = . 1 . . . . . . . . . . . . . . . . str[i] != ' | " ; ->
c = . . 1 . . . . . . . . . . . . . . . str[i] != ' | " ; ->
c = . . . 1 . . . . . . . . . . . . . . str[i] != ' | " ; ->
c = . . . . 1 . . . . . . . . . . . . . str[i] != ' | " ; ->
c = . . . . . 1 . . . . . . . . . . . . str[i] != ' | " ; ->
c = . . . . . . 0 . . . . . . . . . . . str[i] == ' ; q == ' c == 1 -> c--; q = 0;
arg1 = [a][r][g][1][ ]
c = . . . . . . . 0 . . . . . . . . . . str[i] != ' | " ; ->
c = . . . . . . . . 0 . . . . . . . . . str[i] != ' | " ; ->
c = . . . . . . . . . 0 . . . . . . . . str[i] != ' | " ; ->
c = . . . . . . . . . . 0 . . . . . . . str[i] != ' | " ; ->
c = . . . . . . . . . . . 1 . . . . . . str[i] == " ; q != " c == 0 -> c++; q = ";
arg2 = [a][r][g][2]
c = . . . . . . . . . . . . 1 . . . . . str[i] != ' | " ; ->
c = . . . . . . . . . . . . . 1 . . . . str[i] != ' | " ; ->
c = . . . . . . . . . . . . . . 1 . . . str[i] != ' | " ; ->
c = . . . . . . . . . . . . . . . 1 . . str[i] != ' | " ; ->
c = . . . . . . . . . . . . . . . . 1 . str[i] != ' | " ; ->
c = . . . . . . . . . . . . . . . . . 0 str[i] == " ; q == " c == 1 -> c--; q = 0;
arg3 = [ ][a][r][g][3]
['][a]["][r]['][g][']["][1][']
c = 0 . . . . . . . . . .
c = 1 . . . . . . . . . str[i] == ' ; q == 0 c == 0 -> c++; q = '
c = . 1 . . . . . . . . str[i] != ' | " ; ->
c = . . 2 . . . . . . . str[i] == " ; q != " c == 1 -> c++;
c = . . . 2 . . . . . . str[i] != ' | " ; ->
c = . . . . 3 . . . . . str[i] == ' | " ; q == ' c == 2 -> c++;
c = . . . . . 3 . . . . str[i] != ' | " ; ->
c = . . . . . . 2 . . . str[i] == ' ; q == ' c == 3 -> c--; q = 0;
c = . . . . . . . 1 . . str[i] == " ; q != " c == 2 ->
ERROR ...
c = . . . . . . . . 1 . str[i] != ' | " ; ->
c = . . . . . . . . . 0 str[i] != ' | " ; ->
3.1.3 erreur :
-> comment le programme sait que cette fois il doit decrementer "c" ? en retenant dans l'ordre toutes les dernieres valeurs de "q" !
-> donc plutot partir sur une recursive
3.2 tentative methode 2 :
3.2.1 deroulement :
.--------------------------------------------.
: .--------------------------------------. :
: : .--------------------------------. : :
: : : .--------. .--------. : : :
: : : .--. : .--. : : .--. : : : :
'__"__'__"__"__"__'__'__"__"__'__'__"__'__"__'
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16
-> identifier les paires de quotes ou dquotes -> piège :
| |
.--. .--. v v .--.
'__"__'__"__"__"__'__'__"__"__'__'__"__'__"__'
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
- envoyer une copie de la str dans une fonction
- chercher la premiere pair
- l'effacer
- recommencer en boucle
- stop quand la premier quote a trouvé sa paire
- renvoyer l'emplacement de la pair
3.2.2 application :
.--.
1 : '__"__'__"__"__"__'__'__"__"__'__'__"__'__"__'
.--.
2 : '__"__'________"__'__'__"__"__'__'__"__'__"__'
.--------.
3 : '__"__'________"________"__"__'__'__"__'__"__'
.--.
4 : '__"__'____________________"__'__'__"__'__"__'
.--------.
5 : '__"__'____________________"________"__'__"__'
.--------------------------------.
6 : '__"__'________________________________'__"__'
.--------------------------------------.
7 : '__"______________________________________"__'
.--------------------------------------------.
8 : '____________________________________________'
3.3 comportement reel chelou :
echo "_"
_
echo "_'_'_"
_'_'_
echo "_'_"_"_'_"
_'___'_
echo "_'_"_'_'_"_'_"
_'_____'_
echo "_'_"_'_"_"_'_"_'_"
_'___"_"___'_
echo "_'_"_"_"_"_'_"
_'_____'_
echo "_'_"_'_"_"_"_"_'_"_'_"
_'___"_"_"_"___'_
echo "_'_"_'_"_'_'_"_'_"_'_"
_'___"___"___'_
echo "_'_"_'_"_'_"_"_'_"_'_"_'_"
_'___"_____"___'_
echo "_'_"_'_"_'_"_'_'_"_'_"_'_"_'_"
_'___"___'_'___"___'_
echo "_'_"_'_"_'_"_'_"_"_'_"_'_"_'_"_'_"
_'___"___'___'___"___'_
echo "_'_"_'_"_'_"_'_"_'_'_"_'_"_'_"_'_"_'_"
_'___"___'_____'___"___'_
echo "_'_"_'_"_'_"_'_"_'_"_"_'_"_'_"_'_"_'_"_'_"
_'___"___'___"_"___'___"___'_
4. notes :
idea about malloc protection :
have them(malloc and similar) done in a specific function that would check their return and accordingly exit the program or continue
-> so that it can be done in one line
void calloc_or_exit(int num, int size);
and possibly give it a pointer to a function that will clean before exit
void calloc_or_exit(int num, int size, void (*f)(void *ptr), void *ptr);
Ordre Interpreteur :
- Couper les mots (comment faire ? je ne vois pas comment gerer ce genre de bordel ci dessous)
shell@prompt> ./arg_test 'mot1 '$NAME" mot2"
argc = 2
argv[0] = |./arg_test|
argv[1] = |mot1 Tour-Lemdows10 mot2|
shell@prompt>
- $ - Env Vars Expand. if $VAR not set, expand to nothing(""). Dont save it as "" argument for fonctions, just delete.
Commandes dans variables d'env fonctionne :
shell@prompt> export VAR_TEST="echo"
shell@prompt> $VAR_TEST "string :)"
string :)
shell@prompt>
l'expension des variables dans des variables est fait au moment de export(), donc pas de recursivité infini et de prise de tête au moment de l'interpretation. Il suffit de faire "une passe" pour dévelloper les variables, aucun besoin de refaire des passes succesives pour d'éventuels nouvelles variables aprés dévellopement :
export :
export VAR_TEST="test $NAME"
dans l'env :
NAME="Tour-Lemdows10"
VAR_TEST="test Tour-Lemdows10"
export :
export NAME="Un nouveau nom"
dans l'env :
NAME="Un nouveau nom"
VAR_TEST="test Tour-Lemdows10"
export :
export VAR_TEST="test $VAR_TEST"
dans l'env :
VAR_TEST="test test Tour-Lemdows10"
export :
export VAR_TEST="test $VAR_TEST"
dans l'env :
VAR_TEST="test test test Tour-Lemdows10"
Tests pour Hugo, pour illustrer les pipes simultanés :
sleep 3 ; sleep 3 ; sleep 3 ; sleep 3
sleep 3 | sleep 3 | sleep 3 | sleep 3
sleep 5 ; ls | wc -l
sleep 5 | ls | wc -l
argv passé à execv(), telle quelle ou aprés avoir trouvé le PATH ? exemple, si j'apelle "cat file.txt", dans un shell genre bash/zsh, la commande cat recoit t'elle :
argv[0] == "cat"
argv[1] == "file.txt"
OU
argv[0] == "/bin/cat"
argv[1] == "file.txt"
A test sur bash et minishell :
echo "phrase quelquonque" > file1 > file2 > file3
Pour les builtins dans une commande simple :
- detecter la commande builtin
- Ne pas fork() et execv(). A la place, chercher la fonction aproprié dans un tableau contenant les pointeurs vers les fonctions builtins. Puis appeler la fonction dans le genre : builtin(ft_arrlen(cmd->argv), cmd->argv, &c).
Pour les builtins dans une commande complexe (pipes, redirections, here doc, ...) :
- detecter la commande builtin
- fork()
- Ne pas execv(). A la place, chercher la fonction aproprié dans un tableau contenant les pointeurs vers les fonctions builtins. Puis appeler la fonction dans le genre : builtin(ft_arrlen(cmd->argv), cmd->argv, &c).
- aprés la fin de la commande, free toute la mémoire du sous_processus, puis quitter le sous_processus en renvoyant le return de la commande builtin. par exemple comme ça : free_exit(&c, builtin(ft_arrlen(cmd->argv), cmd->argv, &c))
Peut-être faire un champ "void *builtin_command" dans une struct "cmd" pour contenir l'adresse de la fonction builtin.
typedef struct s_cmd
{
char **argv;
pid_t pid;
void *builtin_command;
} t_cmd;
Si la commande n'est pas builtin
cmd->builtin_command == NULL.
tester comment ce comporte minishell aprés le crash d'une commande (message sigfault comme dans bash ? Autres ?)
Implementer les exit status dans les builtins
EDIT : Aprés reflexion et verification, ça semble en effet le mieux a faire. En l'etat le return de la derniere fonction marche correctement, j'ai verifier pas de problemes. Si le child est deja fini avant l'apelle de wait, ça return quand même. Cependant, pour optimiser la liberation des resources dés que possible, il semble plus logique de liberer les sous_processus dés qu'ils ont terminés, et donc, pas nécessairement dans l'ordre de la pipeline. Donc, un waitpid() sur la derniere commande pour sauvegarder le retour, puis wait() pour les autres, l'ordre n'ayant plus d'importance.
Revoir le wait() de mon pipex. Il n'est peut être pas correct. En effet, j'attend que le premier process se ferme avec waitpid(), puis le deuxiéme, le troisiéme, ... Mais en faite, il vaudrait peut être mieux attendre qu'un sous process QUELQUONQUE se ferme, puis le suivant, et le suivant, ...
Vu le fonctionnement des pipes, je crois que ce n'est pas toujours la premiere commande qui ce termine en premier, et donc, qu'il ne faudrait pas attendre la fin de la premiere commande en particulier, mais d'une commande QUELQUONQUE avec wait() plutot que waitpid().
IDÉE : Attendre avec waitpid() la derniere commande pour obtenir son status, puis wait() en boucle les autres commandes, et enfin renvoyer le status.
Faire des test avec
"env -i ./minishell"
pour voir si ça ne crash pas.
Normal si le comportement attendu n'est pas correct (en l'absence de PATH et tout ça) mais ça ne devrait pas crasher pour autant.