external functions :
readline :
- readline
- rl_clear_history
- rl_on_new_line
- rl_replace_line
- rl_redisplay
- add_history
files :
- access
- open
- read
- close
- dup
- dup2
- pipe
- stat :
int stat(const char *pathname, struct stat *statbuf);returns information about a file, in the buffer pointed to by statbuf - lstat :
int fstat(int fd, struct stat *statbuf);lstat() is identical to stat(), except that if pathname is a symbolic link, then it returns information about the link itself, not the file that it refers to - fstat :
int lstat(const char *pathname, struct stat *statbuf);fstat() is identical to stat(), except that the file about which information is to be retrieved is specified by the file descriptor fd - unlink :
int unlink(const char *pathname);unlink() deletes a name from the filesystem
process :
- fork
- wait
- waitpid
- wait3
- wait4
- exit
signals :
- signal
- sigaction
- kill
directories :
- getcwd :
char *getcwd(char *buf, size_t size);returns a null-terminated string containing an absolute pathname that is the current working directory of the calling process - chdir :
int chdir(const char *path);changes the current working directory of the calling process to the directory specified in path - execve :
int execve(const char *filename, char *const argv[], char *const envp[]);executes the program pointed to by filename - opendir :
DIR *opendir(const char *name);opens a directory stream corresponding to the directory name, and returns a pointer to the directory stream - readdir :
struct dirent *readdir(DIR *dirp);returns a pointer to a dirent structure representing the next directory entry in the directory stream pointed to by dirp - closedir :
int closedir(DIR *dirp);closes the directory stream associated with dirp
errors :
- strerror :
char *strerror(int errnum);returns a pointer to a string that describes the error code passed in the argument errnum - perror :
void perror(const char *s);produces a message on standard error describing the last error encountered during a call to a system or library function
other :
-
printf
-
malloc
-
free
-
write
-
isatty
-
ttyname
-
ttyslot
-
ioctl
-
getenv
-
tcsetattr
-
tcgetattr
-
tgetent
-
tgetflag
-
tgetnum
-
tgetstr
-
tgoto
-
tputs
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. parsing :
2.1 methode arbre binaire :
transformer arbre normal en arbre binaire
ARCHITECTURE :
all
expend $
. pipes
. . redirections (program function or file)
. . . arguments (nom arg arg arg ...)
. . . .
EXEMPLE : . .
[sort < ./prgrm 'arg1 '$VARIABLE" arg3" | grep "word.$EXTENSION" | wc -l > file]
[sort < ./prgrm 'arg1 'arg2" arg3" | grep "word.md" | wc -l > file]
. [sort < ./prgrm 'arg1 'arg2" arg3"]
. . [sort]
. . [./prgrm 'arg1 'arg2" arg3"]
. . . [./prgrm]
. . . ['arg1 ']
. . . [arg2]
. . . [" arg3"]
. [grep "word.md"]
. . . [grep]
. . . ["word.md"]
. [wc -l > file]
. . [wc -l]
. . . [wc]
. . . [-l]
. . [file]
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 :
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.