Lee's Error Library
===================

This is a library to help improve error messages.

For example,
    $ cat /bin/sh/boy
    cat: can't open "/bin/sh/boy" for reading: "/bin/sh" is not a directory

There are several ways of using the library at the moment.
This probably reflect poor design, and a stupid author.

In each case, include a header file
	#include <lrqErrors.h>
in every source (.c/.l/.y/.c++) file in which you want error checking.

The different options are invoked with the lrqETYPE variable.

If you don't define lrqETYPE, the entire header file is ignored, and
everything is as ``normal''.  This way, you can turn off the new functions
entirely from the Makefile.

If lrqETYPE is defined, it should be an integer.
There are also some symbolic constants you can use instead, if you prefer.

Value	Name		Effect
========================================================================

0	lrqESTRING	If a system call or library function would return
			an error, the replacement routine builds up a
			string containing a description of the problem.
			You can access this directly as lrqE_lasterror,
			or you can arrange for the function to print it
			with lrqEseterror().

1	lrqEPERROR	This is the same as 0, except that there is a
			modified version of perror(), which prints
			lrqE_lasterror instead of sys_errlist[errno] as
			usual.  STRING and PERROR are different
			becaues people sometimes use perror() in strange
			ways, setting errno themselves first...
			It would be unacceptable for lrqE_lasterror to be
			printed in that sort of situation, of course.

2	lrqEDEBUG	This is useful for writing and testing smallish
			programs; the routines themselves call error 
			printing functions.  You don't want to use this if
			you have cases where you don't want your program
			to bomb out on error.
			If you also set the error severity (see below)
			appropriately, the program will call abort() so
			you can attack the corpse with your favourite
			debugger (adb/sdb/cdb/dbx/gdb/...).

3	lrqENORMAL	This is the most general case.  You have to make
			source changes to the code, however, so unless you
			are writing from scratch it might be too much work.
			Errors cause a string to be made (as in PERROR,
			DEBUG, etc), and this is then passed to a user-
			defined function.  The default function -- loaded
			from the library if you don't redefine it youself --
			is lrqE_errprint, descibed below in detail.
			Of course, the minimal change you might make is
			simply to call lrqEseterror() to set your own
			error handler.

Now, there are two other issues that are quite important: one is
efficiency, and ther other is that you might not want an error reported.
Taking these in reverse order...
1. Special Cases

Example:
	 exec("/usr/ucb/csh",....);
	 exec("/bin/sh",....);
Here you wouldn't want the first exec() to exit with an error message,
because you've arranged a contingency plan.  Of course, if the *second*
exec() fails...

If you compile with lrqE defined (even if it's 0), some macros are defined
to help.
You can always call the library routines directly:
    lrqE_exec("/bin/sh"...)
will print errors through the current error handler irrespective of the
value of lrqE. Thus, you can use cc -DlrqE=lrqEPERROR and explicitly call
the E-routines when you want an error message.

2. Efficiency
There are some macros which call the error functions only if there was
an error.  Thus, in the usual, non-error, case, they're as fast as using the
original calls.  The disadvantage is that they evaluate their arguments
twice if there's an error, so they may have side-effects.  For example,
you can safely do
	strcpy(malloc(length), string)
if lrqE is set appropriately, because malloc will never return 0.
It would be more readable to use lrqEmalloc (which is the same except when
lrqE is 0 or uindefined).
But you can't use the macros, because otherwise the malloc() gets done
twice if the strcpy fails...

Anyway, if you want them, they are called EMmalloc, etc. (the EM are for
Error Macro, to remind you every time you use them).

They could do things like
    #define open(a,b,c) (((r=open(_1=a,_2=b,_3=c))<0)?Eopen(_1,_2,_3):r)
using temporary variables, but that would be slower, defeating the object!

If anyone has any ideas on this...
