[comp.unix.questions] Stdio flaws?

lgy@pupthy.UUCP (05/28/87)

    There does not appear to be any clean/portable way to either
(a) flush an input stream, such as stdin, or (b) truncate an existing,
open file.  By portable, I mean sufficiently portable to be used on
non-Unix systems (e.g., Vax VMS or Cyber VSOS) which provide C and
Unix compatible subroutine libraries for stdio level routines,
but not for low-level I/O.  In practice, this means section 3 routines,
but not section 2.

    For some unknown reason, fflush(3) only flushes output buffers,
not input buffers.  The best solution I know uses:

    if (input = fdopen (dup (fileno (stdin)), "r"))
	fclose (stdin), *stdin = *input ;

This is less than ideal due to the use of `dup' which can cause protability
problems.  Using something like `ioctl (0,TCFLSH,0)' is not a solution since
stdin need not be coming from a terminal, and also since (as far as I know)
stdio buffering is totally independent from whatever buffer this ioctl call
refers to.

    Truncating a file also appears to be impossible using stdio level calls.
To illustrate the problem consider trying to:
	1) Create a temporary file using `tmpfile'.
	2) Storing a line of text in this file, and later rewinding the file
		and rereading the text.
	3) Storing a second line of text in the file, for later reuse.

At step (3) you must truncate the file to zero length; rewinding the file
is not sufficient since this allows the previous contents of the file to
`stick out' beyond the end to the current contents.  This may be accomplished
using `ftruncate (fileno (fp), 0)', however many systems do not support
ftruncate(2).  Using something like `freopen (...)' is no good since you
do not know the name of the file - only an open file pointer is available!

    I consider the inability to perform these tasks at the stdio level to
be a significant flaw in the stdio package.  If there are better ways to
deal with these problems, please let me know.
------------------------------------------------------------------------
Laurence G. Yaffe                          Physics Dept ; Princeton Univ
lgy@pupthy.PRINCETON.EDU                 PO Box 708 ; Princeton NJ 08544

guy@gorodish.UUCP (05/29/87)

>     For some unknown reason, fflush(3) only flushes output buffers,
> not input buffers.

The reason is quite well known.  "flushing" a stream means writing
all buffered output to the underlying device.  This has nothing to do
with input.  Purging an input buffer is a completely separate
operation; I agree that it might be a good thing if standard I/O
permitted you to do this, but it is emphatically NOT the operation
that "fflush" should do.  (No, please don't say "well, have 'fflush'
do a purge on a stream opened for reading"; what about something open
for reading *and* writing?)

Another problem is that you may want to flush anything buffered *by
the underlying device* as well; this would be a lot harder to do in a
portable and device-independent fashion.

> At step (3) you must truncate the file to zero length; rewinding the file
> is not sufficient since this allows the previous contents of the file to
> `stick out' beyond the end to the current contents.  This may be accomplished
> using `ftruncate (fileno (fp), 0)', however many systems do not support
> ftruncate(2).  Using something like `freopen (...)' is no good since you
> do not know the name of the file - only an open file pointer is available!

The fact that many systems do not support "ftruncate" should explain
why the standard I/O library *can't* help you here.  If you don't
have a name for a file, there *is* no portable way to truncate it to
zero length!  What could standard I/O possibly do on a system without
"ftruncate", or some other way to truncate a file given only a
"handle" for the file (e.g. a file descriptor on UNIX), rather than
a name?  The standard I/O library is not capable of performing
miracles....
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

gwyn@brl-smoke.UUCP (05/29/87)

In article <345@phoenix.PRINCETON.EDU> lgy@pupthy.PRINCETON.EDU (Larry Yaffe) writes:
>    There does not appear to be any clean/portable way to either
>(a) flush an input stream, such as stdin,

I don't know quite what that would mean.  "Flushing" an output buffer does
not mean discarding the data in it; it means forcing data buffered in the
STDIO library to be sent to the operating system.  The analogous effect for
an input stream would be to force the operating system to supply the unread
STDIO-buffered input data, but it has already done that.

Assuming from your example that you meant that you want a way to discard
unread data on a stream, note that input data is often at least partially
buffered in the operating system (e.g. UNIX kernel), not in user process
address space which is where STDIO has to operate.  The proposed ANS for C
requires that fflush() on an input stream undo the effect of any preceding
ungetc() operation on the stream; so far as I can judge, it does not
prohibit an implementation from discarding more unread input than that, but
it certainly does not require it (since, among other things, in general it
cannot be accomplished) and I would be annoyed at any implementor who
decided to do so.

On most UNIX-like systems, there is a way to flush kernel-buffered input
from terminals (e.g. change to RAW mode and back).  Some of these also
produce undesirable effects on terminal output.  Since UNIX usually returns
one input line at a time for read()s from a terminal, if you have input
through a new-line via STDIO, there would be no additional STDIO-buffered
input from a terminal until the next time you attempted input from it.
Therefore, forcing the kernel to discard type-ahead at that point would be
meaningful and potentially useful.

On UNIX System V, the termio ioctl command TCFLSH can be used to flush
terminal input and/or output queues (kernel buffers), as you noted.

> or (b) truncate an existing, open file.

There isn't a simple way to implement that.  On older UNIX systems,
truncating a file had to be accomplished by copying the file into a
temporary file, recopying the temporary file up to the truncation point
onto the original after re-creat()ing it (which truncates it to 0 length).
Needless to say, this is a non-atomic operation.  4.nBSD supports an
ftruncate() system call, as you remarked, but I don't know of a UNIX
System V equivalent.  It would certainly be useful, since Fortran-77 is
required to support file truncation.

So the reason for the "serious flaws" is simply that they cannot be
implemented reasonably in the STDIO library on all systems (not even
on all UNIX-like systems).  I consider it a "serious flaw" that I don't
have a universal interface to multiple processes and pipes, but so what?

mouse@mcgill-vision.UUCP (06/07/87)

In article <19977@sun.uucp>, guy%gorodish@Sun.COM (Guy Harris) writes:
>> For some unknown reason, fflush(3) only flushes output buffers, not
>> input buffers.
(The reason is "that's how it's written." :-)

> Purging an input buffer is a completely separate operation; I agree
> that it might be a good thing if standard I/O permitted you to do
> this, but it is emphatically NOT the operation that "fflush" should
> do.

That's why fdumpbuf() is one of the things I've added to stdio. Only
occasionally useful, but when you want it there's basically nothing
else that will do....

> The fact that many systems do not support "ftruncate" should explain
> why the standard I/O library *can't* help you here.  If you don't
> have a name for a file, there *is* no portable way to truncate it to
> zero length!

Is there a portable way to truncate it to zero length if you *do* have
a name for it?  Creating for write won't cut it because of helpful
systems like VMS that try to be nice by providing version numbers, and
creating just creates a new version....

> The standard I/O library is not capable of performing miracles....
(It isn't?  Bummer. :-)

					der Mouse

				(mouse@mcgill-vision.uucp)

devine@vianet.UUCP (06/16/87)

In article <798@mcgill-vision.UUCP>, mouse@mcgill-vision.UUCP (der Mouse) writes:
> >> For some unknown reason, fflush(3) only flushes output buffers, not
> >> input buffers.
> The reason is "that's how it's written." :-)

  Actually, that's how it is written for UNIX.  The Microsoft DOS C library
has a fflush() that operates on files opened for input or output.  It clears
the stdio buffer associated with that file by either writing the contents
(if open for write) or clears the buffer (read file).

Bob Devine