                     DOS STREAMS AND REDIRECTIONS


                            Matthew Probert
                           Servile  Software


When a program is executed by DOS, several file streams are opened, 
these are:

        stdin           (console, keyboard)
        stdout          (console, monitor)
        stderr          (console, monitor)
        stdprn          (lpt1, printer)
        stdaux          (com1, serial port 1)

The usual C command for displaying information, printf(), writes to 
the file 'stdout'. The corresponding cprintf() function writes not to 
a file, but directly to the monitor. This is significant when 
considering alternative flows of data.

Usually you may use printf() to display information to your user on 
the computer's monitor. However, the user may choose to redirect the 
file stdout to another file using the DOS redirection command >.

EG:

        progname.exe > myfile.ext

Will redirect stream stdout to write to the file 'myfile.ext' for the 
duration of the execution of program 'progname.exe'. This is fine if 
you want to allow your users to do this, but perhaps you wish to 
display a copyright message which you want shown on the screen. You 
can use cprintf(), or you can use fprintf() and send the output to the 
file 'stderr'. stderr is a file stream to the console (monitor) which 
cannot be redirected by the user of the program under DOS.

So this example:

        printf("\nHello World");
        fprintf(stderr,"\nTry redirecting this!\n");

Will allow the user to redirect the first message, Hello World, but 
not the second.

If like me you write programs which spawn other utilities written by 
other people, such as PKZIP etc, you may have wondered how you can 
stop these programs from writing to the screen. Or perhaps you want to 
capture their output in a file so that your program can analyse the 
result of the spawn.

Try this example:

        system("pkzip -ex test");

This will call the command processor (usually command.com) and execute 
the program pkzip with the parameters -ex and test. The result, 
hopefully pkzip will create an archive called test.zip containing all 
files in the current directory. Pkzip will also write to the screen. 
To disable the screen output you can use:

        system("pkzip -ex test > NUL");

Which will then send all output to file stdout to the file NUL, which 
is nowhere, the output simply is lost. Similarly, if you want to 
capture the output from the spawned program you could redirect it's 
output to your own temporary file such as:

        system("pkzip -ex test > temp.$$$");

This is all well and good untill you come across a utility which 
writes not just to stdout, but to stderr as well. How can you stop a 
spawned program from writing to the display through stderr?

The function freopen() comes to your rescue. This function allows an 
open file stream (such as stdout, stderr or stdin etc) to be assigned 
to a new file. So to redirect all stderr output to NUL, effectively 
stopping it from being displayed, you can use:

        freopen("NUL","w",stderr);

Where NUL is the new file to be opened, w is the file open mode, write 
in this case, and stderr is the existing stream to be redirected.

There are two ways of preventing a program from redirecting your 
program's output. Firstly you can write not to a stream but to the 
device itself, such as with cprintf(). Secondly you can open your own 
file stream to the required device, such as:

        FILE *fp;

        fp = fopen("CON","w");

Which opens a file stream fp to the device CON, the console (monitor). 
Then anything fprintf()ed to stream fp will come out on the console 
and cannot be redirected by DOS. CON is not actually a file name, but 
the name of a device (device driver). There are several devices defined by 
DOS which can be accessed:

        CON     The console
        PRN     The printer
        NUL     No where!
        COM1    The first serial port
        COM2    The second serial port
        LPT1    The parallel port


This sample program shows several ways of writing to the console, and 
you can use this to test if your redirection commands and techniques 
are working.

#include <stdio.h>

main()
{
    FILE *fp;

    fp = fopen("CON","w");
    fprintf(stdout,"This goes to the console via stdout\n");
    fprintf(stderr,"This goes to the console via stderr\n");
    fprintf(fp,"This goes to the console via CON\n");
}

Of course sometimes you may wish to allow your program's user the 
flexibility of sending input and output from where they wish with 
redirections. This type of program is a filter, like the DOS SORT 
program.

If you want input to come from the keyboard, and allow the user to 
send input from a file, simply read the data with:

        fgets(str,len,stdin);

Where str is the target string to store the data, len is the maximum 
number of bytes to read and stdin is the file stream normally 
associated with the keyboard, but which can be redirected under DOS 
with the < command.

Similarly printf() sends output to device stdout, and this can also be 
redirected.

Redirection works like this:

        progname < source > target

If you want to append to the target file use the command >>.

Similarly, the output of one program can be piped into the input of 
another program (rather than be written to a file) with the | command.
For example this will send the output of the DIR command as input to 
the program progname.

        DIR | progname

You can then also redirect the output after filtering with >. So that 
the whole command becomes:

        DIR | progname > filename

An example filter. This program reads data from stdin and outputs it 
on stdout. If the input string contains the word ZIP the output is in 
lower case, otherwise in upper case. Try this on a directory with a 
few .ZIP archives to see it effectively. To use the filter use the 
command;

        DIR | progname

Where progname is the name of the compiled filter.

#include <stdio.h>
#include <string.h>

main()
{
    char buffer[1000];
    char *p;

    do
    {
        p = fgets(buffer,1000,stdin);
        if (p)
        {
            if (strstr(buffer,"ZIP"))
                printf("%s",strlwr(buffer));
            else
                printf("%s",strupr(buffer));
        }
    }
    while(p);
}

