parser.c 18 KB

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