/*
   Syntax Checker - see manual for specs.

   Use: <exe file> inputfile

   Part of HUB MUD
   Copyright 1995-1997 by Henry McDaniel III.  Permisson granted for single
   user use by and free distribution to persons licensed to use HUB and those
   they authorize who are presently involved with their implementation of HUB.
   All other rights reserved.
 */

#include <stdio.h>
#include <ctype.h>
/* 
 * Note: you may need to use strings.h on some systems
 */
#include <string.h>

/*
 * You may need to decrease this number if your machine has
 * very limit memory.  Do NOT raise above 300.
 * IF you have more than 300 objects, you need to seperate
 * them into different files.
 */

#define H_OBJECTS 300

/* CHANGE NOTHING BELOW THIS LINE!!!!!!!!!!!!!!!!!!!!!!!!  */
/* ******************************************************* */
#define VERSION "0.0"
FILE *fif;
int lns = 0;
char ln_bu[201];
char mn[30];
#define SBU_TOP 2000
char Sbu[SBU_TOP + 1];
int oco = 0;
int start_ln[H_OBJECTS];
int oname[H_OBJECTS][30];
char block_name[30];

#define M_RESET 0
#define M_ID    1
#define M_SHORT 2
#define M_LONG  3
#define M_ANY   4

char tmp_bu[2000];

#define GEM   strcpy(tmp_bu,label); \
strcat(tmp_bu," "); strcat(tmp_bu,ln_bu); \
strcpy(ln_bu,tmp_bu); len=strlen(label)+1;

