Programmation Avancée en C


microshell.c

00001 #include <sys/types.h>
00002 #include <sys/wait.h>
00003 #include <errno.h>
00004 #include <fcntl.h>
00005 #include <stdio.h>
00006 #include <stdlib.h>
00007 #include <string.h>
00008 #include <unistd.h>
00009 
00010 #define LINESIZE 200
00011 #define MAXARGNUM 15
00012 
00013 static inline int carspecial(char c) {
00014         if (c == ' ' || c == ';' || c == '&' || c == 0 || c == '\n' || c == '>')
00015                 return 1;
00016         return 0;
00017 }
00018 
00019 static int separateurpresent(char *ligne, int *mustwait, int *fdout) {
00020         switch (ligne[0]) {
00021         case '&':
00022                 *mustwait = 0;
00023                 /* passe au travers */
00024         case ';':
00025                 ligne[0] = '\0';
00026                 return 1;
00027         case '>':
00028                 ligne[0] = '\0';
00029                 int i = 1;
00030                 while (ligne[i] == ' ') {i++;}
00031                 char* fichier = ligne + i;
00032                 while (!carspecial(ligne[i])) {i++;}
00033                 if (ligne[i] != 0)  ligne[i++] = 0;
00034                 *fdout = open(fichier, O_WRONLY | O_CREAT | O_TRUNC, 0644);
00035                 if (*fdout < 0) fprintf(stderr, "%s\n", strerror(errno));
00036                 return i;
00037         }
00038         return 0;
00039 }
00040 
00041 static int comprendrecommande(char **argv, char *ligne, int offset, int *mustwait,
00042                               int *fdout)
00043 {
00044         int j = 1;
00045         int i = offset;
00046         while (ligne[i] == ' ' || ligne[i] == ';' || ligne[i] == '&') i++;
00047         if (ligne[i] == 0) return -1;
00048         argv[0] = ligne + i;
00049         while (ligne[i] && ligne[i] != '\n'
00050                && ligne[i] != ';' && ligne[i] != '&' && ligne[i] != '>') {
00051                 if (ligne[i] == ' ') {
00052                         ligne[i] = '\0';
00053                         if (!carspecial(ligne[i+1])) {
00054                                 argv[j++] = ligne + i + 1;
00055                                 if (j == MAXARGNUM) return -10;
00056                         }
00057                 }
00058                 i++;
00059         }
00060         argv[j] = NULL;
00061         int ret = separateurpresent(ligne + i, mustwait, fdout);
00062         if (ret) return i + ret;
00063         return 0;
00064 }
00065 
00066 static int executer(char* ligne) {
00067         char *argv[MAXARGNUM + 1];
00068         int offset = 0;
00069         do {
00070                 int ret;
00071                 int mustwait = 1;
00072                 int fdout = -2;
00073                 offset = comprendrecommande(argv, ligne, offset,
00074                                             &mustwait, &fdout);
00075                 if (offset == -1)   break;
00076                 if (offset == -2)   continue;
00077                 if (offset < -2) {
00078                         fprintf(stderr, "Erreur de compréhension"
00079                                 " (trop d'arguments?) !");
00080                         return -1;
00081                 }
00082 
00083                 ret = fork();
00084                 if (ret < 0) {
00085                         fprintf(stderr, "Impossible de forker !");
00086                         exit(EXIT_FAILURE);
00087                 }
00088                 if (ret == 0) {
00089                         if (fdout > 0) 
00090                                 dup2(fdout, STDOUT_FILENO);
00091                         close(fdout);
00092                         execvp(argv[0],argv);
00093                         fprintf(stderr, "%s\n", strerror(errno));
00094                         exit(EXIT_FAILURE);
00095                 }
00096                 if (fdout > 0) 
00097                         close(fdout);
00098                 if (mustwait)
00099                         waitpid(ret, NULL, 0);
00100                 else
00101                         mustwait = 1;
00102                 while (waitpid(-1, NULL, WNOHANG) > 0);
00103         } while (offset > 0);
00104         return 0;
00105 }
00106 
00107 static int lire(char* ligne) {
00108         int ret = read(STDIN_FILENO, ligne, LINESIZE);
00109         switch (ret) {
00110         case 0: /* entrée fermée */
00111                 exit(EXIT_SUCCESS);
00112         case LINESIZE: /* ligne longue */
00113                 fprintf(stderr, "ligne trop longue: ignorée\n");
00114                 while (read(STDIN_FILENO, ligne, LINESIZE) >= LINESIZE);
00115                 return -1;
00116         default: /* cas normal */
00117                 ligne[ret - 1] = 0;
00118                 break;
00119         }
00120         return 0;
00121 }
00122 
00123 static void afficheprompt(void) {
00124         printf(":");
00125         fflush(stdout);
00126 }
00127 
00128 int main()
00129 {
00130         while(1) {
00131                 char ligne[LINESIZE];
00132                 afficheprompt();
00133                 if (lire(ligne))        continue;
00134                 if (executer(ligne))    continue;
00135         }
00136         return EXIT_FAILURE; /* non atteint */
00137 }