/*		Error Message Registration and Decoding		 error_message.c
**		=======================================
**
**  This module emulates the VAX/VMS error message reporting system to a
**  limited extent.  The philosophy is that any error generated is tagged
**  with a facility code and a severity. The severity allows modules which
**  do not know how to interpret the code completely to decide what to do about it.
**  The facility number allows it to be decoded when eventually it is returned
**  to a high level.
**
**  The format of the error code is
**
**2^	31				15			       0
**	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
**	|Res-   |N|	Facility	|F|    Message number	  | Sev	|
**      | erved	|D|			|W|			  |     |
**	+-------+-------+-------+-------+-------+-------+-------+-------+
**
**  Reserved:	Include a few flags used by other systems.
**  ND:		This bit is set to indicate a Non-Digital Facility number
**  Facility:	These bits give a facility number.
**  FW:		If this bit is set, message is facility wide,
**		message number must be interprested w.r.t that facility.
**		If bit clear, message number refers to a standard set (facility
**		zero).
**  Message No	This is a message number - See FW for interpretation.
**
**  Sev		This is  the severity according to the list:
**
**	    0	Warning	    Operation completed not fully as expected.
**	    1	Success	    Operation completed as expected.
**	    2	Error	    Operation failed, other operations may be possible.
**	    3	Information Error code reports information only.
**	    4	Fatal	    Operation failed, others will probably fail too.
**
**
**  Any facility may generate a block of message information, and attach it
**  to this lookup service by calling err_register().  The program "msgstring"
**  automatically generates such a block, and the call to initialise it,
**  using data in the listfile produced by the VAX/MESSAGE utility.
**
**  Under unix, and compatible systems, the internal "errno" codes are given
**  a special facility code.
**  This module will then interpret them using the built-in system tables.
**
**  Under VMS, these error messages are generated un VMS style, e.g.:
**
**	on vms:		%RMS-E-FILNOTFOU, File not found
**
**	elsewhere:	RMS Error FILNOTFOU: File not found
**_____________________________________________________________________________
**
**  VAX/VMS, VAX/MESSAGE, and Digital are trademarks of Digital Equipment, Inc.
**
**  This code is copyright CERN -- implies free distribution if not for profit
**  to certain institutes under certain terms - ask for details.
**
**
** HISTORY:
**   5 Dec 89	Written Tim Berners-Lee CERN/DD
**
** PREPROCESSOR TOKENS:
**
**  USE_SYS_ERRLIST	If defined, uses sys_errlist for errno values.
*/

#ifdef unix
# define USE_SYS_ERRLIST
#endif

#ifdef __TURBOC__
# define USE_SYS_ERRLIST
#endif

#define CRITICAL		/* Code to protect if err_root shared */
#define NONCRITICAL		/* End of protected region */

#ifdef OSK
# include <strings.h>
#else
# include <string.h>		/* ANSI name. <strings.h> is obsolete */
#endif

#include "errmsg.h"

/*	Constant Data
**	-------------
*/

static char *severity_name[8] = {
#ifdef vms
    "W", "S", "E", "I", "F", "?", "?", "?" }; /* Severity codes */
#else
    "Warning", "Success", "Error", "Information", "Fatal",
    "(severity 5?)", "(severity 6?)", "(severity 7?)" }; /* Severity codes */
#endif

static struct err_block_struct default_facility =
    {	0,	    /* next */
	0,	    /* Facility number */
	"SYS",	    /* Prefix	*/
	"NONAME",   /* Facility name */
	0	    /* Pointer to array	*/
    };	/* struct */

#ifdef USE_SYS_ERRLIST
#define UNIX_FACILITY 0x55	/* Facility number for errno values */
static struct err_block_struct errno_facility =
    {	0,	    /* next */
	0x55,	    /* Facility number */
	"E",	    /* Prefix	*/
	"errno",    /* Facility name */
	0	    /* Pointer to array	*/
    };	/* struct */

extern char* sys_errlist[]; /* System error message table */
extern int sys_nerr;	    /* Number of entries in sys_errlist */
#endif

/*	Variable Data
**	-------------
**
**  Global for disgnostics only.
*/

err_pointer err_root = 0;	/* Warning: Not ROMable */


/*	Translate Error Code Into String			err_translate()
**	================================
**
**  On entry,
**
**	error_code	is the 32 bit standard error code
**	buffer		is the address of the user buffer
**	buffer_length	is the length of the buffer in bytes, including space
**			for a terminating zero.
**
**  On succesful exit,
**
**	returns		A status with severity Success
**	buffer[]	Contains the error message, zero terminated
**
**  If the buffer was too small,
**
**	returns		A status with severity Warning
**	buffer[]	Contains the error message trucated and zero terminated.
**
**  If the code could not be translated,
**
**	returns		A status with severity Error
**	buffer[]	Contains a default error message truncated if necessary
*/
#ifdef __STDC__
err_status err_translate(
    err_code	error_code,
    char	*buffer,
    int		buffer_length)
#else
err_status err_translate(error_code, buffer, buffer_length)
    err_code	error_code;
    char	*buffer;
    int		buffer_length;
#endif

