/*
 * $RCSfile: prottbl.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Log: prottbl.c,v $
 * Revision 1.2  1996/01/29 19:56:14  slm
 * Updated copyright and version.
 *
 * Revision 1.1  1995/07/28  21:41:44  slm
 * Initial revision
 *
 * Revision 1.42.1.1.1.4  1994/12/02  17:55:42  hkaram
 * Changed the mapResolve interface
 *
 * Revision 1.42.1.1.1.3  1994/11/22  20:53:25  hkaram
 * Added casts to MapResolve calls
 *
 * Revision 1.42.1.1.1.2  1994/10/27  20:54:49  hkaram
 * Minor cast fix
 *
 * Revision 1.42.1.1  1994/04/19  00:25:44  menze
 * prottbl functions now use 32-bit ProtId
 *
 * Revision 1.42  1994/04/15  21:12:11  davidm
 * (backPatchBinding,dispHlp): relNum now cast to "long" instead of "int"
 * to make it work with 64 bit architectures.
 *
 * Revision 1.41  1994/03/12  22:18:30  davidm
 * Fixed MULT_PROT_MSG and NUM_BOUND_MSG to print longs with "%ld" format.
 *
 * Revision 1.40  1994/02/05  00:08:18  menze
 *   [ 1994/01/28          menze ]
 *   assert.h renamed to xk_assert.h
 *
 * Revision 1.39  1993/12/15  23:32:33  menze
 * Modifications from UMass:
 *
 *   [ 93/07/27          yates ]
 *     added relProtNumById(hlpId, llp)
 *
 * Revision 1.38  1993/12/11  00:23:44  menze
 * fixed #endif comments
 *
 * Revision 1.37  1993/12/10  20:39:20  menze
 * Added unique_hlps ROM option
 *
 * Cleaned up initialization interaction with prottbl_parse.
 * prottbl_init and ROM parsing now live here.
 *
 * Revision 1.36  1993/12/10  18:15:34  menze
 * A protocol can now have multiple protocols bound to the same hlpNum
 */

/* Management of the "protocol id / relative number" table */

#ifndef XKMACHKERNEL
#include "x_stdio.h"
#include "x_libc.h"
#endif /* ! XKMACHKERNEL */
#include "prottbl.h"
#include "prottbl_i.h"
/* #include "idmap.h" */
#include "xk_debug.h"
#include "xk_assert.h"
#include "compose.h"
#include "x_util.h"
#include "romopt.h"

typedef PtblEntry Entry;

int traceptbl;

#define PTBL_MAP_SIZE  101
#define PTBL_NAME_MAP_SIZE 101
#define PTBL_ID_MAP_SIZE 101
#define PTBL_HLP_MAP_SIZE 10

#ifdef __STDC__

static int      backPatchBinding(void *, void * , void *);
static int      checkEntry(void *, void *, void *);
static XkReturn configFileOpt(char **, int, int, VOID *);
static void     error(char *);
static void     mkKey(char *, char *);
static XkReturn uniqueHlpNumOpt(char **, int, int, VOID *);

#ifdef XK_DEBUG

static int dispHlp(void *, void *, void *);
static int dispEntry(void *, void *, void *);

#endif /* XK_DEBUG */

#else

char * protIdToStr();

static int      backPatchBinding();
static int      checkEntry();
static XkReturn configfileOpt();
static void     error();
static void     mkKey();
static XkReturn uniqueHlpNumOpt();

#ifdef XK_DEBUG

static int dispHlp();
static int dispEntry();

#endif /* XK_DEBUG */

#endif /* __STDC__ */

Map ptblNameMap = 0;	/* strings to Entry structures */
static Map  idMap = 0;
static int  errorOccurred;
static int  ptblMapIsEmpty = 1;
static int  uniqueHlpNums = 0;
static char *romTables[100];

static RomOpt romOptions[] = {
    { "unique_hlps", 3, uniqueHlpNumOpt },
    { "", 2, configFileOpt }
};

#define MAX_PROT_NAME 16
/* 
 * Error messages
 */
#define MULT_PROT_MSG "prot table: multiple protocols with id %ld declared"
#define NO_ID_MSG     "prot table: %s (declared as hlp to %s) has no id number"
#define NAME_REPEATED_MSG \
	"prot table: protocol %s is declared with different id's"
#define BIND_REPEATED_MSG \
	"prot table: binding for %s above %s defined multiple times"
