parser.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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, * old;
  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. strncpy(c->cmd, deb, length);
  107. c->next = next;
  108. c->argc = 0;
  109. c->input = STDIN;
  110. c->output = STDOUT;
  111. c->error = STDERR;
  112. c->erase[0] = false;
  113. c->erase[1] = false;
  114. c->bck = false;
  115. //Trim et supprime l'ancienne chaine
  116. old = c->cmd;
  117. c->cmd = rtrim(c->cmd, ' ');
  118. free(old);
  119. //Retour
  120. return length + separator;
  121. }
  122. int set_io(Command* c, char* filename, int redir){
  123. //Declaration variable
  124. int file;
  125. //Ouverture du fichier
  126. file = open(filename, O_CREAT | O_RDWR, S_IRWXU);
  127. if(file == ERR){
  128. perror("Erreur lors de l'ouverture du fichier pour la redirection : ");
  129. return SHELL_ERR;
  130. }
  131. //Analyse dans quel descripteur il doit etre mis
  132. switch(redir){
  133. case SHELLR_IN:
  134. //Si un fichier deja ouvert
  135. if(c->input != STDIN){
  136. //On le ferme
  137. if(close(c->input) == -1){
  138. perror("Erreur lors de la fermeture de l'ancien fichier de redirection : ");
  139. }
  140. }
  141. //Set nouveau fichier
  142. c->input = file;
  143. break;
  144. case SHELLR_OUT:
  145. case SHELLRE_OUT:
  146. //Si un fichier deja ouvert
  147. if(c->output != STDOUT){
  148. //On le ferme
  149. if(close(c->output) == -1){
  150. perror("Erreur lors de la fermeture de l'ancien fichier de redirection : ");
  151. }
  152. }
  153. c->output = file;
  154. if(redir == SHELLRE_OUT){
  155. c->erase[STDOUT - 1] = true;
  156. }
  157. break;
  158. case SHELLR_ERR:
  159. case SHELLRE_ERR:
  160. //Si un fichier deja ouvert
  161. if(c->error != STDERR){
  162. //On le ferme
  163. if(close(c->error) == -1){
  164. perror("Erreur lors de la fermeture de l'ancien fichier de redirection : ");
  165. }
  166. }
  167. c->error = file;
  168. if(redir == SHELLRE_ERR){
  169. c->erase[STDERR - 1] = true;
  170. }
  171. break;
  172. case SHELLR_ALL:
  173. case SHELLRE_ALL:
  174. //Si un fichier deja ouvert
  175. if(c->output != STDOUT){
  176. //On le ferme
  177. if(close(c->output) == -1){
  178. perror("Erreur lors de la fermeture de l'ancien fichier de redirection : ");
  179. }
  180. }
  181. if(c->error != STDERR){
  182. //On le ferme
  183. if(close(c->error) == -1){
  184. perror("Erreur lors de la fermeture de l'ancien fichier de redirection : ");
  185. }
  186. }
  187. c->output = file;
  188. c->error = file;
  189. if(redir == SHELLRE_ALL){
  190. c->erase[STDOUT - 1] = true;
  191. c->erase[STDERR - 1] = true;
  192. }
  193. break;
  194. default :
  195. return SHELL_ERR;
  196. }
  197. //Si on arrive ici tous est ok
  198. return SHELL_OK;
  199. }
  200. int set_redirection(Command* c){
  201. boolean first = true, guillemet = false;
  202. char* deb, * file, * buffer = c->cmd + 1;
  203. int redir = -1, compteur, finCmd = 0;
  204. //Parcours chaine
  205. while(*buffer){
  206. //Repere redirection
  207. while(*buffer){
  208. //Entrée
  209. if(*buffer == '<'){
  210. //Si il n'y a rien apres
  211. if(!(*(buffer + 1))){
  212. return SHELL_ERR;
  213. }
  214. buffer++;
  215. redir = SHELLR_IN;
  216. break;
  217. }
  218. //Sortie
  219. else if (*buffer == '>'){
  220. //Si il n'y a rien apres
  221. if(!(*(buffer + 1))){
  222. return SHELL_ERR;
  223. } else {
  224. buffer++;
  225. //Si >>
  226. if(*buffer == '>'){
  227. //Si il n'y a rien apres
  228. if(!(*(buffer + 1))){
  229. return SHELL_ERR;
  230. } else {
  231. buffer++;
  232. //Si >>&
  233. if(*buffer == '&'){
  234. //Si il n'y a rien apres
  235. if(!(*(buffer + 1))){
  236. return SHELL_ERR;
  237. }
  238. buffer++;
  239. redir = SHELLRE_ALL;
  240. }
  241. //Sinon toujours >>
  242. else {
  243. redir = SHELLR_OUT;
  244. }
  245. }
  246. }
  247. // Si >&
  248. else if(*buffer == '&'){
  249. //Si il n'y a rien apres
  250. if(!(*(buffer + 1))){
  251. return SHELL_ERR;
  252. }
  253. redir = SHELLRE_ALL;
  254. buffer++;
  255. }
  256. //Sinon >
  257. else {
  258. redir = SHELLRE_OUT;
  259. }
  260. }
  261. break;
  262. }
  263. //Sortie erreur
  264. else if (*buffer == '2' && *(buffer - 1) == ' ' && *(buffer + 1) && *(buffer + 1) == '>'){
  265. buffer++;
  266. //Si il n'y a rien apres
  267. if(!(*(buffer + 1))){
  268. return SHELL_ERR;
  269. } else {
  270. buffer++;
  271. //Si 2>>
  272. if(*buffer == '>'){
  273. buffer++;
  274. //Si il n'y a rien apres
  275. if(!(*(buffer + 1))){
  276. return SHELL_ERR;
  277. }
  278. redir = SHELLR_ERR;
  279. }
  280. //Sinon 2>
  281. else {
  282. redir = SHELLRE_ERR;
  283. }
  284. }
  285. break;
  286. }
  287. buffer++;
  288. //On incremente pour trouver la fin de la commande
  289. if(first){
  290. finCmd++;
  291. }
  292. }
  293. //Apres ce n'est que des redirection
  294. first = false;
  295. //On passe les espaces
  296. while(*buffer == ' '){
  297. buffer++;
  298. }
  299. //Si on est à la fin de la chaine
  300. if(*buffer == '\0'){
  301. if(redir == -1){
  302. //Aucune redirection
  303. return finCmd + 1;
  304. }
  305. //Sinon on est dans un redirection non terminée
  306. return SHELL_ERR;
  307. }
  308. //Regarde si le nom du fichier est entre ""
  309. guillemet = *buffer == '"';
  310. //Sauvegarde debut nom fichier
  311. deb = buffer;
  312. compteur = 0;
  313. //Lecture nom du fichier
  314. while(*buffer){
  315. //Incremente
  316. compteur++;
  317. buffer++;
  318. //Test arret
  319. if(guillemet && *buffer == '"'){
  320. break;
  321. }
  322. else if(!guillemet && (*buffer == '<' || *buffer == '>' || (*buffer == ' ' && *(buffer - 1) != '\\'))){
  323. break;
  324. }
  325. }
  326. //Si fin de la commande et guillemet alors erreur
  327. if(!(*buffer) && guillemet){
  328. return SHELL_ERR;
  329. }
  330. //Retire guillement si besoin
  331. if(guillemet){
  332. deb++;
  333. compteur--;
  334. buffer++;
  335. }
  336. //Allocation file et copie nom fichier
  337. file = malloc(sizeof(char) * compteur);
  338. strncpy(file, deb, compteur);
  339. //Redirection
  340. if(set_io(c, file, redir) == SHELL_ERR){
  341. free(file);
  342. return SHELL_ERR;
  343. }
  344. free(file);
  345. //Passe les espaces
  346. while(*buffer == ' '){
  347. buffer++;
  348. }
  349. }
  350. //Si on arrive ici tous est ok
  351. return finCmd;
  352. }
  353. /* --- Fonctions publiques --- */
  354. int parse_line(CommandTab* ct, char* line){
  355. //Declaration variable
  356. int compteur, tmp;
  357. //Nettoyage ligne
  358. line = trim(mtrim(line, '\n'));
  359. //Compte le nombre de commande dans la ligne
  360. compteur = nb_commands(line);
  361. if(compteur == SHELL_ERR){
  362. return SHELL_ERR;
  363. }
  364. //Initialisation structure
  365. ct->cmd = malloc(sizeof(Command*) * compteur);
  366. ct->length = compteur;
  367. //Recupération de chaque commande
  368. for(int i = 0; i < compteur; i++){
  369. ct->cmd[i] = malloc(sizeof(Command));
  370. tmp = get_command(ct->cmd[i], line);
  371. line += tmp;
  372. //Si pas dernière commande on trim
  373. if(i + 1 < compteur){
  374. line = ltrim(line, ' ');
  375. }
  376. }
  377. //Retour
  378. return SHELL_OK;
  379. }
  380. int parse_command(Command* c){
  381. //Declaration variable
  382. int length;
  383. char* cmd;
  384. //Parse les redirections
  385. length = set_redirection(c);
  386. if(length == SHELL_ERR){
  387. return SHELL_ERR;
  388. }
  389. //Recup la commande (sans redirection)
  390. cmd = malloc(length);
  391. strncpy(cmd, c->cmd, length);
  392. //Split en un tableau
  393. c->argv = str_split(cmd, ' ', &c->argc); //ToDo changer par un methode de split qui prend en compte les " " et les \espaces
  394. c->name = c->argv[0];
  395. //Analyse wildcard
  396. /* Todo */
  397. //Ici tous est ok
  398. return SHELL_OK;
  399. }
  400. int parse_all_command(CommandTab* ct){
  401. int tmp;
  402. for(int i = 0; i < ct->length; i++){
  403. tmp = parse_command(ct->cmd[i]);
  404. if(tmp != SHELL_OK){
  405. return SHELL_FAIL;
  406. }
  407. }
  408. return SHELL_OK;
  409. }
  410. void clean_command(CommandTab* ct){
  411. extern int errno;
  412. //Vide le tableau
  413. for(int i = 0; i < ct->length; i++){
  414. //Si la commande a été parsée on vide les arguments
  415. if(ct->cmd[i]->argc > 0){
  416. ct->cmd[i]->name = NULL;
  417. for(int j = 0; j < ct->cmd[i]->argc; j++){
  418. free(ct->cmd[i]->argv[j]);
  419. }
  420. }
  421. //Ferme les fichiers ouverts si besoin
  422. if(ct->cmd[i]->input != STDIN){
  423. if(close(ct->cmd[i]->input)){
  424. fprintf(stderr, "Erreur lors de la fermeture du fichier d'input de %s : %s", ct->cmd[i]->cmd, strerror(errno));
  425. }
  426. }
  427. if(ct->cmd[i]->output != STDOUT){
  428. if(close(ct->cmd[i]->output)){
  429. fprintf(stderr, "Erreur lors de la fermeture du fichier d'output de %s : %s", ct->cmd[i]->cmd, strerror(errno));
  430. }
  431. }
  432. if(ct->cmd[i]->error != STDERR){
  433. if(close(ct->cmd[i]->error)){
  434. fprintf(stderr, "Erreur lors de la fermeture du fichier d'error de %s : %s", ct->cmd[i]->cmd, strerror(errno));
  435. }
  436. }
  437. //Supprime la ligne de commande
  438. free(ct->cmd[i]->cmd);
  439. //Supprime la structure
  440. free(ct->cmd[i]);
  441. }
  442. //Met à 0 la taille du tableau
  443. ct->length = 0;
  444. }