/* $Id$
 * errSymLib.c
 *      Author:          Marty Kraimer
 *      Date:            6-1-90
 *
 *      Experimental Physics and Industrial Control System (EPICS)
 *
 *      Copyright 1991, the Regents of the University of California,
 *      and the University of Chicago Board of Governors.
 *
 *      This software was produced under  U.S. Government contracts:
 *      (W-7405-ENG-36) at the Los Alamos National Laboratory,
 *      and (W-31-109-ENG-38) at Argonne National Laboratory.
 *
 *      Initial development by:
 *              The Controls and Automation Group (AT-8)
 *              Ground Test Accelerator
 *              Accelerator Technology Division
 *              Los Alamos National Laboratory
 *
 *      Co-developed with
 *              The Controls and Computing Group
 *              Accelerator Systems Division
 *              Advanced Photon Source
 *              Argonne National Laboratory
 *
 * Modification Log: errSymLib.c
 * -----------------
 * .01  mm-dd-yy        rcz     See below for Creation/Merge
 ****	Merged Modification Logs:
 * Modification Log: errMessage.c
 * -----------------
 * .01  10-08-91	mrk	Allow logMsg or printF
 * .02  03-10-93	joh	expanded errMessage() to accept the
 *				same format string and variable
 *				length arguments as printf()	
 *				and renamed it errPrintf()
 * .03  03-11-93        joh     added __FILE__ and __LINE__ to errPrintf()
 *				created macro errMessage() that calls
 *				errPrintf with __FILE__ and __LINE__
 *				as arguments
 * errMessage.c - Handle error messages 
 ***************************************************************************
 * This must ultimately be replaced by a facility that allows remote
 * nodes access to the error messages. A message handling communication
 * task should be written that allows multiple remote nodes to request
 * notification of all error messages.
 * For now lets just print messages and last errno via logMsg or printf
 ***************************************************************************
 * Modification Log: errPrint.c
 * -----------------
 * .01  10-08-91	mrk	Allow logMsg or printf
 * .02  04-29-93	joh 	extra arg for errPrint()	
 * .03  04-29-93	joh 	errPrint() became errPrintStatus()	
 * .04	05-06-93	joh	errPrintStatus() get var args 
 *				for vprintf()
 * .05 	05-02-94	joh	errToLogMsg now defaults to TRUE
 *
 * Modification Log: errSymLib.c
 * -----------------
 * .01  09-04-93        rcz     Merged errMessage.c, errPrint.c, errSymFind.c
 *			rcz	into one file (errSymLib.c) and changed method
 *			rcz	of errSymTable lookup.
 */

#include <ellLib.h>
#include <dbDefs.h>
#include <errMdef.h>
#include "errSymTbl.h"
#include <stdio.h>

#ifdef vxWorks
#include <vxWorks.h>
#include <taskLib.h>
#include <logLib.h>
#include <types.h>
#include <symLib.h>

extern SYMTAB_ID  statSymTbl;

#else
#include <string.h>
extern int errno;
extern int      sys_nerr;
extern char    *sys_errlist[];
#endif


#ifdef vxWorks
#define MYERRNO	(errnoGet()&0xffff)
#else
#define MYERRNO	errno
#endif

typedef struct errnumnode {
    ELLNODE             node;
    long                errNum;
    struct errnumnode *hashnode;
    char               *message;
    long                pad;
} ERRNUMNODE;
#define NHASH 256


static ELLLIST errnumlist;
static ERRNUMNODE **hashtable;
static int initialized = FALSE;
extern ERRSYMTAB_ID errSymTbl;


#ifdef vxWorks
int errToLogMsg = TRUE;
#endif

/*Declare storage for errVerbose( defined in errMdef.h)*/
int errVerbose=0;




/****************************************************************
 * ERRSYMBLD
 *
 * Create the normal ell LIST of sorted error messages nodes
 * Followed by linked hash lists - that link together those
 * ell nodes that have a common hash number.
 *
 ***************************************************************/
