3

I have a program that parses the command given, and allocates all the arguments/programs to a struct. In my main program that executes the commands, I am trying to redirect the output of the pipe command to a file if a ">" is given. For example, my program will successfuly execute the command

cat filea | grep pattern

but I want to also be able to execute the command

cat filea | grep pattern > outfile

As a side note, it's not too important to understand the exact mechanics of cmdscan.c as it was given as sort of a helper program to help parse the command string and fill in the struct values which makes it easier to check for cases in the main program hsh.c. Also, the argv1 and argv2 are the left and right hand side of the pipe, so argv2 is only filled up when there is a pipe. And if there is redirection of any sort then the name of the file will be stored in infile/outfile depending on the redirection

This is my main program hsh.c that executes the commands:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <string.h>

#define BUFSIZE 500
struct cmd
  {
    int redirect_in;     /* Any stdin redirection?         */
    int redirect_out;    /* Any stdout redirection?        */
    int redirect_append; /* Append stdout redirection?     */
    int background;      /* Put process in background?     */
    int piping;          /* Pipe prog1 into prog2?         */
    char *infile;        /* Name of stdin redirect file    */
    char *outfile;       /* Name of stdout redirect file   */
    char *argv1[10];     /* First program to execute       */
    char *argv2[10];     /* Second program in pipe         */
  };

int cmdscan(char *cmdbuf, struct cmd *com);

int main() {
    char buf[BUFSIZE];
    struct cmd command;
    pid_t pid;
    int status;
    int fd[2];
    pipe(fd);
    int fdout;

    while((fgets(buf,BUFSIZE,stdin) != NULL)) {

    if(cmdscan(buf,&command)==-1) {
        printf("illegal format\n"); 
        continue; 
    }


    if((pid=fork()) <0) 
        perror("fork error\n"); 

    if(strcmp(command.argv1[0],"exit") == 0) {
            return 0; 
        }
    else if (pid == 0) {
        //if the command has piping
        if(command.piping){
            if((pid = fork()) <0)
                perror("fork error");
            //fork again so we can do more commands after this one
            else if(pid == 0) {
                if((pid = fork()) < 0)
                    perror("fork error");
                else if (pid == 0){
                    //fdout = open(command.outfile, O_CREAT | O_WRONLY);
                    //dup2(fdout, STDOUT_FILENO);
                    dup2(fd[1], STDOUT_FILENO);
                        close(fd[1]);
                        execvp(*command.argv1,command.argv1);


                } else {
                    dup2(fd[0],STDIN_FILENO);
                    close(fd[0]);
                    execvp(*command.argv2,command.argv2);
                }
            }
        //execute normal command
        }else {
            //if normal command has redirection
            if(command.redirect_out){
                fdout = open(command.outfile, O_CREAT | O_WRONLY);
                dup2(fdout,STDOUT_FILENO);
                close(fd[0]);
                execvp(*command.argv1,command.argv1);

            }
            else{
                execvp(*command.argv1,command.argv1);
            }
        }
        //..
        exit(0);
    } else {
        if(wait(&status)!=pid)
            perror("wait error"); 
    }
     }


    return 0;
}

This is the program that parses the command line, cmdscan.c.:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

struct cmd
  {
    int redirect_in;     /* Any stdin redirection?         */
    int redirect_out;    /* Any stdout redirection?        */
    int redirect_append; /* Append stdout redirection?     */
    int background;      /* Put process in background?     */
    int piping;          /* Pipe prog1 into prog2?         */
    char *infile;        /* Name of stdin redirect file    */
    char *outfile;       /* Name of stdout redirect file   */
    char *argv1[10];     /* First program to execute       */
    char *argv2[10];     /* Second program in pipe         */
  };

#define TRUE 1
#define FALSE 0

