I spent a bit of time to write a one-off that both performs the
requisite task correctly, /and/ embodies the stylistic points that
I made in the prior post. This is what I came up with...
/******************* Code begins **********************************/
/*
** This program demonstrates a solution to a problem posted by
** Meredith Montgomery in the comp.unix.programmer usenet group.
**
** The programmer wants a program that creates a pipe between
** itself and the stdin of a child process. The child process
** runs less(1) on it's stdin (the read end of the pipe) while
** the parent process generates print data to the write end of
** the pipe.
**
** The development of this program followed "Occam's razor",
** in that it subscribed to the principle that "entities
** should not be multiplied beyond necessity". In this case,
** I endeavoured to avoid needless encapsulation and abstraction
** of logic, in order to create a simple working version of the
** solution. Embellishments, such as logic encapsulation and
** isolation, are left to others to implement.
**
** == Evolution ==
** 0: basic framework
** 1: create pipe
** 2: fork
** 3: child process reads input from pipe
** 4: parent process output to pipe
** 5: document finer points in comments
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int status = EXIT_FAILURE;
int pfd[2];
/*
** Step 1, make our pipe
*/
if (pipe(pfd) == 0) /* On error, pipe(2) will return -1 and set errno */
{
/*
** Step 2: we got a valid pipe pair, now launch child and perform all processing
*/
switch (fork())
{
/*
** On error, fork(2) will return -1, and set errno appropriately
*/
case -1: /* fork failed */
fprintf(stderr,"%s: fork() failed - %s\n",argv[0],strerror(errno));
break;
/*
** In the child process, fork(2) returns 0
*/
case 0: /* CHILD process */
/*
** Step 3: prepare the child process to read the pipe through stdin
** and run "less" on the input
*/
close(pfd[1]); /* don't hold pipe write end open */
if (dup2(pfd[0],STDIN_FILENO) != -1)
{
close(pfd[0]); /* close the now useless fd */
execl("/usr/bin/less","less",NULL); /* use "less" to process stdin */
/* we only get here if execl() failed */
fprintf(stderr,"%s: execl() failed - %s\n",argv[0],strerror(errno));
}
else fprintf(stderr,"%s: child dup2() failed - %s\n",argv[0],strerror(errno));
break;
/*
** In the parent process, fork(2) returns the non-zero, non -1 PID
** of the child process
*/
default: /* PARENT process */
/*
** Step 4: prepare the parent process to write the pipe through stdout
** write some data so that the child can process it, wait for
** the child to process the data and then terminate the parent.
*/
close(pfd[0]); /* dont hold pipe read end open */
if (dup2(pfd[1],STDOUT_FILENO) != -1) /* use the write end of pipe as stdout */
{
close(pfd[1]); /* close the now useless fd */
/* generate some output to be piped to the child */
for (int count = 0; count < 100; ++count)
printf("This is generated line number %d\n",count);
fclose(stdout); /* signals EOF to child process stdin */
/*
** The parent needs to wait because otherwise, the process running
** less(1) becomes orphaned and will be killed (via SIGTTOU) when
** it tries to access the terminal.
** (Rainer Weikusat <
rwei...@talktalk.net>)
*/
wait(NULL); /* wait for the child to finish */
status = EXIT_SUCCESS; /* Mission Accomplished! */
}
else fprintf(stderr,"%s: parent dup2() failed - %s\n",argv[0],strerror(errno));
break;
}
}
else fprintf(stderr,"%s: pipe() failed - %s\n",argv[0],strerror(errno));
return status;
}
/******************* Code ends **********************************/