ENOTTY and perror (was: Checking return values)
Tom Christiansen
tchrist at convex.COM
Sat Oct 27 04:12:46 AEST 1990
In article <8215:Oct2521:30:3890 at kramden.acf.nyu.edu> brnstnd at kramden.acf.nyu.edu (Dan Bernstein) writes:
>In article <1990Oct25.075856.4923 at robobar.co.uk> ronald at robobar.co.uk (Ronald S H Khoo) writes:
>> perror(name);
>> fprintf(stderr, "%s: error opening %s (see error message above)\n",
>> progname, name);
>
>Correct but ugly. As long as you have an array sys_errlist[] with the
>messages, you can just copy errno into a variable and use that. To skip
>coding most of the common idioms you might snarf err.c from the pty
>package.
Note that POSIX doesn't really want you looking at perror. You should use
strerror() to be compliant. Some POSIX systems do let you look at
sys_errlist, but strictly conforming ones won't.
The problem with fprintf() unjustly mucking with errno can be cleaned up a
little by having isatty() resent errno to its previous value if it returns
ENOTTY. I see no reason for putc to set ENOTTY; it's not relevant and
confusing. This may cause a bunch of programs to print "Error 0" or "Not
a system error" if you've changed sys_errlist[0] to say that, but so
what? They all used to say "Not a typewriter", which was worse, because
they confuse end-users by calling perror() inappropriately and getting
spurious warnings. (One user actually once asked why he had to use a
typewriter to send mail to a (non-existent) user before I made the
aforementioned changes.)
Here is a brief but evil package that could be made far more portable by
using varargs, int sprintf(), and strerror; this should work as is on a
non-POSIX paranoid, straight BSD system. The MESG macro helps the perror
problem a bit. Use -DBSD=42, -DBSD=43, or -DBSD=44, depending on what
flavor of sysexits.h you have.
--tom
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
# die.h
# die.c
# This archive created: Fri Oct 26 13:06:28 1990
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'die.h'" '(1193 characters)'
if test -f 'die.h'
then
echo shar: "will not over-write existing file 'die.h'"
else
sed 's/^ X//' << \SHAR_EOF > 'die.h'
X#ifndef FILE
X#include <stdio.h>
X#endif
X
X#ifndef EX_OK
X#include <sysexits.h>
X#endif
X
X
X#ifdef MAIN
X# define VAR(name,value) name = value
X#else
X# define VAR(name,value) extern name
X#endif
X
XVAR( char Assert_Mask[], "Assertion botched <%s> in file \"%s\" @ line %d\n" );
X
XVAR( char *program, "Unknown Program") ; /* set to basename(argv[0]) */
X
Xchar _msg_errbuf[50];
X
Xextern int errno, sys_nerr;
Xextern char *sys_errlist[];
X
X/* current error message */
X#define EMSG (errno < sys_nerr ? sys_errlist[errno] : DUNNO(errno))
X#define DUNNO(ERR) sprintf(_msg_errbuf, "unknown system error %d", ERR)
Xextern char *sprintf ();
X
X/* system call succeeds or i die */
XVAR( char Failed_Syscall [], "syscall \"%s\" failed: %s");
X#define GOOD_SYS(Z) if (Z < 0) die (EX_OSERR, Failed_Syscall, "Z", EMSG)
X
X/* better assert */
X#ifndef FAST_AND_LOOSE
X# define _assert(EXPR) \
X { if (!(EXPR)) { \
X fprintf( stderr, Assert_Mask, "EXPR", __FILE__, __LINE__); \
X abort(); /* generate core dump */ \
X } \
X }
X# define assert(EXPR) _assert(EXPR)
X#else
X# define _assert(EXPR)
X# define assert(EXPR)
X#endif !FAST_AND_LOOSE
X
SHAR_EOF
if test 1193 -ne "`wc -c < 'die.h'`"
then
echo shar: "error transmitting 'die.h'" '(should have been 1193 characters)'
fi
chmod 664 'die.h'
fi
echo shar: "extracting 'die.c'" '(1963 characters)'
if test -f 'die.c'
then
echo shar: "will not over-write existing file 'die.c'"
else
sed 's/^ X//' << \SHAR_EOF > 'die.c'
X#define MAIN
X#include "die.h"
X
X/*
X * die function. first argument should be a legit exit status
X * out of sysexits.h; the rest should be arguments as you would
X * give to printf(), ie, string format followed by optional arguments.
X *
X * note that you should bind (char *) program to your name
X *
X * here are examples:
X
X extern char *program;
X program = rindex(*argv,'/');
X program = program ? program+1 : *argv;
X
X if (argc != 2)
X die (EX_USAGE, "wanted one and only one argument");
X
X if (!lock(lock_file))
X die (EX_TEMPFAIL, "couldn't secure lock on %s", lock_file);
X
X if (stat (USERS_FILE, &stat_info))
X die (EX_NOINPUT, "stat failed on %s: %s", USERS_FILE, EMSG);
X
X if ((buf = malloc ( (u_int) stat_info.st_size)) == 0)
X die (EX_OSERR, "malloc(%d) failed: %s", stat_info.st_size, EMSG);
X
X if ((fd = open (USERS_FILE, O_RDONLY)) < 0)
X die (EX_NOINPUT, "absurd error opening %s: %s", USERS_FILE, EMSG);
X
X if ((got = read (fd, buf, stat_info.st_size)) != stat_info.st_size)
X die (EX_IOERR, "only read %d (not %d) from %s: %s",
X got, stat_info_st.size, USERS_FILE, EMSG);
X
X *
X * note also that the EMSG macro is the string of the current system error
X * message, if you want to use it. this is the same as %m in syslog().
X * see below for required extern declarations if you wish to use EMSG.
X */
X
X/* VARARGS2 */
Xdie (status, fmt, args)
X int status;
X char *fmt;
X int args; /* convenient lie */
X{
X
X#if BSD == 42 /* { */
X assert(status >= EX_OK && status <= EX_NOPERM);
X#else
X#if BSD == 43 /* { */
X assert(status >= EX_OK && status <= EX_CONFIG);
X#else
X#if BSD >= 44 /* { */
X assert(status >= EX_OK && status <= EX__MAX);
X#endif /* 44 } */
X#endif /* 43 } */
X#endif /* 42 } */
X
X/*
X * sigh; wish there were an EX_MAXEXIT to check against.
X */
X
X fprintf(stderr, "%s: ", program ? program : "unknown program");
X _doprnt(fmt, &args, stderr);
X putc('\n', stderr);
X
X exit(status);
X}
SHAR_EOF
if test 1963 -ne "`wc -c < 'die.c'`"
then
echo shar: "error transmitting 'die.c'" '(should have been 1963 characters)'
fi
chmod 664 'die.c'
fi
exit 0
# End of shell archive
More information about the Comp.unix.programmer
mailing list