Question: Rewrite the program so that it can do pipes ('|'). Note that the program must be able to parse the input line, identify the pipe
Rewrite the program so that it can do pipes ('|'). Note that the program must be able to parse the input line, identify the pipe symbols ('|'), and then launch each command as a process. Also the processes must be connected together with pipes. These pipes become replacements for stdin and stdout. pipe.c is an example of implementing this using dup2.
have the shell be able to do up to 9 pipes. Note that the in[] and out[] arrays may have to organized into structs since you may need 9 of them. Your shell should not create zombie processes if it properly exits.
* This is a simple shell program from * rik0.altervista.org/snippetss/csimpleshell.html * It's been modified a bit and comments were added. * * It doesn't allow misdirection, e.g., <, >, >>, or | */ #include #include #include #include #include #include #define BUFFER_SIZE 80 #define ARR_SIZE 80
// #define DEBUG 1 /* In case you want debug messages */
void parse_args(char *buffer, char** args, size_t args_size, size_t *nargs) { /* * size_t data type is defined in the 1999 ISO C standard (C99). * It is used to represent the sizes of objects. size_t is the * preferred way to declare arguments or variables that hold the * size of an object. */ char *buf_args[args_size]; /* You need C99. Note that args_size is normally a constant. */ char **cp; /* This is used as a pointer into the string array */ char *wbuf; /* String variable that has the command line */ size_t i, j; wbuf=buffer; buf_args[0]=buffer; args[0] =buffer; /* * Now 'wbuf' is parsed into the string array 'buf_args' * * The for-loop uses a string.h function * char *strsep(char **stringp, const char *delim); * * Description: * If *stringp = NULL then it returns NULL and does * nothing else. Otherwise the function finds the first token in * the string *stringp, where tokens are delimited by symbols * in the string 'delim'. * * In the example below, **stringp is &wbu, and * the delim = ' ', ' ', and '\t'. So there are three possible * delimiters. * * So in the string " Aloha World ", the spaces and " " are * delimiters. Thus, there are three delimiters. The tokens * are what's between the delimiters. So the first token is * "", which is nothing because a space is the first delimiter. * The second token is "Aloha", and the third token is "World". * * The function will scan a character string starting from * *stringp, search for the first delimiter. It replaces * the delimiter with '\0', and *stringp is updated to point * past the token. In case no delimiter was found, the * token is taken to be the entire string *stringp, and *stringp * is made NULL. Strsep returns a pointer to the token. * * Example: Suppose *stringp -> " Aloha World " * * The first time strsep is called, the string is "\0Aloha World ", * and the pointer value returned = 0. Note the token is nothing. * * The second time it is called, the string is "\0Aloha\0World ", * and the pointer value returned = 1 Note that 'Aloha' is a token. * * The third time it is called, the string is '\0Aloha\0World\0', * and the pointer value returned is 7. Note that 'World' is a token. * * The fourth time it is called, it returns NULL. * * The for-loop, goes through buffer starting at the beginning. * wbuf is updated to point to the next token, and cp is * updated to point to the current token, which terminated by '\0'. * Note that pointers to tokens are stored in array buf_args through cp. * The loop stops if there are no more tokens or exceeded the * array buf_args. */ /* cp is a pointer to buff_args */ for(cp=buf_args; (*cp=strsep(&wbuf, " \t")) != NULL ;){ if ((*cp != '\0') && (++cp >= &buf_args[args_size])) break; }
/* * Copy 'buf_args' into 'args' */ for (j=i=0; buf_args[i]!=NULL; i++){ if(strlen(buf_args[i])>0) /* Store only non-empty tokens */ args[j++]=buf_args[i]; } *nargs=j; args[j]=NULL; }
int main(int argc, char *argv[], char *envp[]){ char buffer[BUFFER_SIZE]; char *args[ARR_SIZE];
int *ret_status; size_t nargs; pid_t pid; while(1){ printf("ee468>> "); /* Prompt */ fgets(buffer, BUFFER_SIZE, stdin); /* Read in command line */ /* Parse the command line into args */ parse_args(buffer, args, ARR_SIZE, &nargs); if (nargs==0) continue; /* Nothing entered so prompt again */ if (!strcmp(args[0], "exit" )) exit(0);
pid = fork();
if (pid){ /* The parent */ #ifdef DEBUG printf("Waiting for child (%d) ", pid); #endif pid = wait(ret_status); #ifdef DEBUG printf("Child (%d) finished ", pid); #endif }
else{ /* The child executing the command */ if( execvp(args[0], args)) { puts(strerror(errno)); exit(127); }
} } return 0; }
/* If hexdump wasn't executed then we would still have the following * function, which would indicate an error */ error("Could not exec hexdump"); }
/* The following is in the parent process */ printf("Spawned 'hexdump -C' as a child process at pid %d ", pid); /* This is the parent process */ /* Close the pipe ends that the child uses to read from / write to so * the when we close the others, an EOF will be transmitted properly. */ close(in[0]); close(out[1]);
/* The following displays on the console what's in the array 'data' * The array was initialized at the top of this program with * the string 'Some input data' */ // printf("<- %s", data); Galen replaced this line with the following printf("String sent to child: %s ", data);
/* From the parent, write some data to the childs input. * The child should be 'hexdump', which will process this * data. */ write(in[1], data, strlen(data)); /* Because of the small amount of data, the child may block unless we * close its input stream. This sends an EOF to the child on it's * stdin. */ close(in[1]); /* Read back any output */ n = read(out[0], buf, 250); buf[n] = 0; // printf("-> %s",buf); Galen replaced this line with the following printf("This was received from the parent: %s",buf);
exit(0); } void error(char *s) { perror(s); exit(1); }
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