#define NO_ENTRY_MSG  "prot table: No entry exists for llp %s"
#define NUM_BOUND_MSG \
	"prot table: relative number %ld is already bound for llp %s"

static void
error(msg)
char *msg;
{
    xError(msg);
    errorOccurred = 1;
}

static void
mkKey(key, name)
char *key;
char *name;
{
    bzero(key, MAX_PROT_NAME);
    strncpy(key, name, MAX_PROT_NAME);
}

/* 
 * This function is called for each entry in a lower protocol's temp map
 * (binding higher protocol name strings to relative numbers).  We find the
 * hlp's id number and bind it to the relative number, signalling an error
 * if we can't find the hlp's id number.
 */
static int
backPatchBinding(key, relNum, arg)
VOID *key;
VOID *relNum;
VOID *arg;
{
    Entry *llpEntry = (Entry *)arg;
    Entry *hlpEntry;
    
    if (mapResolve(ptblNameMap, (char *)key, (void **)&hlpEntry) == XK_FAILURE){
	sprintf(errBuf, NO_ID_MSG, (char *)key, llpEntry->name);
	error(errBuf);
    }
    else {
	xTrace3(ptbl, TR_MAJOR_EVENTS,
		"Backpatching binding of %s->%s using %ld",
		hlpEntry->name, llpEntry->name, relNum);
	mapBind(llpEntry->idMap, &hlpEntry->id, relNum);
    }
    return MFE_CONTINUE;
}

/* 
 * This function is called for each entry (lower protocol) in the name map.
 * If there are entries in the llp's tmpMap (higher protocols which were
 * bound with a string instead of an id), we backpatch the id binding.
 */
static int
checkEntry(key, value, arg)
VOID *key;
VOID *value;
VOID *arg;
{
    Entry *llpEntry = (Entry *)value;

    /* 
     * If there is anything in this entry's tmpMap, transfer it to the idMap.
     * If we don't know the id number of the hlp in the tmpMap, signal an error.
     */
    if (llpEntry->tmpMap) {
	mapForEach(llpEntry->tmpMap, backPatchBinding, llpEntry);
	mapClose(llpEntry->tmpMap);
	llpEntry->tmpMap = mapCreate(PTBL_MAP_SIZE, MAX_PROT_NAME);
    }
    return MFE_CONTINUE;
}

int
protTblBuild(filename)
char *filename;
{
    xTrace1(ptbl, TR_GROSS_EVENTS, "protTblBuild( %s )", filename);
    xAssert(ptblNameMap);
    if ((protTblParse(filename)) != 0 || errorOccurred) {
        xTrace1(ptbl, TR_ERRORS,
		"protTblBuild: problems building input file %s", filename);
	return -1;
    }
    xTrace1(ptbl, TR_GROSS_EVENTS,
	    "protTblBuild( %s ) checking map consistency", filename);

    /* Check the consistency of the protocol map. */
    mapForEach(ptblNameMap, checkEntry, 0);
    xIfTrace(ptbl, TR_ERRORS) {
      if (errorOccurred)
	  printf("prottbl: protTblBuild: Consistency error in file %s",
		 filename);
    }
    return errorOccurred;
}

ProtId
protTblGetId(protocolName)
char *protocolName;
{
    Entry *e;
    char  key[MAX_PROT_NAME];
    
    xAssert(ptblNameMap);
    mkKey(key, protocolName);
    if (mapResolve(ptblNameMap, key, (void **)&e) == XK_FAILURE) {
        xTrace0(ptbl, TR_ERRORS, "protTblGetId failed to find key");
	if (ptblMapIsEmpty)
	    xError("No protocol tables have been loaded");
	return -1;
    }
    return e->id;
}

char *
protIdToStr(id)
ProtId id;
{
    Entry *e;

    if (mapResolve(idMap, &id, (void **)&e) == XK_FAILURE)
	return 0;
    return e->name;
}

