/*
 * $RCSfile: prottbl_parse.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Log: prottbl_parse.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.21.1.3.1.1  1994/10/27  20:52:11  hkaram
 * New branch
 *
 * Revision 1.21.1.3  1994/07/28  01:33:51  ho
 * Fixed longstanding problem parsing hex constants.
 *
 * Revision 1.21.1.2  1994/07/27  21:23:54  ho
 * Fixed scanf of hex int's.
 *
 * Revision 1.21.1.1  1994/04/19  00:25:01  menze
 * prottbl functions now use 32-bit ProtId
 *
 * Revision 1.21  1994/04/14  00:17:12  davidm
 * (id): sscanf() of long variable "n" used formats %x/%d instead of
 * 	%lx/%ld.
 *
 * Revision 1.20  1993/12/15  23:34:02  menze
 * Fixed forceChar() to compile with strict ANSI restrictions
 *
 * Revision 1.19  1993/12/11  00:23:46  menze
 * fixed #endif comments
 *
 * Revision 1.18  1993/12/10  20:41:41  menze
 * Cleaned up initialization interaction with prottbl.c.
 * prottbl_init and ROM parsing now live there.
 */

/*
 *  Parser for the protocol table file.
 *
 *  Syntax (nonterminals capitalized):
 *
 * 	Entry -> Name  Id   OptionalList
 * 	OptionalList -> { L } | null
 * 	List -> Name Id List | null
 * 	Name -> string
 * 	Id -> integer
 *
 *  A '#' marks a comment until end of line
 */


#include <stdio.h>
#include "platform.h"
#include "xk_debug.h"
#include "prottbl.h"
#include "prottbl_i.h"
#include "x_stdio.h"
#include <ctype.h>
#include "compose.h"
#include "x_util.h"

#define BUFSIZE 80


#ifdef __STDC__

static void	entry( void );
static void	error( ErrorCode );
static int	forceChar( int );
static void	get( void );
static ProtId	id( void );
static int	list( char * );
static char *	name( char * );
static void	optionalList( char * );
static void	skipWhite( void );

#else

static void	entry();
static void	error();
static void	get();
static long	id();
static int	list();
static char *	name();
static void	optionalList();

#endif /* __STDC__ */


static int	line = 1;
static int	position = 0;
static ErrorCode	errorCode;
static int	nextChar;
static FILE	*inputFile;
static char	*fileName;



/* 
 * Top-level parsing routine.
 */
ErrorCode
protTblParse(f)
    char *f;
{
    fileName = f;
    errorCode = NO_ERROR;
    inputFile = fopen(fileName, "r");
    if ( inputFile == 0 ) {
	error(ERR_FILE);
	/* 
	 * An error return code will trigger 'parse error' messages
	 * which aren't really appropriate, so we'll just return
	 * success. 
	 */
    } else {
	get();
	while ( nextChar != EOF && ! errorCode ) {
	    entry();
	}
	fclose(inputFile);
    }
    return errorCode;
}



static void
get()
{
    do {
	nextChar = getc(inputFile);
	position++;
	if (nextChar == '#') {
	    while (nextChar != '\n' && nextChar != EOF) {
		nextChar = getc(inputFile);
		position++;
	    }
	}
	if (nextChar == '\n') {
	    line++;
	    position = 1;
	}
    } while ( nextChar == '#' || nextChar == '\n' );
}


static void
skipWhite()
{
    while ( isspace(nextChar) ) {
	get();
    }
}


/* 
 * consumes c from the input stream, consuming white space on either side.
 * Returns non-zero if c is not the next character.
 */
static int
forceChar( c )
    int	c;
{
    skipWhite();
    if ( nextChar != (char)c ) {
	return 1;
    }
    get();
    skipWhite();
    return 0;
}
  

