parser.c 16 KB

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