int errSymBld()
{
    ERRSYMBOL      *errArray = errSymTbl->symbols;
    ELLLIST        *perrnumlist = &errnumlist;
    ERRNUMNODE     *perrNumNode = NULL;
    ERRNUMNODE     *pNextNode = NULL;
    ERRNUMNODE    **phashnode = NULL;
    int             i;
    int             modnum;
    unsigned short  hashInd;

    if(initialized) return(0);
    hashtable = (ERRNUMNODE**)dbCalloc(NHASH, sizeof(ERRNUMNODE*));

    for (i = 0; i < errSymTbl->nsymbols; i++, errArray++) {
	modnum = errArray->errNum >> 16;
	if (modnum < 501) {
	    printf("errSymBld: ERROR - Module number in errSymTbl < 501\n");
	    return (-1);
	}
	if ((errSymbolAdd(errArray->errNum, errArray->name))  <0 ) {
	    printf("errSymBld: ERROR - errSymbolAdd() failed \n");
	    return (-1);
	}
    }
    perrNumNode = (ERRNUMNODE *) ellFirst(perrnumlist);
    while (perrNumNode) {
	/* hash each perrNumNode->errNum */
	hashInd = errhash(perrNumNode->errNum);
	phashnode = (ERRNUMNODE**)&hashtable[hashInd];
	pNextNode = (ERRNUMNODE*) *phashnode;
	/* search for last node (NULL) of hashnode linked list */
	while (pNextNode) {
	    phashnode = &pNextNode->hashnode;
	    pNextNode = *phashnode;
	}
	*phashnode = perrNumNode;
	perrNumNode = (ERRNUMNODE *) ellNext((ELLNODE *) perrNumNode);
    }
    initialized = TRUE;
    return(0);
}



/****************************************************************
 * HASH
 * returns the hash index of errNum
****************************************************************/
#ifdef __STDC__
static
unsigned short errhash(long errNum)
#else
static
unsigned short errhash(errNum)
long errNum;
#endif /* __STDC__ */
{
unsigned short modnum;
unsigned short errnum;

	modnum = errNum >> 16;
	errnum = errNum & 0xffff;
	return((unsigned short)(((modnum - 500) * 20) + errnum) % NHASH);
}

/****************************************************************
 * ERRMESSAGE - now a macro to call errPrintf
 * ERRPRINTF  - print an error symbol message
 ***************************************************************/
#ifdef __STDC__
void errPrintf(long status, char *pFileName, int lineno, char *pformat, ...)
#else
void errPrintf(va_alist)
va_dcl
#endif
{
    va_list 	   pvar;
    static char    *pReformat;
    static int	   reformatSize;
    static char	   pAdd[] = {'\n', '\0'};
#ifndef __STDC__
    long     	   status;
    char           *pformat;
    char	   *pFileName;
    int		   lineno;
#endif
#ifdef vxWorks
    int             id;
    static int      saveid = -1;
    char           *pname;
#endif

#ifdef __STDC__
    va_start(pvar, pformat);
#else
    va_start(pvar);
    status = va_arg(pvar, long);
    pFileName = va_arg(pvar, char *);
    lineno = va_arg(pvar, int);
    pformat = va_arg(pvar, char *);
#endif

#ifdef vxWorks
    if(!errToLogMsg) {
	id = taskIdSelf();
	if (saveid != id) {
	    saveid = id;
	    pname = taskName(id);
	    printf("taskid=%x taskname=%s ", id, pname);
	}
    }
#endif

    if(pFileName && errVerbose){
#ifdef vxWorks
      	if(errToLogMsg) {
        	logMsg("filename=\"%s\" line number=%d\n", pFileName, lineno,
		NULL, NULL, NULL, NULL);
      	}
      	else{
        	printf("filename=\"%s\" line number=%d\n", pFileName, lineno);
      	}
#else
      	printf("filename=\"%s\" line number=%d\n", pFileName, lineno);
#endif
    }

    if (pformat != NULL) {
	int		size;

	size = strlen(pformat)+NELEMENTS(pAdd);
	
	if(reformatSize < size){
		/*
		 * use a reasonable size string
		 */
		size = max(0xff, size);
		if(pReformat){
			free(pReformat);
		}

		pReformat = (char *) malloc(size);
		if(pReformat){
			reformatSize = size;
		}
		else{
#ifdef vxWorks
		    logMsg("%s: calloc error\n", __FILE__,
		    NULL, NULL, NULL, NULL, NULL);
#else
		    printf("%s: calloc error\n", __FILE__);
#endif
		    return;
		}
	}
	strcpy(pReformat, pformat);
	strcat(pReformat, pAdd);

    }

    verrPrintStatus(status, pReformat, pvar);

    return;
}

