title: Shell implementation with pipes and I\O redirection in c
body: <p>I'm having a lot of troubles with an assignment for school. I'm supposed to add pipes and I/O redirection to a shell implementation in c. I already got it to work with I/O redirections and the pipes work by themselves but I need to support something like this "sort &lt; file.txt | grep main | cat > output". I have no idea how to make it work with both at the same time. Any help would be gratly appreciated. </p>
  
  <pre><code>int get_args(char* cmdline, char* args[]){
    int i = 0;
  
    /* if no args */
    if((args[0] = strtok(cmdline, "\n\t ")) == NULL)
      return 0;
    while((args[++i] = strtok(NULL, "\n\t ")) != NULL) {
    if(i &gt;= MAX_ARGS) {
       printf("Too many arguments!\n");
       exit(1);
    }
   }
   /* the last one is always NULL */
   return i;
   }
  
  void pipehandler(char* args[], int nargs){
  
     int num_cmds = 0;
     char *command[256];
     int filedes[2]; // pos. 0 output, pos. 1 input of the pipe
     int filedes2[2];
     int i = 0;
     //calculate the number of commands
     for(int i = 0; i &lt; nargs; i++){
         if (strcmp(args[i], "|") == 0){
            num_cmds++;
         }
     }
     num_cmds++;
     if(num_cmds &lt;= 1){
        return;
     }
     int j = 0;
     int end = 0;
     pid_t pid;
     while(args[j] != NULL &amp;&amp; end != 1){
         int k = 0;
  
         while (strcmp(args[j],"|") != 0){
             command[k] = args[j];
             j++;
             if (args[j] == NULL){
                 // 'end' variable used to keep the program from entering
                 // again in the loop when no more arguments are found
                 end = 1;
                 k++;
                 break;
             }
             k++;
         }
         // Last position of the command will be NULL to indicate that
        // it is its end when we pass it to the exec function
        command[k] = NULL;
         j++;
  
      // Depending on whether we are in an iteration or another, we
      // will set different descriptors for the pipes inputs and
      // output. This way, a pipe will be shared between each two
      // iterations, enabling us to connect the inputs and outputs of
      // the two different commands.
      if (i % 2 != 0){
          pipe(filedes); // for odd i
      }else{
          pipe(filedes2); // for even i
      }
      pid=fork();
  
      if(pid==-1){
          if (i != num_cmds - 1){
              if (i % 2 != 0){
                  close(filedes[1]); // for odd i
              }else{
                  close(filedes2[1]); // for even i
              }
          }
          printf("Child process could not be created\n");
          return;
      }
      if(pid==0){
          // If we are in the first command
          if (i == 0){
              dup2(filedes2[1], STDOUT_FILENO);
          }
          // If we are in the last command, depending on whether it
          // is placed in an odd or even position, we will replace
          // the standard input for one pipe or another. The standard
          // output will be untouched because we want to see the
          // output in the terminal
          else if (i == num_cmds - 1){
              if (num_cmds % 2 != 0){ // for odd number of commands
                  dup2(filedes[0],STDIN_FILENO);
              }else{ // for even number of commands
                  dup2(filedes2[0],STDIN_FILENO);
              }
          // If we are in a command that is in the middle, we will
          // have to use two pipes, one for input and another for
          // output. The position is also important in order to choose
          // which file descriptor corresponds to each input/output
          }else{ // for odd i
              if (i % 2 != 0){
                  dup2(filedes2[0],STDIN_FILENO);
                  dup2(filedes[1],STDOUT_FILENO);
              }else{ // for even i
                  dup2(filedes[0],STDIN_FILENO);
                  dup2(filedes2[1],STDOUT_FILENO);
              }
          }
          execvp(command[0],command);
      }
  
      // CLOSING DESCRIPTORS ON PARENT
      if (i == 0){
          close(filedes2[1]);
      }
      else if (i == num_cmds - 1){
          if (num_cmds % 2 != 0){
              close(filedes[0]);
          }else{
              close(filedes2[0]);
          }
      }else{
          if (i % 2 != 0){
              close(filedes2[0]);
              close(filedes[1]);
          }else{
              close(filedes[0]);
              close(filedes2[1]);
          }
      }
  
      waitpid(pid,NULL,0);
  
      i++;
    }
  
  
  
   }
  
   int searchIO(char* args[], int *nargs, char** input_file, char** output_file, int* in, int* out, int* ap){
   int ioRedirection = -1;
   //search through the array args
   printf("searching for io redirections\n");
   for(int i = 0; i &lt; *nargs; i++){
      if(strcmp(args[i], "&lt;") == 0){
          //if you find &lt; then you need to redirect the stdin to be from a file
          //if you are here then the input file is in args[i+1]
          *input_file = args[i+1];
          printf("this is the input file %s \n", *input_file);
          //we need to remove the &lt; and input_file from args[]
          int j;
          for(j = i; j &lt; *nargs; j++){
              //move what is in args[j+2] to args[j];
              args[j] = args[j+2];
          }
          //then we need to execute the comand
          *nargs = *nargs-2;
          *in = 1;
          i = 0;
          ioRedirection = 1;
      }else if (strcmp(args[i], "&gt;") == 0){
          //if you are here then the output file is in args[i+1]
          //so aparently execute what is in args[0] to args[i]
          *output_file = args[i+1];
          //we need to remove the &gt; and output_file from args[]
          int j;
          for(j = i; j &lt; *nargs; j++){
              //move what is in args[j+2] to args[j];
              if(j &lt; *nargs){
                  args[j] = args[j+2];
              }
          }
          *nargs = *nargs-2;
          *out = 1;
          i = 0;
          ioRedirection = 1;
      }else if (strcmp(args[i], "&gt;&gt;") == 0){
          //if you are here then the output file is in args[i+1]
          //so aparently execute what is in args[0] to args[i]
          *output_file = args[i+1];
          //we need to remove the &gt; and output_file from args[]
          int j;
          for(j = i; j &lt; *nargs; j++){
              //move what is in args[j+2] to args[j];
              args[j] = args[j+2];
          }
          *nargs = *nargs-2;
          *out = 1;
          *ap = 1;
          i = 0;
          ioRedirection = 1;
      }
  
    }
    return -1;
  
   }
  
   void execute(char* cmdline){
   int pid, async;
   char* args[MAX_ARGS];
   char *input_file;
   char *output_file;
   int _in, _out, _ap;
   _in = 0;
   _out = 0;
   _ap = 0;
   int nargs = get_args(cmdline, args);
   pipehandler(args, nargs);
   searchIO(args, &amp;nargs, &amp;input_file, &amp;output_file, &amp;_in, &amp;_out, &amp;_ap);
   if(nargs &lt;= 0) return;
   if(!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) {
          exit(0);
   }
   if(!strcmp(args[nargs-1], "&amp;")) { async = 1; args[--nargs] = 0; }
   else async = 0;
  
   pid = fork();
   if(pid == 0) { /* child process */
     if(_in == 1){
        int input = open(input_file, O_RDONLY);
        dup2(input, STDIN_FILENO);
        close(input);
    }
    if(_out == 1){
        int output;
        if(_ap){
            output = open(output_file, O_APPEND | O_CREAT);
        }else{
            output = open(output_file, O_CREAT | O_WRONLY, 0666);
        }
  
        dup2(output, STDOUT_FILENO);
        close(output);
    }
   execvp(args[0], args);
   /* return only when exec fails */
    perror("exec failed");
    exit(-1);
    } else if(pid &gt; 0) { /* parent process */
   if(!async) waitpid(pid, NULL, 0);
    else printf("this is an async call\n");
   } else { /* error occurred */
   perror("fork failed");
   exit(1);
   }
   }
   int main (int argc, char* argv [])
   {
   char cmdline[BUFSIZ];
  
    for(;;) {
    printf("$ ");
    if(fgets(cmdline, BUFSIZ, stdin) == NULL) {
      perror("fgets failed");
      exit(1);
    }
    execute(cmdline) ;
   }
   return 0;
   }
  </code></pre>