parser.c 19 KB

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