Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!mnetor!uunet!husc6!sri-unix!quintus!ok
From: ok@quintus.UUCP (Richard A. O'Keefe)
Newsgroups: comp.lang.c
Subject: Re: stdio error detection
Message-ID: <336@cresswell.quintus.UUCP>
Date: Wed, 2-Dec-87 18:07:50 EST
Article-I.D.: cresswel.336
Posted: Wed Dec  2 18:07:50 1987
Date-Received: Sun, 6-Dec-87 04:03:12 EST
References: <289@cresswell.quintus.UUCP> <13100001@bucc2>
Organization: Quintus Computer Systems, Mountain View, CA
Lines: 165
Summary: ANSI C errno stdio

If you're interested in fclose(), here is the conclusion so far
from mail several people were kind enough to send me:
    -	because of buffered output,
	anything that can go wrong with fputc() can go wrong with fclose().
    -	even if you have successfully fflush()ed just before fclose()ing,
	some sort of device control error might happen
    -	when an error occurs,
	everyone thinks the stdio stream ought to be closed,
	but there is no guarantee that it is.
    -	in EUUG V7 UNIX, fclose() called fflush(), close(), and free()
	in that order.  An error might be signalled because of any of
	them, so errno could be anything, but at least in that version
	of UNIX fclose() always freed up the stdio stream.
My conclusion is that I ought to check for an error return from fclose(),
because there *might* be lost data.  In fact, I ought to fclose() or
fflush() stdout before exiting, to be sure I don't miss lost data.  But
there is nothing I can do to tell the difference between lost data and
a device that won't close.  At least with respect to UNIX, I'm not
worrying about lost streams any more.

I have definitely learned something from this:  it had never occurred
to me before that my programs ought to end with something like
	if (fflush(stdout))
	    error_exit("probable data loss from stdout");
Ouch.

If you're interested in a rambling discussion of error codes,
continue, otherwise stop now.

In article <289@cresswell.quintus.UUCP> I said
> It would be really nice if the stdio functions were defined to set
> errno (any ANSI C people care to comment?).
In article <13100001@bucc2>, markb@bucc2.UUCP replied:
> Set errno to what?  There is no way the C standards committee is going to
> attempt to identify all OS-dependent low-level error causes and provide
> standard encodings for all of them!

This is a red herring:  maybe the latest draft is radically different,
but in the October '86 draft of the ANSI C standard *NO* values of
errno are defined at all.  The standard *does* say that certain
specified functions set errno to indicate an error, but says nothing
about what they might set it TO.  All I was suggesting here was that
ANSI C should do the same sort of thing for fclose() that it already
does for signal().  Here is what the text says:
	Otherwise, a value of SIG_ERR is returned, and
	>> the integer expression errno is set to indicate the error. <<

The October 86 draft says of fclose:
	The fclose function returns zero if the stream was successfully
	closed or nonzero if any errors were detected or if the stream
	was already closed.
All I'm asking for here is for something like
	If the stream was successfully closed, the fclose function
	returns zero.
  NEW	If any errors were detected or if the stream was already
  NEW   closed, the fclose function returns nonzero, and
  NEW	the integer expression errno is set to indicate the error.

Several people told me that all my problems could be solved by
setting errno to 0 beforehand and checking it afterwards.  This is
not defined to work for ANYTHING, even a straight system call.  A
successful system call is entitled to set errno to anything it pleases.

Even supposing an implementation of stdio uses straight V7 system
calls and nothing else and that those system calls do not change
errno when they are successful, there is no reason to expect the
last unsuccessful system call done by an implementation of some
stdio routine to be the one which is responsible for the failure.
Suppose an implementation of fopen(3s), having found that open(2)
failed, tried to call stat(2) in an attempt to diagnose the error
for me, and that while executing stat(2) an I/O error occurred.
Then errno would be set to reflect the incomplete status of some
operation I neither know nor care about, AND THAT WOULD BE LEGAL.

How difficult could it be for a C implementor to ensure that the
value given to errno reflected the error responsible for the failure
of the stdio operation?  [Strictly speaking it is impossible, but
that's true of everything, not just stdio.  See signal().]  Quite
easy:  just put errno in a safe place and move it back just before
returning the failure code.  Yes, the actual numbers would be
implementation defined, but ALL errno numbers are implementation
defined.  That's a great deal better than not having any errno at all.

To repeat markb's comment:
> Set errno to what?  There is no way the C standards committee is going to
> attempt to identify all OS-dependent low-level error causes and provide
> standard encodings for all of them!
Why not?  The COBOL committee made the attempt (:-).
Seriously, provided that host-specific details are accessible some other
way (which might as well be host-specific itself), there aren't all that
many different cases.  With a little bit of mental effort, it is easy to
find error classes which are not just host-independent, but aren't even
tied to files as such.  Here are some, with UNIX examples.

E_DEADLY_PARAMETERS
    The parameters are so scrambled we nearly died trying to read them
	EFAULT	(typically means wild address, e.g. 0)

E_INVALID_PARAMETERS
    We could find the parameters, but they didn't make sense
	EINVAL	(file name syntax error)

E_NO_SUCH_OBJECT
    We found the parameters and they made sense but there is no such object
	ENOENT	(missing file or directory)

E_OBJECT_ALREADY_EXISTS
    We found the parameters and they made sense but there already is
    an object of that sort so you can't create one
	EEXIST, EADDRINUSE

E_WRONG_TYPE
    We found the object ok, but it is not the right type of object
	ENOTDIR, EISDIR, ENOTTY, ESPIPE, EPROTOTYPE, ENOTSOCK,

E_NOT_ALLOWED
    We found the object ok, but you aren't allowed to do that operation
	ENOPERM	(no write permission), EROFS, EACCES

E_BUSY_TRY_LATER
    We found the object ok, and you can do that, but it's busy with
    another caller right now.  Try again later.
	EBUSY, ETXTBSY, EALREADY, EWOULDBLOCK, EOPNOTSUPP

E_WRONG_STATE
    We found the object ok, and you might be able to do that, but you
    have the object in the wrong state just now.
	EISCONN, ENOTCONN, 

E_RESOURCE_EXCEEDED
    We found the object and you can do that, but you ran out of X
	(processes: EAGAIN, memory: ENOMEM, per process file
	table: EMFILE, system file table: ENFILE, link count field:
	EMLINK, file size: EFBIG, socket buffers: ENOBUFFS, &c)

E_OPERATION_FAILED
    We found the object and you can do that, but something went wrong
	EIO,	(physical I/O error, RFS locks lost, &c)
	ETIMEDOUT, ECONNREFUSED, ...

You want more information about various errors.  For example, if you
can't find a file:  is the device off-line, which directory is missing,
or is it the file proper?  But you can't pack it all into one integer,
and the availability of further details would be system-specific.
That doesn't mean that a coarse classification such as I'm sketched
above would be any less use, or that it would be system-specific.

This was something of a digression.  It was NOT the job of the ANSI
C committee to redesign the errno mechanism.  For ANSI C, specifying
that errno is set, without saying what it is set to, is the right
choice.  Given that the description of  has always been
incredibly vague, ANSI C's leaving it undefined whether error is set
to indicate a stdio error, or some other error, or the phase of the
moon, is probably the right choice too:  no VALID existing program,
it appears, could have depended on the setting of errno after a
stdio operation, so no valid existing program will be broken by
leaving it undefined.  But it would be a help to new code if the
stdio operations were defined to set errno on error, and it would be
interesting to know whether the ANSI C committee considered doing
this, and what practical obstacles they found.

I used to be rather fond of C, but this error stuff is quite
incredibly bad.  The problem isn't really the language; it's
the libraries.  For a really horrible example of an under-specified
library package, look up "hsearch" in the SVID or a System V manual.