From: ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe)
Newsgroups: comp.lang.c
Subject: Re: Life, stderr, and everything (was Re: malloc() as a user...)
Date: 16 Oct 92 06:34:01 GMT
Article-I.D.: goanna.15327
Organization: Comp Sci, RMIT, Melbourne, Australia

I wrote:
:The point is that C programmers on UNIX systems (and others)
:have *no* *way* *to* *know* whether malloc() sets errno or not
:other than reading the source code of their particular runtime library.
Henderson wrote:
> If we don't know, then we can wait until malloc actually does return NULL,
> and then check errno. If it didn't set errno, this is perhaps disappointing,
> but it doesn't cause any major problems.

We seem to be arguing past each other.
I am saying "In the absence of explicit promises to the contrary in the
C standard, the operating system 'standard', or vendor documentation, a
C _programmer_ has no way of knowing whether malloc() *CAN*BE*RELIED*ON*
to set errno to anything meaningful."
Henderson is saying "A C _program_ can determine whether malloc() has
set errno _this time_."
He is almost right (it _might_ have been a signal handler that set errno,
but on the other hand, all bets are off when you use signal handlers).
My point is, "malloc() set errno _this_ time" is not a useful thing to know.

Look, the ANSI committee _could_ have insisted that standard library
functions like malloc() and fopen() should set errno to a meaningful
value if they report an error.  My single biggest gripe about ANSI C
is that they _didn't_ include any such guarantee for a great many functions
where you could really benefit from it.  My pet peeve is fopen():  there is
no portable way to get any meaningful indication of _why_ fopen() failed.
For one application I really really wanted that; I wanted to be able,
for example, to distinguish between
	"There isn't any such directory."
	"There is such a directory but you aren't allowed to look inside."
	"There isn't any such file in that directory."
	"There is such a file but you aren't allowed to touch it."
	"There is such a file, but it's a symbolic link to never-never land."
	"You can't create that file because you don't own it."
	"You can't create that file because the disc is full."
	"You can't create that file because the disc is mounted read-only."
and so on.  What I wanted to do was to provide information to the end user
which would let it [%] distinguish between say typing mistakes (try again
with the right spelling) and real problems.

I consulted a number of net.unix.experts who informed me that not only was
there no portable way to do this in C, there was no portable way to do it
in UNIX.  There are several stdio implementations, and you quite literally
_cannot_ rely on a call to fopen() mapping onto just one system call.
There way be calls to open(), creat(), fcntl(), and in some cases lseek().
Those are just the more obvious ones.  In the event of an error being
detected, you have no idea which one was the last one to set errno.

At one time I did write several pages of C code to diagnose the cause of
a failure to open a file.  (It did things like walk back down the path
trying each directory to see just where the problem was.)  I had to keep
on reading the on-line manuals for 3 different versions of UNIX at once
to have a hope of getting something that would port between versions of
UNIX, and it was _still_ hard to port to the 4th, because different
versions of UNIX have different sets of errno values and assign different
meanings to some of them.

> If malloc() sets errno to a value that is misleading, then you should
> complain to your vendor. If the message corresponding to a particular
> error number is misleading, then you should complain to your vendor.
> ...
> If your system overloads error numbers in such a way as to cause
> the error messages to be inappropriate, then complain to your vendor.
> 
> On *my* system, the error message for EAGAIN is the more informative
> 	Resource temporarily unavailable
> which you probably *would* want to pass on to Joe Luser.

No, I would not.  That is a *hopelessly* vague message.
WHAT resource?  Cpu cycles?  Some remote file server?  Disc space?
10p pieces to put in the gas meter so the operator won't freeze?
HOW temporarily?  If it's likely to be available soon, why the X
didn't the program just wait for it instead of dropping dead?  Is
there in fact any point in trying to run the program again?
This particular issue is important; if a resource is only
*temporarily* unavailable, what kind of shoddy program drops dead
instead of at least offering a dialogue?

