[comp.unix.programmer] ENOTTY and perror

tchrist@convex.COM (Tom Christiansen) (10/27/90)

In article <8215:Oct2521:30:3890@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>In article <1990Oct25.075856.4923@robobar.co.uk> ronald@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