ProtId
relProtNum(hlp, llp)
Protl hlp;
Protl llp;
{
    Entry *llpEntry;
    ProtId res;
    
    xAssert(ptblNameMap);
    if (!xIsProtl(hlp) || !xIsProtl(llp)) {
        xIfTrace(ptbl, TR_ERRORS) {
            if (!xIsProtl(hlp))
                printf("prottbl: relProtNum: hlp is not a protocol\n");
            if (!xIsProtl(llp))
                printf("prottbl: relProtNum: llp is not a protocol\n");
      }
      return -1;
    }
    if (mapResolve(idMap, &llp->id, (void **)&llpEntry) == XK_FAILURE) {
        xTrace1(ptbl, TR_ERRORS,
	        "prottbl: relProtNum: Cannot find llp id %d in protocol table",
	        llp->id);
        return -1;
    }
    if (llpEntry->idMap == 0) {
	/* 
	 * The lower protocol does not use relative naming.  Return the
	 * absolute id of the upper protocol.
	 */
        xTrace1(ptbl, TR_EVENTS,
	     "prottbl: relProtNum: Returning absolute id of upper protocol, %d",
	        hlp->id);
        return hlp->id;
    }
    if (mapResolve(llpEntry->idMap, &hlp->id, (void **)&res) == XK_FAILURE) {
	res = -1;
    }
    xTrace1(ptbl, TR_EVENTS, "prottbl: relProtNum: Returning %d", res);
    return res;
}

ProtId
relProtNumById(hlpId, llp)
ProtId hlpId;
Protl  llp;
{
    Entry *llpEntry;
    ProtId res;
    
    xAssert(ptblNameMap);
    if (hlpId < 0) {
        xTrace1(ptbl, TR_ERRORS,
		"prottbl: relProtNumById: hlp id (%d) is invalid", hlpId);
        return -1;
    }
    if (!xIsProtl(llp)) {
        xTrace0(ptbl, TR_ERRORS,
		"prottbl: relProtNumById: llp is not a protocol");
	return -1;
    }
    if (mapResolve(idMap, &llp->id, (void **)&llpEntry) == XK_FAILURE) {
        xTrace1(ptbl, TR_ERRORS,
	     "prottbl: relProtNumById: Cannot find llp id %d in protocol table",
		llp->id);
        return -1;
    }
    if (llpEntry->idMap == 0) {
	/* 
	 * The lower protocol does not use relative naming.  Return the
	 * absolute id of the upper protocol.
	 */
        xTrace1(ptbl, TR_EVENTS,
	 "prottbl: relProtNumById: Returning absolute id of upper protocol, %d",
		hlpId);
        return hlpId;
    }
    if (mapResolve(llpEntry->idMap, &hlpId, (void **)&res) == XK_FAILURE)
	res = -1;
    xTrace1(ptbl, TR_EVENTS, "prottbl: relProtNumById: Returning %d", res);
    return res;
}

void
protTblAddProt(name, id)
char *name;
ProtId id;
{
    Entry *e;
    char key[MAX_PROT_NAME];
    
    xTrace2(ptbl, TR_MAJOR_EVENTS, "protTblAddProt adding %s(%d)", name, id);
    ptblMapIsEmpty = 0;
    mkKey(key, name);
    if (mapResolve(ptblNameMap, key, (void **)&e) == XK_FAILURE) {
	/* Entry does not exist for this name */
	if (mapResolve(idMap, &id, 0) == XK_SUCCESS) {
	    sprintf(errBuf, MULT_PROT_MSG, id);
	    error(errBuf);
	    return;
	}
	e = (Entry *)xMalloc(sizeof(Entry));
	e->name = xMalloc(strlen(name) + 1);
	strcpy(e->name, name);
	e->id = id;
	e->idMap = e->tmpMap = 0;
	mapBind(ptblNameMap, key, e);
	mapBind(idMap, &e->id, e);
    } else {
	/* 
	 * Make sure that this declaration has the same id number as the
	 * previous declarations.
	 */
	if (e->id != id) {
	    sprintf(errBuf, NAME_REPEATED_MSG, name);
	    error(errBuf);
	}
    }
}

