For a class assignment I've had to implement part of a simple UNIX shell. It must support redirection, piping, and backgrounding. I was provided with a parser that populates a struct called Command_line (I'll include the struct prototype below). My job is to write a function that processes these Command_lines (handles redirection, backgrounding, piping, and executes programs).

I've almost got it working but for some reason it doesn't properly handle commands of the form program1 | program2 - file. For example, cat < file1.in | cat - file2.in. The problem doesn't seem to be in the redirection as I've written test programs to put in front of the pipe that do not require redirection but still cause the same problem. The pipelining does work in most cases; it's just these programs with "-" as an argument that cause problems.

When I run one of these problematic command lines, the output from the first program is printed and the process hangs up (I have to manually suspend and kill it). It does not give the user a prompt afterwards or react to input (aside from ctrl + z which I use to suspend the process).

Any advice on how to get this working would be much appreciated.

Here's the struct:
Code:
/* This is the structure that holds the information about a parsed
 * command line.  The argvs array is an array of string vectors; in
 * other words, for some int i, argvs[i] is an array of strings.
 * You should be able to use argvs[i] in calls to one of the execv*()
 * functions.
 */
typedef struct {
  char *argvs[MAX_PROGS + 1][MAX_ARGS + 1];
  int num_progs;  /* Number of argument vectors; if > 1, piping is requested */
  char *infile;   /* Name of stdin redirect file; NULL if no redirection */
  char *outfile;  /* Name of stdout redirect file; NULL if no redirection */
  int append;     /* Is output redirection appending? */
  int bg;         /* Put command into background? */
} Command_line;
And my code that processes one of these structs (I've left out the #includes).

Code:
pid_t runproc(int fd[][2], int num, Command_line *cmd);

void execute_command_line(Command_line *cmd) {
  int n;
  int temp_pipe[2];
  int fd[MAX_PROGS-1][2];
  pid_t pids[MAX_PROGS];
    
  /* Clears pipes (sets all values to -1*/
  for(n = 0; n < cmd->num_progs; n++){
    fd[n][0] = -1;
    fd[n][1] = -1;
  }
  
  /*Uses temp_pipe to connect write end of nth pipe to read end of (n+1)th 
    pipe*/
  for(n = 0; n < cmd->num_progs - 1; n++){
    pipe(temp_pipe);
    fd[n][1] = temp_pipe[1];
    fd[n+1][0] = temp_pipe[0];
  }

  /*If input file redirection is occuring, redirects read end of first pipe to
    file*/
  if(cmd->infile){
    fd[0][0] = open(cmd->infile, O_RDONLY);
    if(fd[0][0] < 0){
      printf("Error executing command\n");
      exit(1);
    }
  }

  /*If output file redirection is occurring, redirects write end of last pipe to
    file. Sets append option according to append field of command*/
  if(cmd->outfile){
    if(cmd->append){
      fd[cmd->num_progs - 1][1] = open(cmd->outfile, O_APPEND | O_WRONLY);
      if(fd[cmd->num_progs - 1][1] < 0){
	printf("Error executing command\n");
	exit(1);
      }
    }else{
      fd[cmd->num_progs - 1][1] = open(cmd->outfile, O_WRONLY);
      if(fd[cmd->num_progs - 1][1] < 0){
	printf("Error executing command\n");
	exit(1);
      }
    }
  }

  /*Runs runproc for every program in pipe, stores return values (pids of
    children) in array*/
  for(n = 0; n < cmd->num_progs; n++){
    pids[n] = runproc(fd, n, cmd);
  }

  /*Closes all pipes*/
  for(n = 0; n < cmd->num_progs; n++){
    if(fd[n][0] >= 0) close(fd[n][0]);
    if(fd[n][1] >= 0) close(fd[n][1]);
  }

  /*Waits for all children*/
  for(n = 0; n < cmd->num_progs; n++){
    wait(NULL);
  }
  
}

pid_t runproc(int fd[][2], int num, Command_line *cmd){
  pid_t pid;
  int n;
  int frk_chk;

  pid = fork();
  if(pid < 0){
    printf("Error executing command\n");
    exit(1);
  }else if (!pid){ /*Child code*/
    /*Redirects stdin/stdout of process to read/write end of corresponding
      pipe*/
    printf("one\n");
    if(fd[num][0] >= 0) dup2(fd[num][0], STDIN_FILENO);
    if(fd[num][1] >= 0) dup2(fd[num][1], STDOUT_FILENO);

    /*Closes pipe ends*/
    printf("two\n");
    for(n=0; n < cmd->num_progs - 1; n++){
      if(fd[num][0] >= 0) close(fd[num][0]);
      if(fd[num][1] >= 0) close(fd[num][1]);
    }

    /*If backgrounding: forks, parent exits, child executes program. 
      If not backgrounding: program just executes*/
    printf("three\n");
    if(cmd->bg){
      if((frk_chk = fork()) < 0){
	printf("Error executing command\n");
	exit(1);
      }else if(frk_chk){
	exit(0);
      }else{
	if(!(cmd->infile) && num == 0) close(STDIN_FILENO);
	execvp(cmd->argvs[num][0], cmd->argvs[num]);
      }
    }else{
      printf("four %d\n", num);
      if(!num){
	dup2(fd[0][1], STDOUT_FILENO);
      }
      execvp(cmd->argvs[num][0], cmd->argvs[num]);
    }
    printf("Error executing command\n");
    exit(1);
  }else{ /*Parent code*/
    /*Returns pid of child, used for reaping loop*/
    return pid;
  }
}
I would be eternally grateful to any who could help me with this problem. This project has driven me insane. To be so close to having it working but unable to get it to work is maddening.