/* dbparser.C
 * John Viega
 *
 * Feb 8 2000
 */

#include "config.H"
#include "token.H"
#include "vulndb.H"
#include "strpool.H"

static TokenContainer  *tc;
static int next = 0;
struct VarInfo
{
  TokenId type;
  int     val;
};
static Dictionary<VarInfo> *varlist;
static char *cur_var;
static char *cur_funcname;

TokenId expecting;
int *dst_str;
int *dst_int;

char *current_key_name;
int  new_desc;
int  new_repair;
int  new_severity;
int  new_handler;
int  new_input;

void InitParser(TokenContainer *t)
{
  tc         = t;
  new_desc   = 0;
  new_repair = 0;
  new_input  = 0;
  varlist    = new Dictionary<VarInfo>(1);
}

void ParseError(char *s1, char *s2)
{
  fprintf(stderr,  "%s:%s: %s (when parsing data file)\n", GetProgramName(), 
	  s2, s1);
}

void ParseError(char *s, Token *t)
{
  if(t)
    fprintf(stderr, "%s:%d: %s (when parsing data file)\n", GetProgramName(), 
	    t->GetLineNo(), s);
  else
    fprintf(stderr, "%s: Unexpected end of file (when parsing data file).\n", 
	    GetProgramName());
}


void YieldFunctionDefEnd()
{
  if(!new_desc)
    {
      ParseError("Missing problem description", cur_funcname);
      exit(11);
    }
  if(!new_repair)
    {
      ParseError("Missing repair hint", cur_funcname);
      exit(12);
    }
  if((new_severity < 0) || (new_severity > 5))
    {
      ParseError("Invalid severity.", cur_funcname);
      exit(13);
    }
  AddRecord(cur_funcname, new_desc, new_repair, new_severity, new_handler, 
	    new_input);
  new_desc = new_repair = new_input = 0;
}

void YieldKeyName(char *n)
{
  current_key_name = n;
  if(!strcasecmp(n, "desc"))
    {
      expecting = STRING;
      dst_str = &new_desc;
      return;
    }
  if(!strcasecmp(n, "solution"))
    {
      expecting = STRING;
      dst_str = &new_repair;
      return;
    }
  if(!strcasecmp(n, "risk"))
    {
      expecting = INTEGER;
      dst_int = &new_severity;
      return;
    }
  if(!strcasecmp(n, "handler"))
    {
      expecting = INTEGER;
      dst_int = &new_handler;
      return;
    }
  if(!strcasecmp(n, "input"))
    {
      expecting = INTEGER;
      dst_int = &new_input;
      return;
    }
  ParseError("Warning: Unrecognized key", n);
  expecting = COMMENT; // Kludge.
}

void YieldStringValue(char *s)
{
  switch(expecting)
    {
    default:
      return; // Ignore (Kludge)
    case INTEGER:
      fprintf(stderr, "%s:%s: Expected integer value to key '%s', got '%s'.\n",
	      GetProgramName(), cur_funcname, current_key_name, s);
      exit(14);
    case STRING:
      *dst_str = AddStringToPool(s);
      return;
    }
}

void YieldStringValue(int s)
{
  switch(expecting)
    {
    default:
      return; // Ignore (Kludge)
    case INTEGER:
      fprintf(stderr, "%s:%s: Expected integer value to key '%s', got var.\n",
	      GetProgramName(), cur_funcname, current_key_name);
      exit(14);
    case STRING:
      *dst_str = s;
      return;
    }
}


void YieldIntegerValue(int t)
{
  switch(expecting)
    {
    default:
      return; // Ignore (Kludge)
    case STRING:
      fprintf(stderr, "%s:%s: Expected string value to key '%s', got '%d'.\n",
	      GetProgramName(), cur_funcname, current_key_name, t);
      exit(15);
    case INTEGER:
      *dst_int = t;
      return;
    }
}

int YieldVariableValue(char *s)
{
  VarInfo *v;
  short ignore;

  if(!(v = varlist->GetItem(s, ignore)))
    return -1;
  switch(v->type)
    {
    case INTEGER:
      YieldIntegerValue(v->val);
      return 0;
    default:
      YieldStringValue(v->val);
      return 0;
    }
}

void YieldFuncName(char *n)
{
  cur_funcname = new char[strlen(n)+1];
  strcpy(cur_funcname, n);  // ITS4: ignore strcpy
  new_desc     = 0;
  new_repair   = 0;
  new_severity = 0;
  new_handler  = 0;
}

void YieldEmptyDefinition()
{
  delete[] cur_funcname;
}

void YieldStringAssignment(Token *t)
{
  VarInfo *v = new VarInfo();
  if(!v) OutOfMemory();
  v->type = STRING;
  char *val = ((StringTok *)t)->GetContents();
  v->val = AddStringToPool(val);
  varlist->SetItem(cur_var, v);
}

void YieldIntegerAssignment(Token *t)
{
  VarInfo *v = new VarInfo();
  if(!v) OutOfMemory();
  v->type = INTEGER;
  v->val = ((IntegerTok *)t)->GetNumericValue();
  varlist->SetItem(cur_var, v);
  // TODO: Will leak memory if it's already in the varlist.  Not a big deal.
}

void YieldVarName(char *n)
{
  // TODO: Need to copy out the contents to cur_var
  cur_var = new char[strlen(n)+1];
  if(!cur_var) OutOfMemory();
  strcpy(cur_var, n);  // ITS4: ignore strcpy
}

