Files
42_INT_07_minishell/README.md
2021-10-17 22:12:55 +02:00

16 KiB

sommaire :


1. todo list :


  • global features :
    • prompt show a prompt
    • history ? the command history ?
    • binaries fetch and launch the right executable
    • ' " ; \ ? don't interpret special characters and unclosed quotes ?
  • pipes : video vulgarisation
    • | pipes
  • expensions :
  • 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 :
    • ^C close process
    • ^D exit minishell
    • ^\ do nothing
  • builtins (can't be executed in child process) :
    • cd <relative path>
    • cd <absolute path>
    • pwd
    • export
    • unset
    • exit
  • builtins (don't need to be) : source
    • env
    • echo
    • echo -n
  • autres :

\go to sommaire

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]

\go to sommaire

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++;
}

\go to sommaire

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] != ' | " ;                  ->

\go to sommaire

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

\go to sommaire

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
  1. envoyer une copie de la str dans une fonction
  2. chercher la premiere pair
  3. l'effacer
  4. recommencer en boucle
  5. stop quand la premier quote a trouvé sa paire
  6. renvoyer l'emplacement de la pair

\go to sommaire

3.2.2 application :

               .--.
1 :   '__"__'__"__"__"__'__'__"__"__'__'__"__'__"__'

                        .--.
2 :   '__"__'________"__'__'__"__"__'__'__"__'__"__'

                     .--------.
3 :   '__"__'________"________"__"__'__'__"__'__"__'

                                    .--.
4 :   '__"__'____________________"__'__'__"__'__"__'

                                 .--------.
5 :   '__"__'____________________"________"__'__"__'

            .--------------------------------.
6 :   '__"__'________________________________'__"__'

         .--------------------------------------.
7 :   '__"______________________________________"__'

      .--------------------------------------------.
8 :   '____________________________________________'

\go to sommaire

3.3 comportement reel chelou :

echo "_"
_

echo "_'_'_"
_'_'_

echo "_'_"_"_'_"
_'___'_

echo "_'_"_'_'_"_'_" 
_'_____'_

echo "_'_"_'_"_"_'_"_'_"
_'___"_"___'_

echo "_'_"_"_"_"_'_"    
_'_____'_

echo "_'_"_'_"_"_"_"_'_"_'_"
_'___"_"_"_"___'_

echo "_'_"_'_"_'_'_"_'_"_'_"
_'___"___"___'_

echo "_'_"_'_"_'_"_"_'_"_'_"_'_"
_'___"_____"___'_

echo "_'_"_'_"_'_"_'_'_"_'_"_'_"_'_"
_'___"___'_'___"___'_

echo "_'_"_'_"_'_"_'_"_"_'_"_'_"_'_"_'_"
_'___"___'___'___"___'_

echo "_'_"_'_"_'_"_'_"_'_'_"_'_"_'_"_'_"_'_"
_'___"___'_____'___"___'_

echo "_'_"_'_"_'_"_'_"_'_"_"_'_"_'_"_'_"_'_"_'_"
_'___"___'___"_"___'___"___'_

\go to sommaire

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 :

  1. 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>
  1. $ - Env Vars Expand. if $VAR not set, expand to nothing(""). Dont save it as "" argument for fonctions, just delete.

\go to sommaire


Commandes dans variables d'env fonctionne :

shell@prompt> export VAR_TEST="echo"
shell@prompt> $VAR_TEST "string :)"
string :)
shell@prompt>

\go to sommaire


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"

\go to sommaire


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

\go to sommaire


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"

\go to sommaire


A test sur bash et minishell :

echo "phrase quelquonque" > file1 > file2 > file3

\go to sommaire


Pour les builtins dans une commande simple :

  1. detecter la commande builtin
  2. 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, ...) :

  1. detecter la commande builtin
  2. fork()
  3. 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).
  4. 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.

\go to sommaire


tester comment ce comporte minishell aprés le crash d'une commande (message sigfault comme dans bash ? Autres ?)

\go to sommaire


Implementer les exit status dans les builtins

\go to sommaire


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.

\go to sommaire


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.

\go to sommaire