Look, let's be really serious about this.  To be it seems absurd to claim
that 
	Resource temporarily unavailable
is a better message to pass on Jill Luser than
	There is not enough memory for this program to continue.
If you write your own message, you can be SURE that it is accurate
and phrased in terms your intended users can understand.

What good does it do to complain to the vendor?
On the machine I'm using at the moment, there are precisely two
C compilers available.  One is provided by the vendor.  The other
is GCC.   They use the same libraries.  (Never mind the GNU libc;
if we don't use the vendor's malloc() we can't use the vendor's
threads package, and there isn't any GNU threads library for this
machine.)  The vendor is now pushing a different line of hardware
and don't really care much.  If they cared to defend themselves
they could truthfully point out that
    there is absolutely NOTHING in the C standard or POSIX or the SVID
    or Plauger&Brodie or BSD documentation or ...
    to require malloc() or fopen() &c to set errno to anything meaningful
So portable code has _never_ been able to use perror() to gloss malloc()
failures.  They could further truthfully point out that
    the gloss given to EAGAIN by perror is precisely one of the things
    they did _not_ change; *many* other UNIX systems (including SunOS
    4.1.2) print precisely the same message.
They could further point to their documentation (and this particular
part is another of the things they did NOT change):
   perror produces a short error message on the standard error file
   describing the last error encountered during a call to the system from a
   C program. ...  The error number is taken from ... errno ..., which is
   set when errors occur on system calls,
----------------------------^^^^^^^^^^^^
Remember that malloc() and fopen() are NOT system calls!
    but not cleared when non-erroneous calls are made.  ... sys_nerr is
    the number of messages. It should be checked because
    new error codes may be added to the system before corresponding
----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    messages are added to the table.
----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

So the vendor can truthfully and FAIRLY claim that their system does
exactly what their documentation claims for it, and that it is no worse
than many other UNIX systems because this is one part of the system that
they have not changed.

Henderson says:
> My manual page for perror() says:
> 
>      Perror produces a message on the standard error output, describing	the
>      last error	encountered during a call to a system or library function.
> 
> If this message is not accurate, then that is a problem with perror(),
> and it is the vendor's responsibility to fix it.

Well, if Henderson's vendor says that perror() describes errors in
"system or LIBRARY function"s then yes, he has a valid complaint if THAT
vendor's malloc() does not live up to that claim.  But other people who
aren't using that vendor's products do _not_.  _I_ can raise no valid
complaint against (company name deleted) in this respect, because their
UNIX implementations do just what their manuals say that do, and just what
the original 4.3BSD and V.3 sources from which they were derived did.

I note that even IRIX (the wording of whose perror man-page corresponds
to Henderson's quotation, and which glosses EAGAIN via perror() as
Henderson says) makes absolutely no claim the either the malloc(3C) or
the malloc(3X) manual page about WHICH errno values are used to indicate
what situations.  As it happens, the only error number I have been able
to make IRIX report is ENOMEM, and that is glossed by perror() as

	Not enough space

As before, I claim that that is a hopelessly vague message to give to
Jill Luser.  What _kind_ of space?  Swap space?  File space?  Physical
memory?  Virtual memory?  Has X run out of room on the desktop?  Are
the operators feeling cramped in the machine room?  I cannot honestly
call this an accurate description of the problem; it gives Ms Luser
absolutely no idea what if anything can be done about it.  And I am not
going to whinge to Silicon Graphics or MIPS about this, because IRIX is
easily the best system I have come across for developing code that is 
intended to be portable across UNIX systems.

Another example of vagueness in UNIX error numbers.  Consider ENAMETOOLONG.
That could be because the _whole_ file name is too long, or because _one_
_component_ is too long.  The message _appears_, alas, to be unambiguous:
	File name too long
It might be wrong.  Consider the old System V file system.  This is a
file name which would provoke that error:
	directory/gimme-eighteen-chs/another
