parser.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. /*
  2. * File: parser.c
  3. * Author: Arthur Brandao
  4. *
  5. * Created on 31 octobre 2018
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <unistd.h>
  10. #include <errno.h>
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #include <fcntl.h>
  14. #include "error.h"
  15. #include "str.h"
  16. #include "wildcard.h"
  17. #include "parser.h"
  18. /* --- Extern --- */
  19. extern int serrno;
  20. /* --- Fonctions privées --- */
  21. /**
  22. * Indique le nombre de commande dans une ligne
  23. * @param char* La ligne à analyser
  24. * @return int Le nombre de commande
  25. */
  26. int nb_commands(char* line){
  27. //Initialisation variable
  28. int compteur = 0;
  29. boolean vide = true;
  30. //Parcours chaine pour chercher séparateur et compter le nombre de commande
  31. while(*line){
  32. //Si on croise un caractère
  33. if(vide && *line != ' ' && *line != '\0'){
  34. vide = false;
  35. }
  36. //Si un ;
  37. if(*line == ';'){
  38. compteur++;
  39. }
  40. //Si | ou ||
  41. else if(*line == '|'){
  42. //Verif que ce n'est pas le dernier carac
  43. if(*(line + 1) == '\0'){
  44. serrno = SEBADEND;
  45. return SHELL_ERR;
  46. }
  47. //Si un || on avance de 1 en plus
  48. else if(*(line + 1) == '|'){
  49. //Si à la fin de la chaine
  50. if(*(line + 2) == '\0'){
  51. serrno = SEBADEND;
  52. return SHELL_ERR;
  53. }
  54. line++;
  55. }
  56. compteur++;
  57. }
  58. //Si un &&
  59. if(*line == '&'){
  60. //Si celui d'apres est bien un &&
  61. if(*(line + 1) == '&'){
  62. line++;
  63. compteur++;
  64. }
  65. //Sinon il doit y avoir un vide pour etre le & du bck ou un > avant
  66. else if(*(line + 1) != '\0' && *(line - 1) != '>'){
  67. serrno = SEBADET;
  68. return SHELL_ERR;
  69. }
  70. }
  71. line++;
  72. }
  73. //Ajoute la dernière commande
  74. if(!vide){
  75. compteur++;
  76. }
  77. return compteur;
  78. }
  79. /**
  80. * Recup la 1er commande d'une chaine de caractère pour initialiser une
  81. * structure Command
  82. * @param Command* Structure commande à initialiser
  83. * @param char* Ligne avec la commande
  84. * @return int Le décallage à effectuer dans ligne
  85. */
  86. int get_command(Command* c, char* line){
  87. //Declaration variable
  88. char* deb = line;
  89. int length = 0, separator = 0, next = SHELL_NONE;
  90. //Parcours chaine pour chercher un séparateur
  91. while(*line){
  92. //Si un ;
  93. if(*line == ';'){
  94. separator = 1;
  95. break;
  96. }
  97. //Si | ou ||
  98. else if(*line == '|'){
  99. //Si 1 ou 2 |
  100. if(*(line + 1) == '|'){
  101. separator = 2;
  102. next = SHELL_ELSE;
  103. } else {
  104. separator = 1;
  105. next = SHELL_PIPE;
  106. }
  107. break;
  108. }
  109. //Si un &&
  110. if(*line == '&'){
  111. //Si celui d'apres est bien un &&
  112. if(*(line + 1) == '&'){
  113. separator = 2;
  114. next = SHELL_IF;
  115. break;
  116. }
  117. }
  118. length++;
  119. line++;
  120. }
  121. //Verif si c'est la dernière commande
  122. if(!*line){
  123. next = SHELL_END;
  124. }
  125. //Allocation memoire et copie chaine
  126. c->cmd = malloc(sizeof(char) * (length + 1));
  127. memset(c->cmd, 0, length + 1);
  128. strncpy(c->cmd, deb, length);
  129. c->next = next;
  130. c->argc = 0;
  131. c->input = STDIN;
  132. c->output = STDOUT;
  133. c->error = STDERR;
  134. //Retour
  135. return length + separator;
  136. }
  137. /**
  138. * Parametre les fichiers d'entrées et de sorties d'une commande
  139. * @param Command* La structure de la commande
  140. * @param char* Le nom du fichier
  141. * @param int Le type de redirection (constante SHELLR)
  142. * @return int SHELL_OK si réussite, SHELL_ERR sinon
  143. */
  144. int set_io(Command* c, char* filename, int redir){
  145. printf("Redir : %d\n", redir);
  146. //Declaration variable
  147. int file;
  148. //Si fichier existe et on supprime
  149. if((redir == SHELLRE_OUT || redir == SHELLRE_ERR || redir == SHELLRE_ALL) && access(filename, F_OK) != ERR){
  150. if(unlink(filename) == ERR){
  151. addperror("Impossible de supprimer le fichier");
  152. serrno = SEOPENF;
  153. return SHELL_ERR;
  154. }
  155. }
  156. //Ouverture du fichier
  157. file = open(filename, O_CREAT | O_RDWR, S_IRWXU);
  158. if(file == ERR){
  159. addperror("Erreur lors de l'ouverture du fichier pour la redirection");
  160. serrno = SEOPENF;
  161. return SHELL_ERR;
  162. }
  163. //On se met à la fin du fichier si sortie ou erreur
  164. if(redir != SHELLR_IN){
  165. if(lseek(file, 0L, SEEK_END) == ERR){
  166. addperror("Impossible de se deplacer dans le fichier");
  167. serrno = SEOPENF;
  168. return SHELL_ERR;
  169. }
  170. }
  171. //Analyse dans quel descripteur il doit etre mis
  172. switch(redir){
  173. case SHELLR_IN:
  174. //Si un fichier deja ouvert
  175. if(c->input != STDIN){
  176. //On le ferme
  177. if(close(c->input) == -1){
  178. addperror("Erreur lors de la fermeture de l'ancien fichier de redirection");
  179. }
  180. }
  181. //Set nouveau fichier
  182. c->input = file;
  183. break;
  184. case SHELLR_OUT:
  185. case SHELLRE_OUT:
  186. //Si un fichier deja ouvert
  187. if(c->output != STDOUT){
  188. //On le ferme
  189. if(close(c->output) == -1){
  190. addperror("Erreur lors de la fermeture de l'ancien fichier de redirection");
  191. }
  192. }
  193. c->output = file;
  194. break;
  195. case SHELLR_ERR:
  196. case SHELLRE_ERR:
  197. //Si un fichier deja ouvert
  198. if(c->error != STDERR){
  199. //On le ferme
  200. if(close(c->error) == -1){
  201. addperror("Erreur lors de la fermeture de l'ancien fichier de redirection");
  202. }
  203. }
  204. c->error = file;
  205. break;
  206. case SHELLR_ALL:
  207. case SHELLRE_ALL:
  208. //Si un fichier deja ouvert
  209. if(c->output != STDOUT){
  210. //On le ferme
  211. if(close(c->output) == -1){
  212. addperror("Erreur lors de la fermeture de l'ancien fichier de redirection");
  213. }
  214. }
  215. if(c->error != STDERR){
  216. //On le ferme
  217. if(close(c->error) == -1){
  218. addperror("Erreur lors de la fermeture de l'ancien fichier de redirection");
  219. }
  220. }
  221. c->output = file;
  222. c->error = file;
  223. break;
  224. default :
  225. serrno = SEREDIRTYPE;
  226. return SHELL_ERR;
  227. }
  228. //Si on arrive ici tous est ok
  229. return SHELL_OK;
  230. }
  231. /**
  232. * Parametre les redirection d'une commande
  233. * @param Command* La commande à analyser
  234. * @return int SHELL_OK si réussite, SHELL_ERR sinon
  235. */
  236. int set_redirection(Command* c){
  237. boolean first = true, guillemet = false;
  238. char* deb, * file, * buffer = c->cmd + 1;
  239. int redir = -1, compteur, finCmd = 0;
  240. //Parcours chaine
  241. while(*buffer){
  242. //Repere redirection
  243. while(*buffer){
  244. //Entrée
  245. if(*buffer == '<'){
  246. //Si il n'y a rien apres
  247. if(!(*(buffer + 1))){
  248. serrno = SEBADREDIR;
  249. return SHELL_ERR;
  250. }
  251. buffer++;
  252. redir = SHELLR_IN;
  253. break;
  254. }
  255. //Sortie
  256. else if (*buffer == '>'){
  257. //Si il n'y a rien apres
  258. if(!(*(buffer + 1))){
  259. serrno = SEBADREDIR;
  260. return SHELL_ERR;
  261. } else {
  262. buffer++;
  263. //Si >>
  264. if(*buffer == '>'){
  265. //Si il n'y a rien apres
  266. if(!(*(buffer + 1))){
  267. serrno = SEBADREDIR;
  268. return SHELL_ERR;
  269. } else {
  270. buffer++;
  271. //Si >>&
  272. if(*buffer == '&'){
  273. //Si il n'y a rien apres
  274. if(!(*(buffer + 1))){
  275. serrno = SEBADREDIR;
  276. return SHELL_ERR;
  277. }
  278. buffer++;
  279. redir = SHELLR_ALL;
  280. }
  281. //Sinon toujours >>
  282. else {
  283. redir = SHELLR_OUT;
  284. }
  285. }
  286. }
  287. // Si >&
  288. else if(*buffer == '&'){
  289. //Si il n'y a rien apres
  290. if(!(*(buffer + 1))){
  291. serrno = SEBADREDIR;
  292. return SHELL_ERR;
  293. }
  294. redir = SHELLRE_ALL;
  295. buffer++;
  296. }
  297. //Sinon >
  298. else {
  299. redir = SHELLRE_OUT;
  300. }
  301. }
  302. break;
  303. }
  304. //Sortie erreur
  305. else if (*buffer == '2' && *(buffer - 1) == ' ' && *(buffer + 1) && *(buffer + 1) == '>'){
  306. buffer++;
  307. //Si il n'y a rien apres
  308. if(!(*(buffer + 1))){
  309. serrno = SEBADREDIR;
  310. return SHELL_ERR;
  311. } else {
  312. buffer++;
  313. //Si 2>>
  314. if(*buffer == '>'){
  315. buffer++;
  316. //Si il n'y a rien apres
  317. if(!(*(buffer + 1))){
  318. serrno = SEBADREDIR;
  319. return SHELL_ERR;
  320. }
  321. redir = SHELLR_ERR;
  322. }
  323. //Sinon 2>
  324. else {
  325. redir = SHELLRE_ERR;
  326. }
  327. }
  328. break;
  329. }
  330. buffer++;
  331. //On incremente pour trouver la fin de la commande
  332. if(first){
  333. finCmd++;
  334. }
  335. }
  336. //Apres ce n'est que des redirection
  337. first = false;
  338. //On passe les espaces
  339. while(*buffer == ' '){
  340. buffer++;
  341. }
  342. //Si on est à la fin de la chaine
  343. if(*buffer == '\0'){
  344. if(redir == -1){
  345. //Aucune redirection
  346. return finCmd + 1;
  347. }
  348. //Sinon on est dans un redirection non terminée
  349. serrno = SEBADCMD;
  350. return SHELL_ERR;
  351. }
  352. //Regarde si le nom du fichier est entre ""
  353. guillemet = *buffer == '"';
  354. //Sauvegarde debut nom fichier
  355. deb = buffer;
  356. compteur = 0;
  357. //Lecture nom du fichier
  358. while(*buffer){
  359. //Incremente
  360. compteur++;
  361. buffer++;
  362. //Test arret
  363. if(guillemet && *buffer == '"'){
  364. break;
  365. }
  366. else if(!guillemet && (*buffer == '<' || *buffer == '>' || (*buffer == ' ' && *(buffer - 1) != '\\'))){
  367. break;
  368. }
  369. }
  370. //Si fin de la commande et guillemet alors erreur
  371. if(!(*buffer) && guillemet){
  372. serrno = SEBADCMD;
  373. return SHELL_ERR;
  374. }
  375. //Retire guillement si besoin
  376. if(guillemet){
  377. deb++;
  378. compteur--;
  379. buffer++;
  380. }
  381. //Allocation file et copie nom fichier
  382. file = malloc(sizeof(char) * compteur);
  383. memset(file, 0, compteur);
  384. strncpy(file, deb, compteur);
  385. //Redirection
  386. if(set_io(c, file, redir) == SHELL_ERR){
  387. free(file);
  388. return SHELL_ERR;
  389. }
  390. free(file);
  391. //Passe les espaces
  392. while(*buffer == ' '){
  393. buffer++;
  394. }
  395. }
  396. //Si on arrive ici tous est ok
  397. return finCmd + 1;
  398. }
  399. /**
  400. * Sépare une commande en chaine de caractere en un tableau contenant chaque
  401. * argument
  402. * @param Command* La structure d'accueil du resultat
  403. * @param char* La commande à découper (sans les redirections)
  404. * @return int SHELL_OK si réussite, SHELL_ERR sinon
  405. */
  406. int split_command(Command* c, char* cmd){
  407. //Declaration variable
  408. char* deb = cmd;
  409. int nb = 0, length = 0, i = 0;
  410. char delim = ' ';
  411. boolean chercheFin = false;
  412. //Compte le nombre d'argument
  413. while(*cmd != '\0' && *cmd != '&'){
  414. //Cherche debut d'un mot
  415. if(*cmd != ' ' && !chercheFin){
  416. chercheFin = true;
  417. //Guillemet
  418. if(*cmd == '"'){
  419. //Verif que ce n'est pas la fin
  420. if(!(*(cmd + 1))){
  421. serrno = SEBADCMD;
  422. return SHELL_ERR;
  423. }
  424. cmd++;
  425. delim = '"';
  426. }
  427. //Cote
  428. else if(*cmd == '\''){
  429. //Verif que ce n'est pas la fin
  430. if(!(*(cmd + 1))){
  431. serrno = SEBADCMD;
  432. return SHELL_ERR;
  433. }
  434. cmd++;
  435. delim = '\'';
  436. }
  437. //Mot sans guillemet autour
  438. else {
  439. length = 0;
  440. delim = ' ';
  441. }
  442. }
  443. //Fin d'un mot
  444. else if(*cmd == delim && chercheFin){
  445. //Adapte si il y avait des guillemet
  446. chercheFin = false;
  447. nb++;
  448. }
  449. //Incremente
  450. cmd++;
  451. }
  452. //Si on termine sur un mot sans "
  453. if(chercheFin){
  454. nb++;
  455. }
  456. //Allocation + retour au debut
  457. c->argv = malloc(sizeof(char*) * (nb + 1));
  458. c->argc = nb;
  459. cmd = deb;
  460. //Parcours chaine et decoupe
  461. while(*cmd != '\0' && *cmd != '&'){
  462. //Cherche debut d'un mot
  463. if(*cmd != ' ' && !chercheFin){
  464. chercheFin = true;
  465. //Guillemet
  466. if(*cmd == '"'){
  467. cmd++;
  468. deb = cmd;
  469. length = 0;
  470. delim = '"';
  471. }
  472. //Cote
  473. else if(*cmd == '\''){
  474. cmd++;
  475. deb = cmd;
  476. length = 0;
  477. delim = '\'';
  478. }
  479. //Mot sans guillemet autour
  480. else {
  481. deb = cmd;
  482. length = 0;
  483. delim = ' ';
  484. }
  485. }
  486. //Fin d'un mot
  487. else if(*cmd == delim && chercheFin){
  488. chercheFin = false;
  489. //Recup le mot
  490. c->argv[i] = malloc(sizeof(char) * (length + 1));
  491. memset(c->argv[i], 0, length + 1);
  492. strncpy(c->argv[i++], deb, length);
  493. }
  494. //Incremente
  495. cmd++;
  496. length++;
  497. }
  498. //Recup le dernier mot si besoin
  499. if(chercheFin){
  500. c->argv[i] = malloc(sizeof(char) * (length + 1));
  501. memset(c->argv[i], 0, length + 1);
  502. strncpy(c->argv[i++], deb, length);
  503. }
  504. //Set la dernière case du tableau à null
  505. c->argv[i] = NULL;
  506. return SHELL_OK;
  507. }
  508. /* --- Fonctions publiques --- */
  509. int parse_line(CommandTab* ct, char* line){
  510. //Declaration variable
  511. int compteur, tmp;
  512. //Nettoyage ligne
  513. line = trim(mtrim(line, '\n'));
  514. //Compte le nombre de commande dans la ligne
  515. compteur = nb_commands(line);
  516. if(compteur == SHELL_ERR){
  517. return SHELL_ERR;
  518. }
  519. //Initialisation structure
  520. tmp = strlen(line);
  521. ct->line = malloc(sizeof(char) * (tmp + 1));
  522. memset(ct->line, 0, tmp + 1);
  523. strncpy(ct->line, line, tmp);
  524. ct->cmd = malloc(sizeof(Command*) * compteur);
  525. ct->length = compteur;
  526. ct->bck = line[strlen(line) - 1] == '&';
  527. //Si il y a un un & on le retire (on à deja l'information)
  528. if(ct->bck){
  529. line[strlen(line) - 1] = '\0';
  530. }
  531. //Recupération de chaque commande
  532. for(int i = 0; i < compteur; i++){
  533. ct->cmd[i] = malloc(sizeof(Command));
  534. tmp = get_command(ct->cmd[i], line);
  535. line += tmp;
  536. //Si pas dernière commande on trim
  537. if(i + 1 < compteur){
  538. line = ltrim(line, ' ');
  539. }
  540. }
  541. //Retour
  542. return SHELL_OK;
  543. }
  544. int parse_command(Command* c){
  545. //Declaration variable
  546. int length, nbWildcard = 0, res;
  547. char* cmd, **wildcardTab;
  548. //Parse les redirections
  549. length = set_redirection(c);
  550. if(length == SHELL_ERR || length == 0){
  551. return SHELL_ERR;
  552. }
  553. //Recup la commande (sans redirection)
  554. cmd = malloc(sizeof(char) * (length + 1));
  555. memset(cmd, 0, length + 1);
  556. strncpy(cmd, c->cmd, length);
  557. //Split la commande
  558. split_command(c, cmd);
  559. //Analyse wildcard
  560. for(int i = 0; i < c->argc; i++){
  561. //Regarde si il faut remplacer l'argument par une suite de fichier
  562. nbWildcard = wildcard_result(c->argv[i]);
  563. if(nbWildcard > 0){
  564. //Si il y a des resultats on prepare un tableau pour les récupérer
  565. wildcardTab = malloc(sizeof(char*) * nbWildcard);
  566. nbWildcard = wildcard(c->argv[i], nbWildcard, wildcardTab);
  567. //Verif retour
  568. if(nbWildcard == ERR){
  569. serrno = SEWC;
  570. return SHELL_ERR;
  571. }
  572. //Ajoute les wildcard dans argv (le +1 est la pour garder le NULL à la fin)
  573. c->argv = insert_array(i, c->argv, c->argc + 1, wildcardTab, nbWildcard, &res);
  574. if(res == ERR){
  575. serrno = SEADDWC;
  576. return SHELL_ERR;
  577. }
  578. c->argc = res - 1; //On ne compte pas le NULL final
  579. }
  580. }
  581. //Ajout nom commande
  582. c->name = c->argv[0];
  583. //Ici tous est ok
  584. return SHELL_OK;
  585. }
  586. int parse_all_command(CommandTab* ct){
  587. int tmp;
  588. for(int i = 0; i < ct->length; i++){
  589. tmp = parse_command(ct->cmd[i]);
  590. if(tmp != SHELL_OK){
  591. return SHELL_FAIL;
  592. }
  593. }
  594. return SHELL_OK;
  595. }
  596. void clean_command(CommandTab* ct){
  597. extern int errno;
  598. //Vide le tableau
  599. for(int i = 0; i < ct->length; i++){
  600. //Si la commande a été parsée on vide les arguments
  601. if(ct->cmd[i]->argc > 0){
  602. ct->cmd[i]->name = NULL;
  603. for(int j = 0; j < ct->cmd[i]->argc; j++){
  604. free(ct->cmd[i]->argv[j]);
  605. }
  606. }
  607. //Ferme les fichiers ouverts si besoin
  608. if(ct->cmd[i]->input != STDIN){
  609. if(close(ct->cmd[i]->input)){
  610. fprintf(stderr, "Erreur lors de la fermeture du fichier d'input de %s : %s\n", ct->cmd[i]->cmd, strerror(errno));
  611. }
  612. }
  613. if(ct->cmd[i]->output != STDOUT){
  614. if(close(ct->cmd[i]->output)){
  615. fprintf(stderr, "Erreur lors de la fermeture du fichier d'output de %s : %s\n", ct->cmd[i]->cmd, strerror(errno));
  616. }
  617. }
  618. 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 >>&)
  619. if(close(ct->cmd[i]->error)){
  620. fprintf(stderr, "Erreur lors de la fermeture du fichier d'error de %s : %s\n", ct->cmd[i]->cmd, strerror(errno));
  621. }
  622. }
  623. //Supprime la ligne de commande
  624. free(ct->cmd[i]->cmd);
  625. //Supprime la structure
  626. free(ct->cmd[i]);
  627. }
  628. //Met à 0 la taille du tableau
  629. ct->length = 0;
  630. free(ct->line);
  631. }