int NT_Guts();

int NT_Opt()
{
  Token *t = tc->GetToken(next++);
  if(!t)
    {
      ParseError("',' or '}' expected.", t);
      return -1;
    }
  switch(t->GetTokenType())
  {
  case OPERATOR:
    if(!strcmp(t->GetValue(), ","))
      {
	return NT_Guts();
      }
  default:
    --next;
    return 0;	   
  }

}

int NT_Guts()
{
 get_1:
  Token *t = tc->GetToken(next++);
  if(!t)
    {
    id:
      ParseError("Identifier expected.", t);
      return -1;
    }
  switch(t->GetTokenType())
    {
    case COMMENT:
      goto get_1;
    case IDENTIFIER:
      YieldKeyName(t->GetValue());
      break;
    default:
      goto id;
    }
get_2:
  t = tc->GetToken(next++);
  if(!t)
    {
    equals:
      ParseError("= expected.", t);
      return -1;
    }
  switch(t->GetTokenType())
    {
    case COMMENT:
      goto get_2; // It's a comment
    case OPERATOR:
      if(!strcmp(t->GetValue(), "="))
	{
	  break;
	}
      // FALLTHROUGH
    default:
      goto equals;
    }
get_3:
  t = tc->GetToken(next++);
  if(!t)
    {
    str_or_int:
      ParseError("string, integer or identifier expected.", t);
      return -1;
    }
  switch(t->GetTokenType())
    {
    case COMMENT:
      goto get_3; // It's a comment.
    case STRING:
      YieldStringValue(((StringTok *)t)->GetContents());
      break;
    case INTEGER:
      YieldIntegerValue(((IntegerTok *)t)->GetNumericValue());
      break;
    case IDENTIFIER:
      if(YieldVariableValue(t->GetValue())) return -1;
      break;
    default:
      goto str_or_int;
    }
  return NT_Opt();
}

int NT_Def()
{
 get_1:
  Token *t = tc->GetToken(next++);
  if(!t)
    {
    id:
      ParseError("Identifier expected.", t);
      return -1;
    }
  switch(t->GetTokenType())
    {
    case COMMENT:
      goto get_1;
    case IDENTIFIER:
      YieldFuncName(t->GetValue());
      break;
    default:
      goto id;
    }
 get_2:
  t = tc->GetToken(next++);
  if(!t)
    {
    lbrace:
      ParseError("'{' expected.", t);
      return -1;
    }
  switch(t->GetTokenType())
    {
    case COMMENT:
      goto get_2;
    case OPERATOR:
      if(!strcmp(t->GetValue(), "{"))
	{
	  break;
	}
    default:
      goto lbrace;
    }
  t = tc->GetToken(next); //Probable lookahead, don't incr
  if(!t)
    {
      ParseError("Dictionary entry expected.", t);
      return -1;
    }
  if((t->GetTokenType() == OPERATOR) && !strcmp(t->GetValue(), "}"))
    {
      YieldEmptyDefinition();
      next++;
      return 0;
    }
  int r = NT_Guts();
  if(r) return r;
 get_4:
  t = tc->GetToken(next++);
  if(!t)
    {
    rbrace:
      ParseError("} expected.", t);
      return -1;
    }
  switch(t->GetTokenType())
    {
    case COMMENT:
      goto get_4; // It's a comment
    case OPERATOR:
      if(!strcmp(t->GetValue(), "}"))
	{
	  YieldFunctionDefEnd();
	  return 0;
	}
      // FALLTHROUGH
    default:
      goto rbrace;
    }
  return 0;  
}

int NT_Var()
{
get_1:
  Token *t = tc->GetToken(next++);
  if(!t)
    {
    equals:
      ParseError("= expected.", t);
      return -1;
    }
  switch(t->GetTokenType())
    {
    case COMMENT:
      goto get_1; // It's a comment
    case OPERATOR:
      if(!strcmp(t->GetValue(), "="))
	{
	  break;
	}
      // FALLTHROUGH
    default:
      goto equals;
    }
get_2:
  t = tc->GetToken(next++);
  if(!t)
    {
    str_or_int:
      ParseError("string or integer expected.", t);
      return -1;
    }
  switch(t->GetTokenType())
    {
    case COMMENT:
      goto get_2; // It's a comment.
    case STRING:
      YieldStringAssignment(t);
      break;
    case INTEGER:
      YieldIntegerAssignment(t);
      break;
    default:
      goto str_or_int;
    }
get_3:
  t = tc->GetToken(next++);
  if(!t)
    {
    semi:
      ParseError("';' expected." , t);
      return -1;
    }
  switch(t->GetTokenType())
    {
    case COMMENT:
      goto get_3;
    case OPERATOR:
      if(!strcmp(t->GetValue(), ";")) return 0;
      // FALLTHROUGH
    default:
        goto semi;
    }
}

int NT_Program()
{
get_1:
  Token *t = tc->GetToken(next++);
  int r;
  if(!t) return 0;
  switch(t->GetTokenType())
    {
    case IDENTIFIER:
      break;
    case COMMENT:
      goto get_1;
    default:
      ParseError("FUNC or identifier expected.", t);
      return -1;
    }
  char *v = t->GetValue();
  if(!strcmp(v, "FUNC"))
    {
      r = NT_Def();
      if(r) return r;
    }
  else
    {
      YieldVarName(v);
      r = NT_Var();
      if(r) return r;
    }

  NT_Program();
  return 0;
}
