returning error from main()

J

junky_fellow

Guys,

If main() calls some function func() and that function returns
the error (errno), then does it make sense to return that value
(errno) from main. (in case main can't proceed further) ?

eg.

int main(void)
{
int error;

if ((error = func()) > 0) {
return(error); /* Is this Ok. ? */
}

return 0;
}

Otherwise, if this is not the right thing, what else should be done ?
 
B

Ben Pfaff

int main(void)
{
int error;

if ((error = func()) > 0) {
return(error); /* Is this Ok. ? */
}

return 0;
}

This seems like a bad idea, for at least three reasons. First, C
only defines the meaning of three return values from main (to
wit, 0, EXIT_FAILURE, and EXIT_SUCCESS). Second, many operating
systems in practice ascribe specific meanings to specific return
values (e.g. VMS) or have a severely limited range of return
values (e.g. Unix). Third, this is not a convention that I have
encountered anywhere.
Otherwise, if this is not the right thing, what else should be done ?

Typical would be to write an explanatory message to stderr and
return EXIT_FAILURE.
 
K

Keith Thompson

If main() calls some function func() and that function returns
the error (errno), then does it make sense to return that value
(errno) from main. (in case main can't proceed further) ?

eg.

int main(void)
{
int error;

if ((error = func()) > 0) {
return(error); /* Is this Ok. ? */
}

return 0;
}

Functions don't typically return errno values. Instead, many
functions return a value indicating that *some* error occurred and set
the global variable 'errno'.

There's normally no particular relationship between errno values and
values passed to exit(). It's tempting to thing that there is, since
0 means "no error" for both, but the non-zero values don't match,
either in standard C or on any system I'm familiar with.
 
A

Army1987

"(e-mail address removed)" <[email protected]> writes:
Functions don't typically return errno values. Instead, many
functions return a value indicating that *some* error occurred and set
the global variable 'errno'.

M$ has lots of their "safe" functions returning an errno_t, such
as errno_t fopen_s(FILE **, const char *, const char *);
errno_t is a typedef for int, and the value returned is 0 on
success and errno on failure.
(I'm still trying to figure out why that should be considered
"safer".)
 
J

jacob navia

Army1987 said:
M$ has lots of their "safe" functions returning an errno_t, such
as errno_t fopen_s(FILE **, const char *, const char *);
errno_t is a typedef for int, and the value returned is 0 on
success and errno on failure.
(I'm still trying to figure out why that should be considered
"safer".)

Because of a better error analysis. That's why is safer.

1) None of its arguments should be NULL.
Those are constraints in the input arguments.
2) If there is a runtime constraint violation, the FILE ** argument
is set to NULL and an error return code is given to the calling
application.

This is much better than just getting a NULL and
trying to figure out why. The fopen specs just says "fopen returns
NULL on failure", not even leaving some errno mention...
No error analysis, like many other functions of the current
standard.
 
A

Army1987

