parser.c 18 KB

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