In an attempt to implement freopen(), I've come up to a piece of specification in the standard that doesn't actually specify anything, as far as I can see.
So... freopen()
will close the stream (ignoring errors), clear its error and EOF flag, reset wide orientation, and then reopen the stream with the given mode. That much is clear; this is basically an fclose() / fopen(). Even if it isn't defined that way, it is pretty clear that this is what was intended.
However, I have two questions with regards to the things setvbuf()
could have done to the stream -- setting a user-allocated buffer, and / or changing buffer policy.
Question 1.
1) Is freopen()
expected to revert things to the default state, as if it had actually called fopen()
? Or is it expected to carry over to the new stream whatever the user did set via setvbuf()
on the old? This refers to both buffer memory and buffer policy, but the main issue here is with buffer memory.
The specification for fclose()
specifies that any buffer the user associated with the stream via setvbuf()
is disassociated, i.e. can now be free()
'd by the user.
But freopen()
only specifies that it closes the file associated with the stream, not that it fclose()
es it.
So, after freopen()
, is user-associated buffer memory still associated with the stream?
Question 2.
freopen()
can conceivably be used on a FILE
structure that is not actually associated with an open file at the time of the call (as errors trying to close the file are to be ignored).
That file structure could have been a previously open stream with user-assigned buffer memory and buffer policy. Is freopen()
to honor these settings, i.e. re-associate the buffer memory / policy with the "re"-opened file, or is it to reinit the structure to defaults, assuming the user free()
d the buffer memory after fclose()
ing the file previously?
My take.
Looking at Q2, I don't see a way for the standard library to reliably determine whether a not-currently-open FILE
structure with user-allocated buffer memory still "owns" that buffer memory, or whether the user has already reclaimed that memory. (That memory could conceivably be local, i.e. not part of the memory lists handled by malloc()
/ free()
even if I were willing to go there -- and this would be very much uncharacteristically involved work expected of standard library functions.)
Similar considerations for buffer policy.
So the only reliable way of handling things, as far as I can see, is for freopen()
to handle the closing of "any file that is associated with the specified stream" as a "real" fclose()
, and re-set buffer memory / policyto defaults.
Am I right in this understanding, or is there an alternative answer to Q1 / Q2?
The C standard does not state the the buffering state is modified in any way.
The entire C11 freopen()
specification is (including footnote 272):
7.21.5.4 The
freopen
functionSynopsis
1
#include <stdio.h> FILE *freopen(const char * restrict filename, const char * restrict mode, FILE * restrict stream);
Description
2 The
freopen
function opens the file whose name is the string pointed to byfilename
and associates the stream pointed to by stream with it. Themode
argument is used just as in thefopen
function.272)3 If
filename
is a null pointer, thefreopen
function attempts to change the mode of the stream to that specified bymode
, as if the name of the file currently associated with the stream had been used. It is implementation-defined which changes of mode are permitted (if any), and under what circumstances.4 The
freopen
function first attempts to close any file that is associated with the specified stream. Failure to close the file is ignored. The error and end-of-file indicators for the stream are cleared.Returns
5 The
freopen
function returns a null pointer if the open operation fails. Otherwise,freopen
returns the value ofstream
.
272) The primary use of the
freopen
function is to change the file associated with a standard text stream (stderr
,stdin
, orstdout
), as those identifiers need not be modifiable lvalues to which the value returned by thefopen
function may be assigned.
To me, the key phrase is associates the stream pointed to by stream with it. The preexisting stream pointed to by stream
has a new file associated with it - and that's all. By not specifying any changes to the buffering, that implies to me that the current buffer state is retained, as freopen()
is just associating a new file and mode with the preexisting stream. Only those changes to the FILE *
stream explicitly noted in the standard should be made, by my reading.
Note also in paragraph 4: The freopen
function first attempts to close any file that is associated with the specified stream. Again, the standard refers to the specified stream.
To me, the conclusion seems inescapable: freopen()
does not create a new stream. It just points the preexisting stream at a new file - and that's all it does.
This reading - that the current stream's buffer status is not modified - is supported by current implementations. They do not modify the buffering state of the preexisting stream.
Neither the GLIBC freopen()
implementation nor the OpenSolaris/Illumos implementation (very likely to be the current Solaris implementation) seem to modify the status of the original buffering other than flushing any buffer before closing the file.
The freopen()
function does appear to be poorly specified. POSIX has this to say:
APPLICATION USAGE
The
freopen()
function is typically used to attach the pre-opened streams associated withstdin
,stdout
, andstderr
to other files.Since implementations are not required to support any stream mode changes when the
pathname
argument isNULL
, portable applications cannot rely on the use offreopen()
to change the stream mode, and use of this feature is discouraged. The feature was originally added to the ISO C standard in order to facilitate changingstdin
andstdout
to binary mode. Since a'b'
character in the mode has no effect on POSIX systems, this use of the feature is unnecessary in POSIX applications. However, even though the'b'
is ignored, a successful call tofreopen (NULL, "wb", stdout)
does have an effect. In particular, for regular files it truncates the file and sets the file-position indicator for the stream to the start of the file. It is possible that these side-effects are an unintended consequence of the way the feature is specified in the ISO/IEC 9899:1999 standard, but unless or until the ISO C standard is changed, applications which successfully callfreopen (NULL, "wb", stdout)
will behave in unexpected ways on conforming systems in situations such as:{ appl file1; appl file2; } > file3
which will result in file3 containing only the output from the second invocation of appl.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With