If main() calls some function func() and that function returns
the error (errno), then does it make sense to return that value
(errno) from main. (in case main can't proceed further) ?

The only value guaranteed to portably indicate failure is
EXIT_FAILURE in stdlib.h. For example, on Unix-like systems the
return status is clipped to the last eight bits, so returning
256, or 512 for example, would actually indicate success. (Yes,
errno codes don't usually go that high, but I think you get the
point.)
 
J

jacob navia

Guys,

If main() calls some function func() and that function returns
the error (errno), then does it make sense to return that value
(errno) from main. (in case main can't proceed further) ?

eg.

int main(void)
{
int error;

if ((error = func()) > 0) {
return(error); /* Is this Ok. ? */
}

return 0;
}

Otherwise, if this is not the right thing, what else should be done ?

It is the right thing to do. You should just document the error
code with its meaning, and if possible and stdin is not redirected
print some error message.

Some systems will truncate the error return, but keeping within
1 to 127 should be probably safe on all systems, i.e. a positive
8 bit number.

jacob
 
R

Richard Tobin

If main() calls some function func() and that function returns
the error (errno), then does it make sense to return that value
(errno) from main. (in case main can't proceed further) ?

You need to consider the environment in which your program will be
used. Will it be used on an operating system where the full range of
error values can be returned? That's not guaranteed by the C
standard. Will those values be useful to someone running the program?

Someone using the program interactively will find it more useful to
have an error message printed ("foo: file not found" for example)
rather than having to decide a return value. Someone using the
program as part of a script may benefit from being able to test the
exit status, but will it really be useful for the script to
distinguish between all the different values?

-- Richard
 
B

Ben Bacarisse

jacob navia said:
Because of a better error analysis. That's why is safer.

This is and odd point of view. fopen can used quite safely and
fopen_s can be used unsafely.
1) None of its arguments should be NULL.
Those are constraints in the input arguments.
2) If there is a runtime constraint violation, the FILE ** argument
is set to NULL and an error return code is given to the calling
application.

You don't mean that (I hope).
This is much better than just getting a NULL and
trying to figure out why. The fopen specs just says "fopen returns
NULL on failure", not even leaving some errno mention...

The standard allows any library function to set errno how it likes
(provided that does not clash with a specified setting) but it wisely
does not require anything more from fopen, since some environments may
not be able to provide anything more.

Good implementations provide error information about fopen failures
through errno. Mandating that this number be returned does not
improve the "error analysis".

The real reason is, I suspect, that MS would like to create a buzz
around the idea that they care about "security". I am not so
absolutist that I think there will be no benefit from these new
"safer" functions -- there may well be a class of programmer who can't
write safe C without them and will be able to with them -- but I
suspect theat class is small, and the improvement will be
insignificant.

I do object to cluttering an already crowded name space with functions
that can be implemented in standard C. If the committee feels obliged
to follow this new proposal I would hope they can find a way to make
it optional for those of us who are happy with what is there already
(e.g. by putting them in new header files -- maybe stdios.h, stdlibs.h
and so on).
 
K

Keith Thompson

jacob navia said:
It is the right thing to do. You should just document the error
code with its meaning, and if possible and stdin is not redirected
print some error message.

Some systems will truncate the error return, but keeping within
1 to 127 should be probably safe on all systems, i.e. a positive
8 bit number.

No, it's not the right thing to do, at least not if you care about
portability.

For example, on one system I use, an errno value of 21 is "EISDIR",
described as "Is a directory". (For example, you might get this if
you pass a directory name to fopen().) How do you know that returning
a value of 21 from main(), or calling exit(21), sends a failure
indication to the invoking environment? I've worked on systems where
exit(21) would indicate *successful* termination. And EISDIR might be
some other number on another system, or it might not be defined at
all, making the interpretation of the program's exit status completely
system-specific.

The same system that defines EISDIR as 21 defines errno values up to
131.

Even if you're willing to limit yourself to a single operating system
(though the OP implied no such willingness), on the systems I use
there is *no* convention of using errno values as returned results
from main. Both coincidentally use 0 to indicate "no error", but
that's where the similarity ends. On Unix-like systems, for example,
'exit(1)' often indicates a non-specific failure. If I saw a program
terminate with an exit status of 1, it would never occur to me to
assume that it had died due to an EPERM ("Operation not permitted")
error (EPERM happens to be defined as 1).

errno values denote failure modes for individual function calls. They
can be used to construct error messages, using perror() or strerror().

And what exit status do you return for a program error that doesn't
correspond to any errno value, such as a missing command-line
argument?

For portable code, use EXIT_FAILURE to denote failure, 0 or
EXIT_SUCCESS to denote success. If a function fails and sets errno,
use perror() or strerror() to construct an error message (but you may
or may not want to show such a message to the user). If you need to
indicate more than one kind of failure in your program's exit status,
you'll need to document which results mean what -- and your choice of
values is likely to depend on the operating system's conventions.

(In a language with stronger typing, errno and the argument to exit()
would have distinct and incompatible types, but that's not really an
option for C.)
 
A

Army1987

jacob navia <[email protected]> writes:

[Why is M$'s fopen_s() safer than fopen()?]
This is and odd point of view. fopen can used quite safely and
fopen_s can be used unsafely.

Indeed. I can't see any difference between using

err = fopen_s(&fp, filename, "rb+");
if (err)
do_something(err);

and

fp = fopen(filename, "rb+");
if (fp == NULL)
do_something(err);

in either ease of use, or usefulness. M$ is allowed to decide that
fopen() should always set errno when it returns NULL, and it is
allowed to decide what happens when either argument is a null
pointer.
Wow. What an error extremely likely to be done. On which fraction
of times you used fopen() or fopen_s() the last argument wasn't
a string literal? As for the filename, it is still very uneasy
to accidentally pass a null pointer...
You don't mean that (I hope).
Even if he meant that the pointer object pointed by the FILE **
argument is set to NULL, I can't see how this helps...
[snip]
The real reason is, I suspect, that MS would like to create a buzz
around the idea that they care about "security". I am not so
absolutist that I think there will be no benefit from these new
"safer" functions -- there may well be a class of programmer who can't
write safe C without them and will be able to with them -- but I
suspect theat class is small, and the improvement will be
insignificant.
Indeed, there are functions which are saner than their ISO C
counterparts (strncpy_s, gets_s...) or do something different
(rand_s), but in the case of fopen_s() I can't see any
improvement, no matter how hard I try to imagine.
I do object to cluttering an already crowded name space with functions
that can be implemented in standard C. If the committee feels obliged
to follow this new proposal I would hope they can find a way to make
it optional for those of us who are happy with what is there already
(e.g. by putting them in new header files -- maybe stdios.h, stdlibs.h
and so on).
M$'s standard C headers only declare them (or some of them, I'm
not sure) if some macro beginning with underscores is defined
before they're #include'd, unlike some (most?) standard headers of
lcc-win32, which have declarations not in ISO C not guarded by any
#if or #ifdef.
 
J

jacob navia

Keith said:
For portable code, use EXIT_FAILURE to denote failure, 0 or
EXIT_SUCCESS to denote success. If a function fails and sets errno,
use perror() or strerror() to construct an error message (but you may
or may not want to show such a message to the user). If you need to
indicate more than one kind of failure in your program's exit status,
you'll need to document which results mean what -- and your choice of
values is likely to depend on the operating system's conventions.


This is exactly what I said:
<quote>
You should just document the error
code with its meaning, and if possible and stdin is not redirected
print some error message.

Some systems will truncate the error return, but keeping within
1 to 127 should be probably safe on all systems, i.e. a positive
8 bit number.
<end quote>

Personally I do not see anything wrong with giving detailed
error codes. They allow to the calling program a more
detailed reaction and still maintaining the convention of
zero for success, and non zero for failure.

Note that I am not speaking about errno at all. If those
constants are applicable, you can use them but then, you
run (maybe) into portability problems.
 
F

Flash Gordon

jacob navia wrote, On 20/09/07 06:12:
This is exactly what I said:
<quote>
You should just document the error
code with its meaning, and if possible and stdin is not redirected
print some error message.

Some systems will truncate the error return, but keeping within
1 to 127 should be probably safe on all systems, i.e. a positive
8 bit number.
<end quote>

Returning 1 (or any odd value) on VMS will indicate success. I.e. you
should replace "probably safe on all systems" with "definitely wrong on
some systems but probably safe on most current systems."
Personally I do not see anything wrong with giving detailed
error codes.

Agreed. However they are not portable.
They allow to the calling program a more
detailed reaction and still maintaining the convention of
zero for success, and non zero for failure.

A convention that VMS does not use, so if you return any odd value the
user will see it as successful termination, and with many values IIRC it
will even display a message giving the reason for success (or at the
very least there are "VMS standard" ways to decode that information).
Note that I am not speaking about errno at all.

The OP *did* mention errno, although it looked to me like the OP was
confused about errno and functions returning values indicating failure.
If those
constants are applicable, you can use them but then, you
run (maybe) into portability problems.

It *definitely* gives portability problems. E.g.
#define EOWNERDEAD 130
#define ENOTRECOVERABLE 131

Also if VMS uses odd values for errno...
 
J

jacob navia

Flash said:
Returning 1 (or any odd value) on VMS will indicate success. I.e. you
should replace "probably safe on all systems" with "definitely wrong on
some systems but probably safe on most current systems."

Ahh VMS...
I am glad it disappeared.

You are right. I stand corrected for VMS.
 
K

Keith Thompson

jacob navia said:
This is exactly what I said:
<quote>
You should just document the error
code with its meaning, and if possible and stdin is not redirected
print some error message.

Some systems will truncate the error return, but keeping within
1 to 127 should be probably safe on all systems, i.e. a positive
8 bit number.
<end quote>

No, it's not exactly what you said. The OP asked specifically about
returning errno values from main(). You said it's the right thing to
do. I said it isn't.
Personally I do not see anything wrong with giving detailed
error codes. They allow to the calling program a more
detailed reaction and still maintaining the convention of
zero for success, and non zero for failure.

Detailed error codes are great *if* the OS supports them.

The convention of zero for success and non-zero for failure is not
universal. For example, VMS uses odd values for success, even values
for failure (the C runtime has a kludge that translates 'exit(0)' to a
status value of 1). Perhaps few people use VMS these days, but any
assumption other than 0 and EXIT_SUCCESS for success, EXIT_FAILURE for
failure is still non-portable. (I've worked on C programs for VMS
that used 'exit(1)' to denote failure; they were wrong.)
Note that I am not speaking about errno at all. If those
constants are applicable, you can use them but then, you
run (maybe) into portability problems.

I won't repeat the arguments against returning errno values from
main(), but that *is* what you were talking about. The OP
specifically mentioned errno, and you told him it's a good idea.
Perhaps you didn't notice?
 
R

Richard Bos

jacob navia said:
(e-mail address removed) wrote:
It is the right thing to do. You should just document the error
code with its meaning, and if possible and stdin is not redirected
print some error message.

No, it is the wrong thing to do. If you do document the code and make
sure that there are no clashes, it is at least a correct thing to do;
but for semantic reasons, it is still the Wrong Thing.

When a program or shell script calls a program that might terminate in
an error, the caller usually does not care precisely _why_ a file, for
example, could not be opened. Instead, it wants to know in _which_ part
of the program the error occurred. For example, you want to be able to
handle the case where "I could not open the settings file for some
reason", and not "I did not have sufficient rights to open a file, but
buggered if I can tell you which file". In the first case, the caller
can look at the settings file; in the second, it must check all files
that could possibly be opened and see for which the rights are correct -
and must know what kinds of rights are needed for which file.

So: do not return a simple errno value from main(). More likely than
not, it will be useless to your caller. Return a meaningful value which
tells your caller _what_ failed, and not because of which detail
"something" went wrong.

Richard
 
M

Mark McIntyre

Ahh VMS...

mhm. Returning a positive number causes the OS to print a system error
message specific to that value. For example returning 126 will advise
you "%SYSTEM-?-DEVNOTMOUNT, device is not mounted"
I am glad it disappeared.

VMS has far from disappeared. You might want to check over in
comp.os.vms where you will be provided with plenty of examples of real
world ongoing use in business applications.

You might also want to ponder why HP maintain an OpenVMS website which
has an extensive support section, and where new releases are still
being made, and where you can buy a license for the OS.

Oh, and you can run VMS on an x86, for what its worth. I'm doing it
right now. <advocacy>For fifty quid you can get a license for the most
stable os ever written. </>


--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
M

Malcolm McLean

Ben Bacarisse said:
The real reason is, I suspect, that MS would like to create a buzz
around the idea that they care about "security". I am not so
absolutist that I think there will be no benefit from these new
"safer" functions -- there may well be a class of programmer who can't
write safe C without them and will be able to with them -- but I
suspect theat class is small, and the improvement will be
insignificant.
The real answer is to implement safe pointers, which consist of three memory
addresses, the pointer itself and the bounds of the object it points to.
It means a slower language, but there just isn't another way I know of of
preventing buffer overruns. If you have an overrun there is a bug in the
program anyway, so whose to say that your length parameter is accurate?
I do object to cluttering an already crowded name space with functions
that can be implemented in standard C. If the committee feels obliged
to follow this new proposal I would hope they can find a way to make
it optional for those of us who are happy with what is there already
(e.g. by putting them in new header files -- maybe stdios.h, stdlibs.h
and so on).
I don't really care whether the function to copy a string is

strcpy(char *dest, const char *src)

or

safestrcpy(char *dest, size_t destlen, const char *src, size_t srclen)

however I need to know that the function will be available wherever I want
the code to run, and I would much prefer a call that is familiar to my
readers than a hand-rolled string library.
 
B

Ben Bacarisse

Malcolm McLean said:
The real answer is to implement safe pointers, which consist of three
memory addresses, the pointer itself and the bounds of the object it
points to.
It means a slower language, but there just isn't another way I know of
of preventing buffer overruns.

We must be thinking of this from opposite ends. The best way to
prevent buffer overruns is to write code that does not allow them to
occur. You must be talking about preventing buffer overruns no matter
how bad the programmer is at writing correct code. That is a solved
problem -- as you say there are languages that simply prevent it.
They can't guard against all nonsense, but buffer overruns are easily
prevented at the language level.
If you have an overrun there is a bug
in the program anyway, so whose to say that your length parameter is
accurate?

Quite. They can't be prevented in traditional C implementations (if
we take your perspective where the programmer must be considered part
of the problem rather than the solution) so I am naturally skeptical
of the "safer C" camp.
I don't really care whether the function to copy a string is

strcpy(char *dest, const char *src)

or

safestrcpy(char *dest, size_t destlen, const char *src, size_t srclen)

however I need to know that the function will be available wherever I
want the code to run, and I would much prefer a call that is familiar
to my readers than a hand-rolled string library.

So you don't care provided it is available everywhere and familiar?
Sounds like you do care and you want strcpy!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top