Do you really think that Joe Luser would realise at once that
	File name too long
is not a reference to the whole string, but just the "gimme..." chunk?
Give us a break.

I wrote:
:But please, people, I am not
:making it up, I am not exaggerating, when I say "you *CANNOT* trust errno
:EXCEPT when the documentation EXPLICITLY says you can", and when I advise
:you _not_ to rely on perror() for error messages sent to users.

Henderson replied:
> This is rather depressing advice.
> I just checked the documentation for fopen() on three different Unix systems,
> and none of them mentioned anything about setting errno.
> Does this mean that I can't use perror() when fopen() fails?

Too flaming right that's what it means.  My own reaction to this ranges from
irritation to outrage, and has for some time.  But that is, alas, the case.
Well, not exactly.

	You *can* "use" perror() when fopen() fails.
	You *can't* depend on it.

> I sure as hell would like to be able to give the user an informative error
> message in this case. (I have got a lot of programs (eg. gcc) that do
> call perror() if fopen() fails - should I throw them away? :-)

Throw them away?  No.  Get someone to fix them, if that is possible, yes.
It has *NEVER* *EVER* been possible to rely on perror() reporting anything
interesting after a failed call to fopen().

To get to specifics, let's consider the steps in a typical implementation
of fopen().

    Step 1.  Parse the "mode", {r|w|a}{b[+]|+[b]|}.
	If this fails, no system call has been made.
	What will errno be set to?  I have seen several UNIX implementations
	of fopen().  None of them set errno AT ALL in this case.  (I do not
	assert this of all UNIX fopens().  I do assert it of three.)

    Step 2.  Allocate a FILE record.
	This _may_ use a fixed pool of FILE records.  In that case, if we
	run out, it is highly unlikely that the implementation will bother
	to set errno.

	This _may_ use malloc() to allocate FILE records (perhaps after a
	fixed pool runs out).  In that case, if malloc() fails, we cannot
	rely on errno because malloc() isn't documented as setting it.

	Torek's stdio forcibly sets errno to ENOMEM in this case, but the
	copy I have does _not_ set errno in Step 1.

    Step 3.  Open the file.
	This will involve a system call.  In older UNIX systems it could
	be open() or creat() or both.  These days it _can_ be one open()
	call.  In this case, errno IS set.

> Seriously, you may be right that some or many versions of Unix have
> implemented error numbers and perror() rather poorly. This should
> not however be taken as a good reason to stop using perror(); instead
> we should demand decent implementations.

Very well, BUT WHAT DO WE DO IN THE MEAN TIME?  Write programs that
_ought_ to work in an ideal world but don't work in the real world?

Let me put it this way.
    The existence of perror() is no excuse for shirking the job of
    producing good error messages _yourself_.  By all means include
    strerror(errno) in your report, but on no account make that the
    ONLY explanation offered to the end user.

:My heart is pure:  I love UNIX and hate VMS just as I should.
:But in comparison with VMS status codes, UNIX errno values are _shocking_.
 
> Well, my heart is pure also, but you have got me interested now:
> could you please explain VMS status codes and why they are better than
> Unix?

There are a heck of a lot more of them, basically.  The VMS designers
decided to allocate a whole 32 bits to error information, so didn't feel
under any pressure to overload several meanings on one little word.
A VMS error code contains in itself an indication of which program, library,
or subsystem originated the message, what the message is, and how severe it
is.  It also has a perror equivalent (which has been "locale" sensitive for
some time) so that end users get moderately readable text and they also get
an alphabetic code they can look up in manuals if you like.  VMS error codes
and result codes are unified, whereas in UNIX <sysexits.h> is widely
available and as widely ignored (I am the only C programmer I have met who
even _tries_ to follow the <sysexits.h> conventions).  Also, since VMS
error texts are stored in files, not the kernel, VMS designers have not
felt any pressure to make the error _texts_ as, um, short as the UNIX
ones.  I would accept VMS error texts in reports intended for end users.