/*
  Math 4970
  Bill Slough

  Illustration of system calls needed to create a pipeline

  This program achieves the same effect as the Unix pipeline:

      ls -l | more -c -N

  To accomplish this, a parent process creates a pipe, then directs
  each of two child processes to perform the "ls" and "more" commands.
*/

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

#define READ_END  0
#define WRITE_END 1

int main() {
    /* Establish argument vectors for each of the two commands */
    char *argvForLs[] = {"ls", "-l", NULL};
    char *argvForMore[] = {"more", "-c", "-N", NULL};

    /* Establish a pipe for communication between child processes */
    int fd[2];
    pipe(fd);

    /* Create a child process and let it produce a directory listing */
    pid_t child1PID = fork();
    if (child1PID == 0) {
	/* The child should ensure that output is directed to the "write end" of the pipe */
	close(STDOUT_FILENO);
	dup(fd[WRITE_END]);
	close(fd[READ_END]);
	close(fd[WRITE_END]);

	/* Now the child should execute the "ls" command */
	execvp("ls", argvForLs);
	perror("child 1");
	return 1;
    }

    /* create a second child process, letting it do the work of "more" */
    pid_t child2PID = fork();
    if (child2PID == 0) {
	/* ensure input is taken from the "read end" of the pipe */
	close(STDIN_FILENO);
	dup(fd[READ_END]);
	close(fd[READ_END]);
	close(fd[WRITE_END]);

	/* now execute "more -c -N" */
	execvp("more", argvForMore);
	perror("child 2");
	return 1;
    }

    /* Wait for the child performing "ls" to terminate */
    waitpid(child1PID, NULL, 0);

    /* When "ls" has finished, the parent needs to close the "write end" of the
       pipe so the "more" process will see an end-of-file */
    close(fd[WRITE_END]);

    /* Finally, wait for the "more" process to terminate */
    waitpid(child2PID, NULL, 0);

    return 0;
}
