/* Compile with: g++ -Wall –Werror -o shell shell.c */ #include #include #include #include #include #include #include /* The array below will hold the arguments: args[0] is the command. */ static char* args[512]; pid_t pid; int command_pipe[2]; #define READ 0 #define WRITE 1 /* * Handle commands separatly * input: return value from previous command (useful for pipe file descriptor) * first: 1 if first command in pipe-sequence (no input from previous pipe) * last: 1 if last command in pipe-sequence (no input from previous pipe) * * EXAMPLE: If you type "ls | grep shell | wc" in your shell: * fd1 = command(0, 1, 0), with args[0] = "ls" * fd2 = command(fd1, 0, 0), with args[0] = "grep" and args[1] = "shell" * fd3 = command(fd2, 0, 1), with args[0] = "wc" * * So if 'command' returns a file descriptor, the next 'command' has this * descriptor as its 'input'. */ static int command(int input, int first, int last) { int pipettes[2]; /* Invoke pipe */ pipe( pipettes ); pid = fork(); /* SCHEME: STDIN --> O --> O --> O --> STDOUT */ if (pid == 0) { if (first == 1 && last == 0 && input == 0) { // First command dup2( pipettes[WRITE], STDOUT_FILENO ); } else if (first == 0 && last == 0 && input != 0) { // Middle command dup2(input, STDIN_FILENO); dup2(pipettes[WRITE], STDOUT_FILENO); } else { // Last command dup2( input, STDIN_FILENO ); } if (execvp( args[0], args) == -1) _exit(EXIT_FAILURE); // If child fails } if (input != 0) close(input); // Nothing more needs to be written close(pipettes[WRITE]); // If it's the last command, nothing more needs to be read if (last == 1) close(pipettes[READ]); return pipettes[READ]; } /* Final cleanup, 'wait' for processes to terminate. * n : Number of times 'command' was invoked. */ static void cleanup(int n) { int i; for (i = 0; i < n; ++i) wait(NULL); } static int run(char* cmd, int input, int first, int last); static char line[1024]; static int n = 0; /* number of calls to 'command' */ int main() { printf("SIMPLE SHELL: Type 'exit' or send EOF to exit.\n"); while (1) { /* Print the command prompt */ printf("$> "); fflush(NULL); /* Read a command line */ if (!fgets(line, 1024, stdin)) return 0; int input = 0; int first = 1; char* cmd = line; char* next = strchr(cmd, '|'); /* Find first '|' */ while (next != NULL) { /* 'next' points to '|' */ *next = '\0'; input = run(cmd, input, first, 0); cmd = next + 1; next = strchr(cmd, '|'); /* Find next '|' */ first = 0; } input = run(cmd, input, first, 1); cleanup(n); n = 0; } return 0; } static void split(char* cmd); static int run(char* cmd, int input, int first, int last) { split(cmd); if (args[0] != NULL) { if (strcmp(args[0], "exit") == 0) exit(0); n += 1; return command(input, first, last); } return 0; } static char* skipwhite(char* s) { while (isspace(*s)) ++s; return s; } static void split(char* cmd) { cmd = skipwhite(cmd); char* next = strchr(cmd, ' '); int i = 0; while(next != NULL) { next[0] = '\0'; args[i] = cmd; ++i; cmd = skipwhite(next + 1); next = strchr(cmd, ' '); } if (cmd[0] != '\0') { args[i] = cmd; next = strchr(cmd, '\n'); next[0] = '\0'; ++i; } args[i] = NULL; }