static void
error(code)
    ErrorCode code;
{
    char 	*errMsg = "UNKNOWN";

    switch ( code ) {
      case ERR_FILE:
	sprintf(errBuf, "could not open protocol table file %s", fileName);
	xError(errBuf);
	return;

      case ERR_NAME:
	errMsg = "expected protocol name";
	break;
    
      case ERR_ID:
	errMsg = "expected id (integer)";
	break;

      case ERR_NAME_TOO_LARGE:
	errMsg = "string exceeded buffer size";
	break;

      case NO_ERROR:
	return;
    }
    sprintf(errBuf, "prot ID parse error: %s at line %d, pos %d\n",
		   errMsg, line, position);
    xError(errBuf);
    errorCode = code;
}


/* 
 * Entry -> Name  Id   OptionalList
 */
static void
entry()
{
    char	protName[BUFSIZE];
    ProtId	protId;

    xTrace0(ptbl, TR_DETAILED, "ptbl parse: entry called");
    if ( name(protName) == 0 ) {
	error(ERR_NAME);
	return;
    }
    if ( (protId = id()) == -1 ) {
	error(ERR_ID);
	return;
    }
    protTblAddProt(protName, protId);
    optionalList(protName);
    xTrace2(ptbl, TR_FUNCTIONAL_TRACE, "ptbl parse: entry == { name == %s, id == %d }",
	    protName, protId);
}


static void
optionalList(n)
    char *n;
{
    xTrace0(ptbl, TR_DETAILED, "ptbl parse: optionalList called");
    if ( nextChar == '{' ) {
	/* 
	 * XXX -- create maps
	 */
	if ( forceChar('{') ) {
	    return;
	}
	if ( list(n) ) {
	    return;
	}
	forceChar('}');
    }
}
  
  
/* 
 * List -> Name Id List | null
 *
 * Returns zero on successful parse, non-zero on error
 */
static int
list(llpName)
    char *llpName;
{
    char	hlpName[BUFSIZE];
    ProtId	hlpId;
    
    xTrace0(ptbl, TR_DETAILED, "ptbl parse: list called");
    if ( name(hlpName) == 0 ) {
	/* 
	 * null list
	 */
	return 0;
    }
    if ( (hlpId = id()) == -1 ) {
	error(ERR_ID);
	return 1;
    }
    protTblAddBinding(llpName, hlpName, hlpId);
    xTrace2(ptbl, TR_FUNCTIONAL_TRACE, "ptbl parse: list element %s -> %d", hlpName, hlpId);
    return list(llpName);
}


/* 
 * returns 0 if a name is not matched
 */
static char *
name(buf)
    char *buf;
{
    int		i;

    if ( ! isalpha(nextChar) ) {
	return 0;
    }
    i = 0;
    while ( nextChar && ! isspace(nextChar) ) {
	if ( i >= BUFSIZE ) {
	    error(ERR_NAME_TOO_LARGE);
	    return 0;
	}
	buf[i++] = nextChar;
	get();
    }
    skipWhite();
    buf[i] = 0;
    return buf;
}


/* 
 * Returns -1 (not a valid id) if an id is not matched
 */
static ProtId
id()
{
    char	buf[80];
    int		i;
    ProtId	n;
    int		res;
    int 	dohex = 0;

    if ( ! isdigit(nextChar) && nextChar != 'x' ) {
	return -1;
    }
    i = 0;
    /* 
     * Check for hex format
     */
    if ( nextChar == 'x' ) {
	buf[i++] = nextChar;
	get();
	dohex = 1;
    }
    while ( nextChar && 
	   ((!dohex && isdigit(nextChar)) || dohex && isxdigit(nextChar))) {
      if ( i >= sizeof(buf) ) {
	error(ERR_NAME_TOO_LARGE);
	return 0;
      }
      buf[i++] = nextChar;
      get();
    }
    skipWhite();
    buf[i] = 0;
    if ( buf[0] == 'x') {
	res = sscanf(buf+1, PROTID_HEX_FORMAT, &n);
    } else {
	res = sscanf(buf, PROTID_DEC_FORMAT, &n);
    }
    if ( res != 1 ) {
	/* 
	 * The scan was botched.  We know this is an error, so we
	 * don't have to worry about pushing characters back.
	 */
	return -1;
    }
    return n;
}




