    Redirection at the system level requires the use of emi(file descriptors),
created by the ti(pipe) system call. When two processes want to communicate
using such file descriptors, the following happens:
    itemization(
    it() The process constructs two em(associated file descriptors) using the
tt(pipe) system call. One of the file descriptors is used for writing, the
other file descriptor is used for reading.
    it() Forking takes place (i.e., the system tt(fork) function is called),
duplicating the file descriptors. Now we have four file descriptors as
the child process and the parent process both have their own copies of the two
file descriptors created by tt(pipe).
    it() One process (say, the parent process) uses the file descriptors
for em(reading). It should close its file descriptor intended for em(writing).
    it() The other process (say, the child process) uses the file descriptors
for em(writing). It should therefore close its file descriptor intended for
em(reading).
    it() All information written by the child process to the file
descriptor intended for writing, can now be read by the parent process from
the corresponding file descriptor intended for reading, thus establishing a
communication channel between the child and the parent process.
    )
    Though basically simple, errors may easily creep in. Functions of file
descriptors available to the two processes (child or parent) may easily get
mixed up. To prevent bookkeeping errors, the bookkeeping may be properly set
up once, to be hidden thereafter inside a class like the tt(Pipe) class
developed here. Let's have a look at its characteristics (before using
functions like tt(pipe) and tt(dup) the compiler must have read the
tthi(unistd.h) header file):
    itemization(
    it() The tt(pipe) system call expects a pointer to two tt(int) values,
representing, respectively, the file descriptor used for reading and the file
descriptor used for writing. To avoid confusion, the class tt(Pipe) defines an
tt(enum) having values associating the indices of the array of 2-tt(int)s with
symbolic constants. The two file descriptors themselves are stored in a data
member tt(d_fd). Here is the initial section of the class's interface:
        verbinclude(//HEAD examples/pipe.h)
    it() The class only needs a default constructor. This constructor
calls tt(pipe) to create  a set of associated file descriptors used for
accessing both ends of a pipe:
        verbinclude(//CONS examples/pipe.cc)
    it() The members tt(readOnly) and tt(readFrom) are used to configure the
pipe's reading end. The latter function is used when using redirection. It is
provided with an alternate file descriptor to be used for  reading from the
pipe. Usually this alternate file descriptor is ti(STDIN_FILENO), allowing
tt(cin) to extract information from the pipe.  The former function is merely
used to configure the reading end of the pipe. It closes the matching writing
end and returns a file descriptor that can be used to read from the pipe:
        verbinclude(//READ examples/pipe.cc)
    it() tt(writeOnly) and two tt(writtenBy) members are available to
configure the writing end of a pipe. The former function is only used to
configure the writing end of the pipe. It closes the reading end, and
returns a file descriptor that can be used for writing to the pipe:
        verbinclude(//WRITE examples/pipe.cc)
    For the latter member two overloaded versions are available:
        itemization(
        itt(writtenBy(int fd)) is used to configure em(single)
redirection, so that a specific file descriptor (usually ti(STDOUT_FILENO)
or ti(STDERR_FILENO)) can be used to write to the pipe;
        itt((writtenBy(int const *fd, size_t n))) may be used
to configure em(multiple) redirection, providing an array argument containing
file descriptors. Information written to any of these file descriptors is
actually written to the pipe.
        )
    it() The class has one private data member, tt(redirect), used to set up
redirection through the ti(dup2) system call. This function expects two file
descriptors. The first file descriptor represents a file descriptor that can
be used to access the device's information; the second file descriptor is an
alternate file descriptor that may also be used to access the device's
information. Here is tt(redirect)'s implementation:
        verbinclude(//REDIRECT examples/pipe.cc)
    )
    Now that redirection can be configured easily using one or more tt(Pipe)
objects, we'll use tt(Fork) and tt(Pipe) in various example programs.