/****************************************************************
 * ERRPRINTSTATUS
 ***************************************************************/
#ifdef __STDC__
int errPrintStatus(long status, char *pFormat, ...)
#else
int errPrintStatus(va_alist)
va_dcl
#endif
{
	va_list        pvar;
#ifndef __STDC__
	long		status;
	char		*pFormat;
#endif

#ifdef __STDC__
	va_start(pvar, pFormat);
#else
	va_start(pvar);
	status = va_arg(pvar, long);
	pFormat = va_arg(pvar, char *);
#endif

	return verrPrintStatus(status, pFormat, pvar);
}

/****************************************************************
 * VERRPRINTSTATUS
 ***************************************************************/
#ifdef __STDC__
int verrPrintStatus(long status, char *pFormatString, va_list pvar)
#else
int verrPrintStatus(status, pFormatString, pvar)
long 	status;
char	*pFormatString;
va_list	pvar;
#endif
{
	static char	ctxToLarge[] = "** Context String Overflow **";
	char 		name[256];
	int 		rtnval;
	int		namelen;
	int		formatlen;
	unsigned short modnum;
	unsigned short errnum;


	name[0] = '\0';
	if(status==0) status = MYERRNO;
	if(status >= -1){
		rtnval = 0;
	}
	else {
		rtnval = status;
	}
	if(status>0) {
	    rtnval = errSymFind(status,name);
	    modnum = status >> 16;
	    errnum = status & 0xffff;
	    if(rtnval) {
		sprintf(name, 
			"Error status (module %hu, number %hu) not in symbol table", 
			modnum, errnum);
	    }
	}
	if(pFormatString){
		namelen = strlen(name);
		formatlen = strlen(pFormatString);
		strcat(name," ");
		if(sizeof(name)-namelen-1 > formatlen){
			strcat(name, pFormatString);
		}
		else if(sizeof(name)-namelen-1 > sizeof(ctxToLarge)){
			strcat(name, ctxToLarge);
		}
		else{
			fprintf(stderr,ctxToLarge);
		}
	}

#ifdef vxWorks
	if(errToLogMsg){
		int     i;
		int     logMsgArgs[6];

		for(i=0; i< NELEMENTS(logMsgArgs); i++){
			logMsgArgs[i] = va_arg(pvar, int);
		}

		logMsg(
			name,
			logMsgArgs[0],
			logMsgArgs[1],
			logMsgArgs[2],
			logMsgArgs[3],
			logMsgArgs[4],
			logMsgArgs[5]);
	}
	else{
		vprintf(name, pvar);
	}
#else
	vprintf(name, pvar);
#endif

	return rtnval;
}

/****************************************************************
 * ERRSYMBOLADD
 * adds symbols to the master errnumlist as compiled from errSymTbl.c
 ***************************************************************/
#ifdef __STDC__
int errSymbolAdd (long errNum,char *name)
#else
int errSymbolAdd (errNum,name)
long errNum;
char *name;
#endif /* __STDC__ */
{
    ELLLIST        *perrnumlist = &errnumlist;
    ERRNUMNODE     *pNew;

    pNew = (ERRNUMNODE*)dbCalloc(1, sizeof(ERRNUMNODE));
    pNew->errNum = errNum;
    pNew->message = name;
    ellAdd(perrnumlist,(ELLNODE*)pNew);
    return(0);
}

/****************************************************************
 * UNIXSYMFIND
 ***************************************************************/
#ifndef vxWorks
#ifdef __STDC__
int UnixSymFind(long status, char *pname, long *pvalue)
#else
int UnixSymFind(status, pname, pvalue)
    long            status;
    char           *pname;
    long           *pvalue;
#endif /* __STDC__ */
{
    if (status >= sys_nerr || status < 1) {
	*pvalue = -1;
	return;
    }
    strcpy(pname, sys_errlist[status]);
    *pvalue = status;
    return;
}
#endif

/****************************************************************
 * MODSYMFIND
 ***************************************************************/
#ifdef __STDC__
int ModSymFind(long status, char *pname, long *pvalue)
#else
int ModSymFind(status, pname, pvalue)
    long            status;
    char           *pname;
    long           *pvalue;
