#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void error(char *msg)
{
    fprintf(stderr, "%s\n", msg);
    exit(-1);
}

void wait_for_children()
{
    int pid;
    while ( (pid = wait(0)) > 0 )
    {
        printf("child terminated PID = %d\n", pid);
    }
}

void create_process(char *command, int in, int out)
{
    int pid;
    switch (pid = fork())
    {
        case -1:
            error("fork failed");
        case 0:
            dup2(in, 0);
            dup2(out, 1);
            if (execl(command, command, 0) == -1)
                error("exec failed");
        default:
            /* Note: my program hung when I did not close the pipe in the parent */
            close(in);
            close(out);
            printf("Created child with PID %d\n", pid);
            break;
    }
}

int open_for_reading(char *input)
{
    int fd = open(input, O_RDONLY);
    if (fd == -1)
        error("unable to open input file");
    return fd;
}

int open_for_writing(char *output)
{
    int fd = open(output, O_CREAT|O_WRONLY|O_TRUNC, 0666);
    if (fd == -1)
        error("unable to open input file");
    return fd;
}

void copy(char *input, char *output)
{
    int pipefds[2];
    if (pipe(pipefds) == -1)
        error("can not create pipe");
    create_process("/bin/cat", open_for_reading(input), pipefds[1]);
    create_process("/bin/cat", pipefds[0], open_for_writing(output));
    wait_for_children();
}

int main(int argc, char *argv[])
{
    /* called
        copy existingFile newFile
    */

    /* implements the following job
        cat < existingFile | cat > newFile
    */

    /* details of processes and their I/O
        creates a pipe with two processes, each running cat.
        input of first comes from existingFile
        output goes to the pipe
        input of second comes from pipe
        output of second goes to newFile
    */

    if (argc != 3)
        error("Usage: copy inputFile outputFile");
    copy(argv[1], argv[2]);
    return 0;
}