int m = 0, sub = 0;
int xmapping = 0, xstring = 0, Sstart = 0, fatals = 0, warns = 0;
void 
note (char *text, int ln)
{
  printf ("line %-2d: %s\n", ln, text);
}
void 
warn (char *text, int ln)
{
  printf ("line %-2d: Warning, %s\n", ln, text);
  warns++;
}
void 
fatal (char *text, int ln)
{
  printf ("line %-2d: Fatal, %s\n", ln, text);
  fatals++;
}
int 
get_x (char *buf, char *input, int bTOP)
{
  int len, x = 0, y, n = 0, z, bTOPr;
  while (*(input + x) < 33 || *(input + x) > 125)
    x++;
  if (!(len = strlen (input + x)))
    return 0;
  bTOPr = bTOP - 1;
  for (y = x; y < (len + 1) && n <= bTOPr; y++)
    if (*(input + y) == '\n' || *(input + y) == '\r' ||
	isspace (*(input + y)))
      break;
    else
      {
	*(buf + n) = *(input + y);
	n++;
      }
  *(buf + n) = '\0';
  if (n == bTOPr)
    return 0;
  if (y == (len - 1))
    return (y + 1);
  for (z = y; z < len; z++)
    if (*(input + z) > 32 && *(input + z) < 126)
      return z;
  return 0;
}
int 
ck_for_dup (char *test)
{
  int TOP = 0, x;
  char buf[256];

  if (oco < 2)
    return;
  TOP = oco - 1;

  for (x = 0; x < TOP; x++)
    if (!strcmp (test, (char *) oname[x]))
      {
	buf[0] = '\0';
	sprintf (buf, "object \"%s\" using same id as another", test);
	warn (buf, lns);
	if (oco >= 2)
	  note ("original object begins here", start_ln[oco - 2]);
	else
	  note ("original object ends? around", start_ln[oco - 1]);
	return 1;
      }
  return 0;
}
int 
get_obtype (char *test)
{
  char buf[256];
  switch (*test)
    {
    case 'r':
    case 'R':
      return 3;
      break;
    case 'n':
    case 'N':
    case 'c':
    case 'C':
      return 2;
      break;
    case 'o':
    case 'O':
      return 1;
      break;
    }
  buf[0] = '\0';
  sprintf (buf, "unknown or illegal object type %s", test);
  fatal (buf, lns);
  return 0;
}
char label[21];
int 
get_type (char *name)
{
  if (!strcmp (name, "size") || !strcmp (name, "block") || !strcmp (name, "start") ||
      !strcmp (name, "store") || !strcmp (name, "weight") || !strcmp (name, "capacity") ||
      !strcmp (name, "liquid") || !strcmp (name, "food") || !strcmp (name, "wear") ||
      !strcmp (name, "armor") || !strcmp (name, "point") || !strcmp (name, "decay") ||
      !strcmp (name, "energy") || !strcmp (name, "mob") || !strcmp (name, "level") ||
      !strcmp (name, "ud") || !strcmp (name, "ud") || !strcmp (name, "us") ||
      !strcmp (name, "ld") || !strcmp (name, "ls") || !strcmp (name, "nb"))
    return 99;
  if (isdigit (*(label)))
    if (!strcmp (mn, "block{"))
      return 99;
  return 10;
}
void 
ast (char *to_add)
{
  int end, x;
  end = strlen (to_add) - 1;
  if (!xstring)
    {
      Sbu[0] = '\0';
      Sstart = lns;
    }
  if (*(to_add + end) == '\\')
    {
      *(to_add + end) = ' ';
      *(to_add + end + 1) = '\0';
      xstring = 1;
      if ((end + 2 + strlen (Sbu)) > SBU_TOP)
	{
	  warn ("string too long", lns);
	  fatal ("possible start of excessive string", Sstart);
	}
    }
  else
    xstring = 0;
  strcat (Sbu, to_add);
  if (!xstring)
    {
      x = strlen (Sbu);
      while (*(Sbu + x) < 33 || *(Sbu + x) > 125)
	x--;
      *(Sbu + x + 1) = '\0';
      Sbu[0] = '\0';
    }
}
char id[21], type[11], environment[21];
int typen = 0, flags = 0;
void 
pin ()
{
  int len = 0, cle = 0, local_type = 0;
  char ck[11];
  char buf[256];

  if (xstring)
    {
      ast (ln_bu);
      ln_bu[0] = '\0';
      return;
    }
  else if (Sbu[0] != '\0')
    Sbu[0] = '\0';
  ck[0] = '\0';
  get_x (ck, ln_bu, 10);

  if (!strcmp (ck, "}") || *ln_bu == '}')
    {
      if (!xmapping)
	warn ("end of mapping mark \"}\" outside of any mapping.", lns);
      else
	xmapping = 0;
      ln_bu[0] = '\0';
      return;
    }
  switch (m)
    {
    case M_ID:
      id[0] = '\0';
      type[0] = '\0';
      len = get_x (id, ln_bu, 20);
      cle += len;
      len = get_x (type, ln_bu + cle, 10);
      if (*type == '\0')
	{
	  fatal ("ID entry missing object type information", lns);
	  exit (0);
	}
      cle += len;
      environment[0] = '\0';
      if (len)
	get_x (environment, ln_bu + cle, 20);
      if (*environment == '\0')
	strcpy (environment, "-");
      ck_for_dup (id);
      strcpy ((char *) oname[oco - 1], id);
      flags = 0;
      if ((*type == 'c' || *type == 'C') && (*(type + 1) != 'h' && *(type + 1) != 'H'))
	typen = 1;
      else
	typen = get_obtype (type);
      ln_bu[0] = '\0';
      m = M_SHORT;
      return;
      break;
    case M_SHORT:
      strcpy (label, "short:");
      GEM
	m = M_LONG;
      break;
    case M_LONG:
      strcpy (label, "long:");
      GEM
	m = M_ANY;
      break;
    case M_ANY:
      label[0] = '\0';
      len = get_x (label, ln_bu, 20);
      break;
    }
  if (*(label + strlen (label) - 1) == '{')
    {
      if (xmapping)
	fatal ("nested mappings are unsupported.", lns);
      else
	{
	  xmapping = 1;
	  strcpy (mn, label);
	}
      *(label + strlen (label) - 1) = '\0';
      ln_bu[0] = '\0';
      return;
    }
  else if (*(label + strlen (label) - 1) == ':')
    {
      *(label + strlen (label) - 1) = '\0';
      local_type = get_type (label);
      if (local_type != 10)
	{
	  if (!len)
	    {
	      buf[0] = '\0';
	      sprintf (buf, "variable \"%s\" without value.  will assume %s=0",
		       label, label);
	      warn (buf, lns);
	    }
	  else;
	  ln_bu[0] = '\0';
	  return;
	}
    }
  else if (*label != '\0')
    {
      buf[0] = '\0';
      sprintf (buf, "\"%s\" unknown symbol or data.", label);
      fatal (buf, lns);
      ln_bu[0] = '\0';
      return;
    }
  else if (*label == '\0')
    return;
  if (len)
    ast (ln_bu + len);
  ln_bu[0] = '\0';
}
void 
snooze ()
{
  int data_to_process = 0, problem_character = 0, len, x, stop, start = 0;
  char buf[256];
  char load_bu[201];

  while (!feof (fif))
    {
      load_bu[0] = '\0';
      fgets (load_bu, 200, fif);
      lns++;
      if (*load_bu == ';' || *load_bu == '\0')
	continue;
      if (*load_bu == '#')
	{
	  start_ln[oco] = lns;
	  oco++;
	  if (oco > H_OBJECTS)
	    {
	      fatal ("too many objects", lns);
	      buf[0] = '\0';
	      sprintf (buf, "%d is the maximum of objects per file.", H_OBJECTS);
	      note (buf, lns);
	      exit (0);
	    }
	  m = M_ID;
	}
      else
	{
	  start = 0;
	  len = strlen (load_bu);
	  data_to_process = 0;
	  for (x = 0; x < len && !data_to_process; x++)
	    if (*(load_bu + x) > 32 && *(load_bu + x) < 126)
	      data_to_process = 1;
	    else
	      {
		start = x;
		*(load_bu + x) = '\t';
	      }
	  if (data_to_process)
	    {
	      problem_character = 0;
	      for (x = 0; x < len; x++)
		if (*(load_bu + x) == 27 || *(load_bu + x) > 126 ||
		    *(load_bu + x) == '|')
		  {
		    problem_character = 1;
		    *(load_bu + x) = '?';
		  }
		else if (*(load_bu + x) == '\n' || *(load_bu + x) == '\r')
		  *(load_bu + x) = '\0';
	      if (problem_character)
		{
		  warn ("illegal character(s) detected and nullified.", lns);
		  note ("illegal chatacter(s) are replaced with \"?\".", lns);
		}
	      strcpy (ln_bu, load_bu + start);
	      pin ();
	    }
	}
    }
}
void 
main (int argvc, char **argv)
{
  if (argvc != 2)
    {
      printf ("use: %s source\n\r", argv[0]);
      exit (0);
    }
  if (!(fif = fopen (argv[1], "r")))
    {
      printf ("Could not open input file \"%s\"\n\r", argv[1]);
      exit (0);
    }
  mn[0] = '\0';
  Sbu[0] = '\0';
  printf ("HUB MUD Basic Syntax Checker v%s.\n\
Input file=\"%s\"\n", VERSION, argv[1]);
  printf ("-----------------------------------------------\n");
  snooze ();
  fclose (fif);
  printf ("*** %d objects read ***\n", oco);
  if (warns && fatals)
    printf ("%d warnings and ", warns);
  else if (warns)
    printf ("%d warnings.\n", warns);
  if (fatals)
    printf ("%d fatal errors.\n", fatals);
  if (!warns && !fatals)
    printf ("No errors.\n");
  else
    printf ("You should fix the indicated problems.\n");
  exit (0);
}