int 
cmdscan(char *cmdbuf, struct cmd *com)
{
  char *token;
  char *curcmd;        /* pointer to current command string */
  char swtch[256];    /* buffer to hold copy of switch */
  char *separators = " \t\n";
  int i;

  com->redirect_in = FALSE;
  com->redirect_out = FALSE;
  com->redirect_append = FALSE;
  com->background = FALSE;
  com->piping = FALSE;

  if ( (com->argv1[0] = strtok(cmdbuf,separators) ) == NULL)
    return(-1);
  i = 1;
  while( (com->argv1[i++] = (token = strtok(NULL,separators))) != NULL && strcmp(token,">") &&
        strcmp(token,"<") && strcmp(token,">>") && strcmp(token,"&") && strcmp(token,"|") );
  com->argv1[i-1] = NULL;

  if ( token != NULL && strcmp(token,"|") == 0 )
    {
      com->piping = TRUE;
      i = 0;
      while( (com->argv2[i++] = (token = strtok(NULL,separators))) != NULL && strcmp(token,">") &&
        strcmp(token,"<") && strcmp(token,">>") && strcmp(token,"&") && strcmp(token,"|") );
      com->argv2[i-1] = NULL;
      if ( com->argv2[0] == NULL )
        return(-1);
    }

  while ( token != NULL ){

    if ( !strcmp(token,">") || !strcmp(token,">>") )
      {
        if ( com->redirect_out )
          return(-1);
        com->redirect_out = TRUE;
        if ( !strcmp(token,">>") )
          com->redirect_append = TRUE;
        if ( (com->outfile = strtok(NULL,separators)) == NULL )
          return(-1);
      } 
    else if ( !strcmp(token,"<") )
      {
        if ( com->redirect_in )
          return(-1);
        com->redirect_in = TRUE;
        if ( (com->infile = strtok(NULL,separators)) == NULL )
          return(-1);
      } 
    else if ( !strcmp(token,"|") )
      {
        if ( com->piping )
          return(-1);
      } 
    else if ( !strcmp(token,"&") )
      {
        if ( com->background )
          return(-1);
        com->background = TRUE;
        if ( (token = strtok(NULL,separators)) != NULL )
          return(-1);
        break;
      }
    else
      return(-1);

    token = strtok(NULL,separators);
  }

  return(0);
}

I tried applying the same logic as the simple command for redirection but I couldn't get it to work and got sort of confused about the pipes.

3
  • Redirection is actually quite complex, have you accounted for, say, cat filea 2>&1 | grep pattern which redirects stderr to stdout before piping it through grep? Commented Dec 5, 2013 at 4:27
  • At first I tried changing the stdout to fdout similar to the general command in the else statement but that was a no go. Then I read in the handout that the out redirection in case of piping should be handled in program 2 so I tried it there too but was confused because program 2 handles the STDIN from the read end of the pipe. I tried so many different combinations and it wouldn't work. I'm a little confused about that command. I know that cat filea | grep pattern will work. Commented Dec 6, 2013 at 0:01
  • I get cat: 2>&: No such file or directory when I run that command. Commented Dec 6, 2013 at 0:01

1 Answer 1

1

Before we get output redirection to work, there's a basic error to fix: You create one pipe in the main program, and this pipe remains open until the end of the program; every process that reads from the pipe until EOF will not terminate before the end of the main program, so the more pipe command lines you enter during a run, the more waiting processes will hang around (you can observe this with ps). To correct this, create the pipe just before the fork for the pipeline processes; in addition you must close both ends of the pipe after the dup2 (see below).

After this, the output redirection is simple, similar to what you did in case if normal command has redirection, but you violated the rule:

int open(const char *pathname, int flags, mode_t mode);

mode specifies the permissions to use in case a new file is created. This argument must be supplied when O_CREAT is specified in flags;

So the core of your piping code becomes:

                pipe(fd);
                if ((pid = fork()) < 0)
                    perror("fork error");
                else if (pid == 0) {
                    dup2(fd[1], STDOUT_FILENO);
                    close(fd[0]);   // close both fd in child
                    close(fd[1]);
                    execvp(*command.argv1, command.argv1);
                } else {
                    dup2(fd[0], STDIN_FILENO);
                    close(fd[0]);
                    close(fd[1]);   // close both fd in parent
                    if (command.redirect_out)
                    {
                        fdout =
                          open(command.outfile, O_CREAT|O_WRONLY|O_TRUNC, 0666);
                        dup2(fdout, STDOUT_FILENO);
                    }
                    execvp(*command.argv2, command.argv2);
                }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.