#endif /* __STDC__ */
{
    unsigned short  modNum;
    unsigned short  hashInd;
    ERRNUMNODE *pNextNode;
    ERRNUMNODE **phashnode = NULL;

    modNum = (status >> 16);
    if (modNum < 501) {
	*pvalue = -1;
	return;
    }
    hashInd = errhash(status);
    phashnode = (ERRNUMNODE**)&hashtable[hashInd];
    pNextNode = *phashnode;
    while (pNextNode) {
        if (pNextNode->errNum == status) {
    	    strcpy(pname, pNextNode->message);
    	    *pvalue = status;
    	    return;
        }
	phashnode = &pNextNode->hashnode;
	pNextNode = *phashnode;
    }
    *pname  = 0;
    *pvalue = -1;
    return ;
}

/****************************************************************
 * ERRSYMFIND
 ***************************************************************/
#ifdef __STDC__
int errSymFind(long status, char *name)
#else
/* errSymFind - Locate error symbol */
int errSymFind(status, name)
    long            status;
    char           *name;
#endif /* __STDC__ */
{
    long            value;
#ifdef vxWorks
    unsigned char   type;
#endif
    unsigned short  modnum;

    if (!initialized) {
	errSymBld();
    }

    modnum = (status >> 16);
    if (modnum <= 500)
#ifdef vxWorks
	symFindByValue((SYMTAB_ID)statSymTbl, status, name,(int*) &value, (SYM_TYPE*)&type);
#else
	UnixSymFind(status, name, &value);
#endif
    else
	ModSymFind(status, name, &value);
    if (value != status)
	return (-1);
    else
	return (0);
}


/****************************************************************
 * errSymDump
 ***************************************************************/
#ifdef __STDC__
void errSymDump()
#else
void errSymDump()
#endif /* __STDC__ */
{
ERRNUMNODE    **phashnode = NULL;
ERRNUMNODE     *pNextNode;
int i;
int modnum;
int errnum;
int msgcount;
int firstTime;

	if (!initialized)
	    errSymBld();

	msgcount = 0;
	printf("errSymDump: number of hash slots=%d\n", NHASH);		
	for ( i=0; i < NHASH; i++) {
	    phashnode = &hashtable[i];
	    pNextNode = *phashnode;
	    firstTime=1;
		while (pNextNode) {
		    if (firstTime) {
		        printf("HASHNODE=%d\n", i);		
		        firstTime=0;
		    }
		    modnum = pNextNode->errNum >> 16;
		    errnum = pNextNode->errNum & 0xffff;
		    printf("\tmod %d num %d \"%s\"\n"
			, modnum , errnum , pNextNode->message);
		    msgcount++;
		    phashnode = &pNextNode->hashnode;
		    pNextNode = *phashnode;
		}
	}
	printf("\nerrSymDump: total number of error messages=%d\n", msgcount);		
}


/****************************************************************
 * errSymTestPrint
 ***************************************************************/
#ifdef __STDC__
void errSymTestPrint(long errNum)
#else
void errSymTestPrint(errNum)
long errNum;
#endif /* __STDC__ */
{
    char            message[256];
    unsigned short modnum;
    unsigned short errnum;

    if (!initialized)
	errSymBld();

    message[0] = '\0';
    modnum = errNum >> 16;
    errnum = errNum & 0xffff;
    if (modnum < 501) {
        printf("Usage:  errSymTestPrint(long errNum) \n");
        printf("errSymTestPrint: module number < 501 \n");
        return;
    }
    errSymFind(errNum, message);
    if ( message[0] == '\0' ) return;
    printf("module %hu number %hu message=\"%s\"\n",
		modnum, errnum, message);
    return;
}

/****************************************************************
 * ERRSYMTEST
****************************************************************/
#ifdef __STDC__
void errSymTest(unsigned short modnum, unsigned short begErrNum, unsigned short endErrNum)
#else
void errSymTest(modnum, begErrNum, endErrNum)
unsigned short modnum;
unsigned short begErrNum;
unsigned short endErrNum;
#endif /* __STDC__ */
{
    long            errNum;
    unsigned short  errnum;
    if (modnum < 501)
	return;

    /* print range of error messages */
    for (errnum = begErrNum; errnum <= endErrNum; errnum++) {
	errNum = modnum << 16;
	errNum |= (errnum & 0xffff);
	errSymTestPrint(errNum);
    }
}
