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

int main(int argc, char *argv[]) {
  int ***p, pid, i, j;

  if (argc < 2) {
    fprintf(stderr, "FATAL: Missing command-line arguments.\n");
    exit(EXIT_FAILURE);
  }
  
  p = malloc(sizeof(int **) * argc);

  /* cycle through command-line arguments here */
  for (i = 1; i <argc; i++) {
    /* allocate space for pipes */
    p[i] = malloc(sizeof(int *) * 3);

    /* set up pipes */
    for (j = 0; j < 3; j++) {
      p[i][j] = malloc(sizeof(int) * 2);
      if (pipe(p[i][j])) {
        fprintf(stderr, "FATAL: pipe() error: %s.\n", strerror(errno));
        exit(EXIT_FAILURE);
      }
    }

    /* launch this argument's process */
    if ((pid = fork()) < 0) {
      fprintf(stderr, "FATAL: fork() error: %s.\n", strerror(errno));
      exit(EXIT_FAILURE);
    } else if (pid > 0) { /* parent */
      close(p[i][0][0]);
      close(p[i][1][1]);
      close(p[i][2][1]);

      /* reap child process (to avoid zombies) */
      waitpid(pid, NULL, WNOHANG);
    } else {    /* child */
      /* close pipe ends */
      close(p[i][0][1]);
      close(p[i][1][0]);
      close(p[i][2][0]);

      /* map pipes to stdio for child */
      dup2(p[i][0][0], 0);
      dup2(p[i][1][1], 1);
      dup2(p[i][2][1], 2);
      
      /* exec process */
      execlp(argv[i], argv[i], NULL);

      /* shouldn't ever get here */
      fprintf(stderr, "FATAL: exec() error: %s.\n", strerror(errno));
      exit(EXIT_FAILURE);
    }
  }
  
  /* cycle through the list of child processes and handle the stdout and
   * stderr of each one */
  for (i = 1; i < argc; i++) {
    FILE *f = NULL;
    char buf[BUFSIZ];

    /* read child process's stdout */
    if ((f = fdopen(p[i][1][0], "r")) != NULL) {
      printf("-- bgn process % 2d (\"%s\") stdout --\n", i, argv[i]);
      while (fgets(buf, sizeof(buf), f) && !feof(f))
        fputs(buf, stdout);
      fclose(f);
      printf("-- end process % 2d (\"%s\") stdout --\n", i, argv[i]);
    }

    /* read child process's stderr */
    if ((f = fdopen(p[i][2][0], "r")) != NULL) {
      printf("-- bgn process % 2d (\"%s\") stderr --\n", i, argv[i]);
      while (fgets(buf, sizeof(buf), f) && !feof(f))
        fputs(buf, stdout);
      printf("-- end process % 2d (\"%s\") stderr --\n", i, argv[i]);

      fclose(f);
    }
  }
  
  return EXIT_SUCCESS;
}