{
    int facility = (error_code >> 16) & 0x7ff;    /* Get facility code  */
    int code     = (error_code >> 3)  & 0xfff;    /* Get message number */
    int severity = error_code & 7;		  /* Get severity	*/
    char	   temp[256];
    err_translation	    *found_translation = 0;			  /* Points to match if found */
    err_pointer	    found_facility =0;		  /* Points to facility */
    err_pointer	scan;
    int reference_facility;

#ifdef USE_SYS_ERRLIST
    static err_translation dynamic_translation	/* for filling in by hand */
	= { 0, "ERRNO", 0 };
#endif

/*  The "Facility wide but means the reference facility is the actual
**  facility: Otherwise, the facility refers to facility 0.
*/
    reference_facility = (error_code & 0x8000) ? facility : 0;

#ifdef USE_SYS_ERRLIST
    if (facility == UNIX_FACILITY)
	found_facility = &errno_facility;

    if (reference_facility == UNIX_FACILITY) {
	if (code < sys_nerr) {
	    found_translation = &dynamic_translation;
	    dynamic_translation.message = sys_errlist[code];
	}
    }
#endif

    for (scan=err_root; (scan) && (!found_facility || !found_translation);
						    scan = scan->next) {
	if (scan->facility_number == facility)
	    found_facility = scan;

	if (scan->facility_number == reference_facility) {
	    int	msg;
	    for (msg=0; scan->array[msg].message; msg++) {
		if (scan->array[msg].code == code) {
		    found_translation = &scan->array[msg];
		    break;
		} /* If code match */
	    } /* for each code*/
	} /*if facility match */
    } /* for each registered facility */

    if (!found_facility)
	found_facility = &default_facility;

#ifdef vms
    if (found_translation) {
	sprintf(temp, "%%%s-%s-%s, %s",      /* Two % give one on the screen*/
	    found_facility->name,
	    severity_name[severity],
	    found_translation->symbol,
	    found_translation->message);
    } else {
	sprintf(temp, "%%%s-%s-NONAME: Unknown error code, hex %lx",
	    found_facility->name,
	    severity_name[severity],
	    error_code);
    } /* if no translation */
#else
    if (found_translation) {
	sprintf(temp, "%s %s %s: %s",
	    found_facility->name,
	    severity_name[severity],
	    found_translation->symbol,
	    found_translation->message);
    } else {
	sprintf(temp, "Unknown %s error code: hex %lx",
	    severity_name[severity],
	    error_code);
    } /* if no translation */
#endif
    if (buffer_length < strlen(temp)) {
	strncpy(buffer, temp, buffer_length);	/* Fill users's buffer */
	buffer[buffer_length-1] = 0;		/* Terminate user's buffer */
	return found_translation
	    ?	ERR_S_BUFFER_OVERFLOW		/* Buffer Overflow */
	    :	ERR_S_NO_TRANSLATION;		/* Overflow on default message*/
    } else {
	strcpy(buffer, temp);			/* Return user's string */
	return found_translation
	    ?	ERR_S_NORMAL			/* Buffer Overflow */
	    :	ERR_S_NO_TRANSLATION;		/* Overflow on default message*/
    }

} /* error_message */

/*	Register Error Messages					  err_register()
**	=======================
** 
** On entry,
**	pblock	    points to a valid struct err_block_struct which may
**		    or may not be already registerd.
**
** On exit,
**	The facility has been incorporated in the queue.
**
**	returns	    ERR_S_ALREADY_REGISTERED	if it was already
**		    ERR_S_NORMAL		if it has just been
*/

#ifdef __STDC__
err_status err_register(err_pointer pblock)
#else
err_status err_register(pblock)
    err_pointer	pblock;
#endif
{
    err_pointer scan;

/*  For safety, we check whether it's on the list already
*/
    for (scan=err_root; scan; scan = scan->next)
	if (scan==pblock)
	    return ERR_S_ALREADY_REGISTERED;

    CRITICAL
    pblock->next = err_root;	    /* Put onto head of list */
    err_root = pblock;
    NONCRITICAL

    return ERR_S_NORMAL;    
}

/*	Unregister Error Messages				err_unregister()
**	=========================
**
**  This reverses the effect of err_register.  It may be called more than
**  once with no ill effect.
**
** On entry,
**	pblock	    points to a valid struct err_block_struct.
**
** On exit,
**	The facility has been removed from the queue if it had been registered.
**
**	returns	    ERR_S_NORMAL if it has been removed
**		    ERR_S_NOT_REGISTERED if it never was registered
**
*/

#ifdef __STDC__
err_status err_unregister(err_pointer pblock)
#else
err_status err_unregister(pblock)
    err_pointer	pblock;
#endif
{
    err_pointer scan, last;

    CRITICAL
    for (last=(err_pointer)(&err_root),
	    scan=last->next; scan; last=scan, scan=last->next)
	if (scan==pblock) {
	    last->next = scan->next;	/* Remove from queue */
	    NONCRITICAL
	    return ERR_S_NORMAL;
	}
    NONCRITICAL
    return ERR_S_NOT_REGISTERED;	/* Wasn't on it. */
}
