/* * File: parser.c * Author: Arthur Brandao * * Created on 31 octobre 2018 */ #include #include #include #include #include #include #include #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){ //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 + 1)); memset(file, 0, compteur + 1); 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); }