From: scs@adam.mit.edu (Steve Summit)
Newsgroups: comp.lang.c
Subject: Re: freopen() How do I get back stdin and stdout?
Date: 30 Sep 92 06:33:50 GMT
Organization: none, at the moment

In article <1992Sep29.201814.11050@newshost.unh.edu>, Dennis Fitanides
(dwf@kepler.unh.edu) writes:
> 	I have a compression program that uses freopen() to reopen stdin and
> stdout.  I've read numerous documents from many sources (including K&R,
> the man pages and AIX Info) that all state that freopen() always closes
> the original file descriptor.  What I need to do is to "reopen" the 
> freopen()'d file descriptor (stdin in this case).

This is, as it happens, a rather Frequently Asked Question, but
as neither the question nor an answer to it appears in the
already overlong comp.lang.c FAQ list, Dennis is to be excused
for asking it.  As penance for not having added the question to
the FAQ list yet, I'll answer it in public:

It is barely possible to save away e.g. stdout before freopening
some file in its place, such that the original stdout can later
be used, but the methods involve system-specific calls such as
dup(), and/or copying or inspecting the contents of a FILE
struct, which is exceedingly unportable and unreliable.

You can, under some systems, explicitly re-open the controlling
terminal (on Unix it is named /dev/tty), but this isn't
necessarily what you want, since the input or output could have
been redirected from the command line (to something other than
the terminal).

A much better solution is to change all of the program's
references to stdout (equivalently, stdin), including implicit
ones (printf, scanf, putchar, getchar, puts, gets, etc.) to use
instead new global FILE *'s of your own choosing.  In other
words, replace

	printf("Cunningly encrypted text follows:\n");
	while((c = getchar() != EOF)
		putchar(c ^ 0xff);
	fflush(stdout);

with

	FILE *ifd = stdin;
	FILE *ofd = stdout;

	fprintf(ofd, "Cunningly encrypted text follows:\n");
	while((c = getc(ifd) != EOF)
		putc(c ^ 0xff, ofd);
	fflush(ofd);

Now, you can switch the input and output files at will, by
assigning different things to ifd and ofd, while still retaining
the ability to go back to stdin and/or stdout.

It is, to be sure, a bit of work to make all the changes,
although a good editor can usually make fairly quick work of it.
To forestall the possibility of such work, I almost always start
out with lines like

	FILE *ifd = stdin;
	FILE *ofd = stdout;

in all but the most trivial of programs, and then use ifd and ofd
(and getc and putc and fprintf) rather than stdin and stdout
(and getchar and putchar and printf).  That way, if I later find
that I need to play games redirecting input or output, the
changes are simple.

freopen is a bit of a kludge, intended as a magic fix to the
problem of having to redirect I/O in an existing program that is
laced with implicit references to stdin and stdout, or (worse) to
redirect the I/O of some subroutine which you don't have full
control over, either because you don't have the source to it, or
because it was produced by some tool such as yacc.  Magic fixes
are, however, rarely perfect, and freopen is no exception.

Vendors of third-party libraries should never supply routines
that refer implicitly to stdin or stdout; any I/O routines should
always take explicit FILE * arguments, precisely to avoid these
problems.  There are other ways of redirecting yacc's (or, more
precisely, lex's) input than by using freopen (and in fact how to
do so is itself a FAQ), namely by re#defining lex's INPUT macro.

In summary, try to avoid using freopen() at all.

					Steve Summit
					scs@adam.mit.edu