Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expected behaviour of freopen() with regards to buffering (setvbuf())?

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?

like image 276
DevSolar Avatar asked Feb 07 '20 12:02

DevSolar


1 Answers

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 function

Synopsis

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 by filename and associates the stream pointed to by stream with it. The mode argument is used just as in the fopen function.272)

3 If filename is a null pointer, the freopen function attempts to change the mode of the stream to that specified by mode, 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 of stream.


272) The primary use of the freopen function is to change the file associated with a standard text stream (stderr, stdin, or stdout), as those identifiers need not be modifiable lvalues to which the value returned by the fopen 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 with stdin, stdout, and stderr to other files.

Since implementations are not required to support any stream mode changes when the pathname argument is NULL, portable applications cannot rely on the use of freopen() 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 changing stdin and stdout 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 to freopen (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 call freopen (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.

like image 86
Andrew Henle Avatar answered Sep 16 '22 23:09

Andrew Henle