123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655 |
- /*
- * File: parser.c
- * Author: Arthur Brandao
- *
- * Created on 31 octobre 2018
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include "error.h"
- #include "str.h"
- #include "wildcard.h"
- #include "ipc.h"
- #include "variable.h"
- #include "parser.h"
- /* --- Extern --- */
- extern int serrno;
- /* --- Fonctions privées --- */
- /**
- * Indique le nombre de commande dans une ligne
- * @param char* La ligne à analyser
- * @return int Le nombre de commande
- */
- int nb_commands(char* line){
- //Initialisation variable
- int compteur = 0;
- boolean vide = true;
- //Parcours chaine pour chercher séparateur et compter le nombre de commande
- while(*line){
- //Si on croise un caractère
- if(vide && *line != ' ' && *line != '\0'){
- vide = false;
- }
- //Si un ;
- if(*line == ';'){
- compteur++;
- }
- //Si | ou ||
- else if(*line == '|'){
- //Verif que ce n'est pas le dernier carac
- if(*(line + 1) == '\0'){
- serrno = SEBADEND;
- return SHELL_ERR;
- }
- //Si un || on avance de 1 en plus
- else if(*(line + 1) == '|'){
- //Si à la fin de la chaine
- if(*(line + 2) == '\0'){
- serrno = SEBADEND;
- return SHELL_ERR;
- }
- line++;
- }
- compteur++;
- }
- //Si un &&
- if(*line == '&'){
- //Si celui d'apres est bien un &&
- if(*(line + 1) == '&'){
- line++;
- compteur++;
- }
- //Sinon il doit y avoir un vide pour etre le & du bck ou un > avant
- else if(*(line + 1) != '\0' && *(line - 1) != '>'){
- serrno = SEBADET;
- return SHELL_ERR;
- }
- }
- line++;
- }
- //Ajoute la dernière commande
- if(!vide){
- compteur++;
- }
- return compteur;
- }
- /**
- * Recup la 1er commande d'une chaine de caractère pour initialiser une
- * structure Command
- * @param Command* Structure commande à initialiser
- * @param char* Ligne avec la commande
- * @return int Le décallage à effectuer dans ligne
- */
- int get_command(Command* c, char* line){
- //Declaration variable
- char* deb = line;
- int length = 0, separator = 0, next = SHELL_NONE;
- //Parcours chaine pour chercher un séparateur
- while(*line){
- //Si un ;
- if(*line == ';'){
- separator = 1;
- break;
- }
- //Si | ou ||
- else if(*line == '|'){
- //Si 1 ou 2 |
- if(*(line + 1) == '|'){
- separator = 2;
- next = SHELL_ELSE;
- } else {
- separator = 1;
- next = SHELL_PIPE;
- }
- break;
- }
- //Si un &&
- if(*line == '&'){
- //Si celui d'apres est bien un &&
- if(*(line + 1) == '&'){
- separator = 2;
- next = SHELL_IF;
- break;
- }
- }
- length++;
- line++;
- }
- //Verif si c'est la dernière commande
- if(!*line){
- next = SHELL_END;
- }
- //Allocation memoire et copie chaine
- c->cmd = malloc(sizeof(char) * (length + 1));
- memset(c->cmd, 0, length + 1);
- strncpy(c->cmd, deb, length);
- c->next = next;
- c->argc = 0;
- c->input = STDIN;
- c->output = STDOUT;
- c->error = STDERR;
- //Retour
- return length + separator;
- }
- /**
- * Parametre les fichiers d'entrées et de sorties d'une commande
- * @param Command* La structure de la commande
- * @param char* Le nom du fichier
- * @param int Le type de redirection (constante SHELLR)
- * @return int SHELL_OK si réussite, SHELL_ERR sinon
- */
- int set_io(Command* c, char* filename, int redir){
- printf("Redir : %d\n", redir);
- //Declaration variable
- int file;
- //Si fichier existe et on supprime
- if((redir == SHELLRE_OUT || redir == SHELLRE_ERR || redir == SHELLRE_ALL) && access(filename, F_OK) != ERR){
- if(unlink(filename) == ERR){
- addperror("Impossible de supprimer le fichier");
- serrno = SEOPENF;
- return SHELL_ERR;
- }
- }
- //Ouverture du fichier
- file = open(filename, O_CREAT | O_RDWR, S_IRWXU);
- if(file == ERR){
- addperror("Erreur lors de l'ouverture du fichier pour la redirection");
- serrno = SEOPENF;
- return SHELL_ERR;
- }
- //On se met à la fin du fichier si sortie ou erreur
- if(redir != SHELLR_IN){
- if(lseek(file, 0L, SEEK_END) == ERR){
- addperror("Impossible de se deplacer dans le fichier");
- serrno = SEOPENF;
- return SHELL_ERR;
- }
- }
- //Analyse dans quel descripteur il doit etre mis
- switch(redir){
- case SHELLR_IN:
- //Si un fichier deja ouvert
- if(c->input != STDIN){
- //On le ferme
- if(close(c->input) == -1){
- addperror("Erreur lors de la fermeture de l'ancien fichier de redirection");
- }
- }
- //Set nouveau fichier
- c->input = file;
- break;
- case SHELLR_OUT:
- case SHELLRE_OUT:
- //Si un fichier deja ouvert
- if(c->output != STDOUT){
- //On le ferme
- if(close(c->output) == -1){
- addperror("Erreur lors de la fermeture de l'ancien fichier de redirection");
- }
- }
- c->output = file;
- break;
- case SHELLR_ERR:
- case SHELLRE_ERR:
- //Si un fichier deja ouvert
- if(c->error != STDERR){
- //On le ferme
- if(close(c->error) == -1){
- addperror("Erreur lors de la fermeture de l'ancien fichier de redirection");
- }
- }
- c->error = file;
- break;
- case SHELLR_ALL:
- case SHELLRE_ALL:
- //Si un fichier deja ouvert
- if(c->output != STDOUT){
- //On le ferme
- if(close(c->output) == -1){
- addperror("Erreur lors de la fermeture de l'ancien fichier de redirection");
- }
- }
- if(c->error != STDERR){
- //On le ferme
- if(close(c->error) == -1){
- addperror("Erreur lors de la fermeture de l'ancien fichier de redirection");
- }
- }
- c->output = file;
- c->error = file;
- break;
- default :
- serrno = SEREDIRTYPE;
- return SHELL_ERR;
- }
- //Si on arrive ici tous est ok
- return SHELL_OK;
- }
- /**
- * Parametre les redirection d'une commande
- * @param Command* La commande à analyser
- * @return int SHELL_OK si réussite, SHELL_ERR sinon
- */
- int set_redirection(Command* c){
- boolean first = true, guillemet = false;
- char* deb, * file, * buffer = c->cmd + 1;
- int redir = -1, compteur, finCmd = 0;
- //Parcours chaine
- while(*buffer){
- //Repere redirection
- while(*buffer){
- //Entrée
- if(*buffer == '<'){
- //Si il n'y a rien apres
- if(!(*(buffer + 1))){
- serrno = SEBADREDIR;
- return SHELL_ERR;
- }
- buffer++;
- redir = SHELLR_IN;
- break;
- }
- //Sortie
- else if (*buffer == '>'){
- //Si il n'y a rien apres
- if(!(*(buffer + 1))){
- serrno = SEBADREDIR;
- return SHELL_ERR;
- } else {
- buffer++;
- //Si >>
- if(*buffer == '>'){
- //Si il n'y a rien apres
- if(!(*(buffer + 1))){
- serrno = SEBADREDIR;
- return SHELL_ERR;
- } else {
- buffer++;
- //Si >>&
- if(*buffer == '&'){
- //Si il n'y a rien apres
- if(!(*(buffer + 1))){
- serrno = SEBADREDIR;
- return SHELL_ERR;
- }
- buffer++;
- redir = SHELLR_ALL;
- }
- //Sinon toujours >>
- else {
- redir = SHELLR_OUT;
- }
- }
- }
- // Si >&
- else if(*buffer == '&'){
- //Si il n'y a rien apres
- if(!(*(buffer + 1))){
- serrno = SEBADREDIR;
- return SHELL_ERR;
- }
- redir = SHELLRE_ALL;
- buffer++;
- }
- //Sinon >
- else {
- redir = SHELLRE_OUT;
- }
- }
- break;
- }
- //Sortie erreur
- else if (*buffer == '2' && *(buffer - 1) == ' ' && *(buffer + 1) && *(buffer + 1) == '>'){
- buffer++;
- //Si il n'y a rien apres
- if(!(*(buffer + 1))){
- serrno = SEBADREDIR;
- return SHELL_ERR;
- } else {
- buffer++;
- //Si 2>>
- if(*buffer == '>'){
- buffer++;
- //Si il n'y a rien apres
- if(!(*(buffer + 1))){
- serrno = SEBADREDIR;
- return SHELL_ERR;
- }
- redir = SHELLR_ERR;
- }
- //Sinon 2>
- else {
- redir = SHELLRE_ERR;
- }
- }
- break;
- }
- buffer++;
- //On incremente pour trouver la fin de la commande
- if(first){
- finCmd++;
- }
- }
- //Apres ce n'est que des redirection
- first = false;
- //On passe les espaces
- while(*buffer == ' '){
- buffer++;
- }
- //Si on est à la fin de la chaine
- if(*buffer == '\0'){
- if(redir == -1){
- //Aucune redirection
- return finCmd + 1;
- }
- //Sinon on est dans un redirection non terminée
- serrno = SEBADCMD;
- return SHELL_ERR;
- }
- //Regarde si le nom du fichier est entre ""
- guillemet = *buffer == '"';
- //Sauvegarde debut nom fichier
- deb = buffer;
- compteur = 0;
- //Lecture nom du fichier
- while(*buffer){
- //Incremente
- compteur++;
- buffer++;
- //Test arret
- if(guillemet && *buffer == '"'){
- break;
- }
- else if(!guillemet && (*buffer == '<' || *buffer == '>' || (*buffer == ' ' && *(buffer - 1) != '\\'))){
- break;
- }
- }
- //Si fin de la commande et guillemet alors erreur
- if(!(*buffer) && guillemet){
- serrno = SEBADCMD;
- return SHELL_ERR;
- }
- //Retire guillement si besoin
- if(guillemet){
- deb++;
- compteur--;
- buffer++;
- }
- //Allocation file et copie nom fichier
- file = malloc(sizeof(char) * compteur);
- memset(file, 0, compteur);
- strncpy(file, deb, compteur);
- //Redirection
- if(set_io(c, file, redir) == SHELL_ERR){
- free(file);
- return SHELL_ERR;
- }
- free(file);
- //Passe les espaces
- while(*buffer == ' '){
- buffer++;
- }
- }
- //Si on arrive ici tous est ok
- return finCmd + 1;
- }
- /**
- * Sépare une commande en chaine de caractere en un tableau contenant chaque
- * argument
- * @param Command* La structure d'accueil du resultat
- * @param char* La commande à découper (sans les redirections)
- * @return int SHELL_OK si réussite, SHELL_ERR sinon
- */
- int split_command(Command* c, char* cmd){
- //Declaration variable
- char* deb = cmd;
- int nb = 0, length = 0, i = 0;
- char delim = ' ';
- boolean chercheFin = false;
- //Compte le nombre d'argument
- while(*cmd != '\0' && *cmd != '&'){
- //Cherche debut d'un mot
- if(*cmd != ' ' && !chercheFin){
- chercheFin = true;
- //Guillemet
- if(*cmd == '"'){
- //Verif que ce n'est pas la fin
- if(!(*(cmd + 1))){
- serrno = SEBADCMD;
- return SHELL_ERR;
- }
- cmd++;
- delim = '"';
- }
- //Cote
- else if(*cmd == '\''){
- //Verif que ce n'est pas la fin
- if(!(*(cmd + 1))){
- serrno = SEBADCMD;
- return SHELL_ERR;
- }
- cmd++;
- delim = '\'';
- }
- //Mot sans guillemet autour
- else {
- length = 0;
- delim = ' ';
- }
- }
- //Fin d'un mot
- else if(*cmd == delim && chercheFin){
- //Adapte si il y avait des guillemet
- chercheFin = false;
- nb++;
- }
- //Incremente
- cmd++;
- }
- //Si on termine sur un mot sans "
- if(chercheFin){
- nb++;
- }
- //Allocation + retour au debut
- c->argv = malloc(sizeof(char*) * (nb + 1));
- c->argc = nb;
- cmd = deb;
- //Parcours chaine et decoupe
- while(*cmd != '\0' && *cmd != '&'){
- //Cherche debut d'un mot
- if(*cmd != ' ' && !chercheFin){
- chercheFin = true;
- //Guillemet
- if(*cmd == '"'){
- cmd++;
- deb = cmd;
- length = 0;
- delim = '"';
- }
- //Cote
- else if(*cmd == '\''){
- cmd++;
- deb = cmd;
- length = 0;
- delim = '\'';
- }
- //Mot sans guillemet autour
- else {
- deb = cmd;
- length = 0;
- delim = ' ';
- }
- }
- //Fin d'un mot
- else if(*cmd == delim && chercheFin){
- chercheFin = false;
- //Recup le mot
- c->argv[i] = malloc(sizeof(char) * (length + 1));
- memset(c->argv[i], 0, length + 1);
- strncpy(c->argv[i++], deb, length);
- }
- //Incremente
- cmd++;
- length++;
- }
- //Recup le dernier mot si besoin
- if(chercheFin){
- c->argv[i] = malloc(sizeof(char) * (length + 1));
- memset(c->argv[i], 0, length + 1);
- strncpy(c->argv[i++], deb, length);
- }
- //Set la dernière case du tableau à null
- c->argv[i] = NULL;
- return SHELL_OK;
- }
- /* --- Fonctions publiques --- */
- int parse_line(CommandTab* ct, char* line){
- //Declaration variable
- int compteur, tmp;
- //Nettoyage ligne
- line = trim(mtrim(line, '\n'));
- //Compte le nombre de commande dans la ligne
- compteur = nb_commands(line);
- if(compteur == SHELL_ERR){
- return SHELL_ERR;
- }
- //Initialisation structure
- tmp = strlen(line);
- ct->line = malloc(sizeof(char) * (tmp + 1));
- memset(ct->line, 0, tmp + 1);
- strncpy(ct->line, line, tmp);
- ct->cmd = malloc(sizeof(Command*) * compteur);
- ct->length = compteur;
- ct->bck = line[strlen(line) - 1] == '&';
- //Si il y a un un & on le retire (on à deja l'information)
- if(ct->bck){
- line[strlen(line) - 1] = '\0';
- }
- //Recupération de chaque commande
- for(int i = 0; i < compteur; i++){
- ct->cmd[i] = malloc(sizeof(Command));
- tmp = get_command(ct->cmd[i], line);
- line += tmp;
- //Si pas dernière commande on trim
- if(i + 1 < compteur){
- line = ltrim(line, ' ');
- }
- }
- //Retour
- return SHELL_OK;
- }
- int parse_command(Command* c){
- //Declaration variable
- int length, nbWildcard = 0, res;
- char* cmd, * str, **wildcardTab;
- //Parse les redirections
- length = set_redirection(c);
- if(length == SHELL_ERR || length == 0){
- return SHELL_ERR;
- }
- //Recup la commande (sans redirection)
- cmd = malloc(sizeof(char) * (length + 1));
- memset(cmd, 0, length + 1);
- strncpy(cmd, c->cmd, length);
- //Split la commande
- split_command(c, cmd);
- //Remplace variables
- for(int i = 0; i < c->argc; i++){
- str = parse_local_var(c->argv[i]);
- free(c->argv[i]);
- c->argv[i] = str;
- }
- for(int i = 0; i < c->argc; i++){
- str = parse_shm_var(c->argv[i]);
- free(c->argv[i]);
- c->argv[i] = str;
- }
- //Analyse wildcard
- for(int i = 0; i < c->argc; i++){
- //Regarde si il faut remplacer l'argument par une suite de fichier
- nbWildcard = wildcard_result(c->argv[i]);
- if(nbWildcard > 0){
- //Si il y a des resultats on prepare un tableau pour les récupérer
- wildcardTab = malloc(sizeof(char*) * nbWildcard);
- nbWildcard = wildcard(c->argv[i], nbWildcard, wildcardTab);
- //Verif retour
- if(nbWildcard == ERR){
- serrno = SEWC;
- return SHELL_ERR;
- }
- //Ajoute les wildcard dans argv (le +1 est la pour garder le NULL à la fin)
- c->argv = insert_array(i, c->argv, c->argc + 1, wildcardTab, nbWildcard, &res);
- if(res == ERR){
- serrno = SEADDWC;
- return SHELL_ERR;
- }
- c->argc = res - 1; //On ne compte pas le NULL final
- }
- }
- //Ajout nom commande
- c->name = c->argv[0];
- //Ici tous est ok
- return SHELL_OK;
- }
- int parse_all_command(CommandTab* ct){
- int tmp;
- for(int i = 0; i < ct->length; i++){
- tmp = parse_command(ct->cmd[i]);
- if(tmp != SHELL_OK){
- return SHELL_FAIL;
- }
- }
- return SHELL_OK;
- }
- void clean_command(CommandTab* ct){
- extern int errno;
- //Vide le tableau
- for(int i = 0; i < ct->length; i++){
- //Si la commande a été parsée on vide les arguments
- if(ct->cmd[i]->argc > 0){
- ct->cmd[i]->name = NULL;
- for(int j = 0; j < ct->cmd[i]->argc; j++){
- free(ct->cmd[i]->argv[j]);
- }
- }
- //Ferme les fichiers ouverts si besoin
- if(ct->cmd[i]->input != STDIN){
- if(close(ct->cmd[i]->input)){
- fprintf(stderr, "Erreur lors de la fermeture du fichier d'input de %s : %s\n", ct->cmd[i]->cmd, strerror(errno));
- }
- }
- if(ct->cmd[i]->output != STDOUT){
- if(close(ct->cmd[i]->output)){
- fprintf(stderr, "Erreur lors de la fermeture du fichier d'output de %s : %s\n", ct->cmd[i]->cmd, strerror(errno));
- }
- }
- if(ct->cmd[i]->error != STDERR && ct->cmd[i]->error != ct->cmd[i]->output){ //Verif en plus qu'il est different du fichier de sortie standard (>& et >>&)
- if(close(ct->cmd[i]->error)){
- fprintf(stderr, "Erreur lors de la fermeture du fichier d'error de %s : %s\n", ct->cmd[i]->cmd, strerror(errno));
- }
- }
- //Supprime la ligne de commande
- free(ct->cmd[i]->cmd);
- //Supprime la structure
- free(ct->cmd[i]);
- }
- //Met à 0 la taille du tableau
- ct->length = 0;
- free(ct->line);
- }
|