void
protTblAddBinding(llpName, hlpName, relativeNum)
char *llpName;
char *hlpName;
ProtId relativeNum;
{
    Entry *llpEntry, *hlpEntry;
    char key[MAX_PROT_NAME];
    long        relNum = relativeNum;
    
    xTrace3(ptbl, TR_MAJOR_EVENTS, "protTblAddBinding adding %s->%s uses %d",
            hlpName, llpName, relNum);
    mkKey(key, llpName);
    if (mapResolve(ptblNameMap, key, (void **)&llpEntry) == XK_FAILURE) {
	/* No entry exists for the lower protocol */
	sprintf(errBuf, NO_ENTRY_MSG, llpName);
	error(errBuf);
	return;
    }
    if (llpEntry->idMap == 0) {
	llpEntry->idMap = mapCreate(PTBL_HLP_MAP_SIZE, sizeof(ProtId));
	llpEntry->tmpMap = mapCreate(PTBL_HLP_MAP_SIZE, MAX_PROT_NAME);
	if (uniqueHlpNums)
	    llpEntry->revMap = mapCreate(PTBL_HLP_MAP_SIZE, sizeof(ProtId));
    }
    if (uniqueHlpNums) {
	if (mapResolve(llpEntry->revMap, &relNum, 0) == XK_SUCCESS) {
	    sprintf(errBuf, NUM_BOUND_MSG, relNum, llpName);
	    error(errBuf);
	    return;
	}
    }
    mkKey(key, hlpName);
    if (mapResolve(ptblNameMap, key, (void **)&hlpEntry) == XK_FAILURE) {
	/* 
	 * Entry for hlp doesn't exist yet.  Add the binding from the
	 * hlp name to the number in tmpMap.  The binding will be
	 * tranferred to idMap after the hlp entry has been added.
	 */
	if (mapResolve(llpEntry->tmpMap, key, 0) == XK_FAILURE) {
	    mapBind(llpEntry->tmpMap, key, relNum);
	    if (uniqueHlpNums)
		mapBind(llpEntry->revMap, &relNum, 0);
	}
	else {
	    sprintf(errBuf, BIND_REPEATED_MSG, hlpName, llpName);
	    error(errBuf);
	}
    }
    else {
	/* 
	 * Add the entry directly to the idMap
	 */
	if (mapResolve(llpEntry->idMap, &hlpEntry->id, 0) == XK_FAILURE) {
	    mapBind(llpEntry->idMap, &hlpEntry->id, relNum);
	    if (uniqueHlpNums)
		mapBind(llpEntry->revMap, &relNum, 0);
	}
	else {
	    sprintf(errBuf, BIND_REPEATED_MSG, hlpName, llpName);
	    error(errBuf);
	}
    }
}

static XkReturn
uniqueHlpNumOpt(str, nFields, ln, arg)
char **str;
int  nFields;
int  ln;
VOID *arg;
{
    if (! strcmp(str[2], "on")) {
	uniqueHlpNums = 1;
	return XK_SUCCESS;
    }
    if (! strcmp(str[2], "off")) {
	uniqueHlpNums = 0;
	return XK_SUCCESS;
    }
    return XK_FAILURE;
}

static XkReturn
configFileOpt(str, nFields, ln, arg)
char **str;
int  nFields;
int  ln;
VOID *arg;
{
    static int i = 0;

    if (i >= sizeof(romTables))
        xError("Too many protocol tables in ROM file");
    else
	romTables[i++] = str[1];
    return XK_SUCCESS;
}

void
prottbl_init()
{
    static int initialized;
    char **tables;

    if (initialized)
	return;
    initialized = 1;
    romTables[0] = 0;
    findRomOpts("prottbl", romOptions, sizeof(romOptions)/sizeof(RomOpt), 0);
    ptblNameMap = mapCreate(PTBL_NAME_MAP_SIZE, MAX_PROT_NAME);
    idMap = mapCreate(PTBL_ID_MAP_SIZE, sizeof(ProtId));
    /* 
     * Config files named in compose files
     */
    for (tables = protocolTables; *tables != 0; tables++) {
	if (protTblBuild(*tables)) {
	    sprintf(errBuf, "error building protocol table %s", *tables);
	    xError(errBuf);
	}
    }
    /* 
     * Config files named in ROM files
     */
    for (tables = romTables; *tables != 0; tables++) {
	if (protTblBuild(*tables)) {
	    sprintf(errBuf, "error building protocol table %s", *tables);
	    xError(errBuf);
	}
    }
}

#ifdef XK_DEBUG

static int
dispHlp(key, relNum, arg)
VOID *key;
VOID *relNum;
VOID *arg;
{
    xTrace2(ptbl, TR_ALWAYS, "      hlp id %d uses rel num %ld",
	    *(ProtId *)key, relNum);
    return MFE_CONTINUE;
}

static int
dispEntry(key, value, arg)
VOID *key;
VOID *value;
VOID *arg;
{
    Entry *e = (Entry *)value;

    xTrace2(ptbl, TR_ALWAYS, "%s (id %d)", e->name, e->id);
    if (e->idMap) {
	xTrace0(ptbl, TR_ALWAYS, "  upper protocols:");
	mapForEach(e->idMap, dispHlp, 0);
    }
    return 1;
}

void
protTblDisplayMap()
{
    xTrace0(ptbl, TR_ALWAYS, "protocol table:");
    mapForEach(ptblNameMap, dispEntry, 0);
}

#endif /* XK_DEBUG */
