/*
   Area creation utility.
   Plain text object parser.

   Part of HUB MUD.
   Copyright 1995-1997 by Henry McDaniel III.  All rights reserved.
 */

#include <stdio.h>
#include <ctype.h>

#define VERSION "0.0"

char prefix[10];
FILE *finput, *foutput;
int lines = 0;			/* number of lines read sofar */
char line_buffer[201];		/* current raw data input */
char map_name[30];

#define STRING_BUFFER_LIMIT 2000
char string_buffer[STRING_BUFFER_LIMIT + 1];

/* object tracking data */
#define MAX_OBJECTS 3000
int obj_count = 0;		/* object counter */
int start_line[MAX_OBJECTS];
int obj_name[MAX_OBJECTS][30];	/* index of ids */

char block_name[30];

#define MODE_RESET 0
#define MODE_ID    1
#define MODE_SHORT 2
#define MODE_LONG  3
#define MODE_ANY   4

char tmp_buffer[2000];

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

/* current state */
int mode = 0, sub = 0;
int in_mapping = 0, in_string = 0, string_start = 0, fatals = 0, warns = 0;

void 
note (char *text, int line)
{
  printf ("line %-2d: %s\n", line, text);
}

void 
warn (char *text, int line)
{
  printf ("line %-2d: Warning, %s\n", line, text);
  warns++;
}

void 
fatal (char *text, int line)
{
  printf ("line %-2d: Fatal, %s\n", line, text);
  fatals++;
}

int 
get_word (char *buf, char *input, int blimit)
{
  int len, x = 0, y, n = 0, z, blimitr;

/*  while(isspace(*(input+x)))x++; */

  while (*(input + x) < 33 || *(input + x) > 125)
    x++;

  if (!(len = strlen (input + x)))
    return 0;

  blimitr = blimit - 1;
/* was len.. made len+1 */
  for (y = x; y < (len + 1) && n <= blimitr; y++)
    if (*(input + y) == '\n' ||
	*(input + y) == '\r' ||
	isspace (*(input + y)))
      break;
    else
      {
	*(buf + n) = *(input + y);
	n++;
      }
  *(buf + n) = '\0';

  if (n == blimitr)
    return 0;

/* changed */
  if (y == (len - 1))
    return (y + 1);

/* was len+1 */
  for (z = y; z < len; z++)
/*    if(!isspace(*(input+z))) */
    if (*(input + z) > 32 && *(input + z) < 126)
      return z;
  return 0;
}

int 
check_for_dup (char *test)
{
  int limit = 0, x;
  char buf[256];

  if (obj_count < 2)
    return;
  limit = obj_count - 1;

  for (x = 0; x < limit; x++)
    if (!strcmp (test, (char *) obj_name[x]))
      {
	buf[0] = '\0';
	sprintf (buf, "object \"%s\" using same id as another", test);
	warn (buf, lines);

	if (obj_count >= 2)
	  note ("original object ends here", start_line[obj_count - 2]);
	else
	  note ("original object ends? around here", start_line[obj_count - 1]);
	return 1;
      }
  return 0;
}

int 
get_obtype (char *test)
{
  char buf[256];

  switch (*test)
    {
    case 'r':			/* ROOM */
    case 'R':
      return 3;
      break;
    case 'n':			/* NPC */
    case 'N':
    case 'c':
    case 'C':
      return 2;
      break;
    case 'o':			/* Other Object */
    case 'O':
      return 1;
      break;
    }
  buf[0] = '\0';
  sprintf (buf, "unknown or illegal object type %s", test);
  fatal (buf, lines);
  return 0;
}

char label[21];

/* get local var type */
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, "drink") ||
      !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, "position") ||
      !strcmp (name, "hunt"))

    return 11;			/* is integer */



  if (isdigit (*(label)))
    if (!strcmp (map_name, "block{"))
      return 11;

  return 10;			/* string otherwise */
}


void 
prefix_exit (char *test)
{
  char tmp[STRING_BUFFER_LIMIT + 1];
  char first_part[25];
  int len;

  if (prefix[0] == '\0')
    return;

  first_part[0] = '\0';
  len = get_word (first_part, string_buffer, 24);

  tmp[0] = '\0';
  if (!len || *first_part == '\0')
    {
      sprintf (tmp, "exit named \"%s\" in error", label);
      fatal (tmp, lines);
      return;
    }

  sprintf (tmp, "%s %s%s", first_part, prefix, string_buffer + len);
  strcpy (string_buffer, tmp);
}


void 
add_string (char *to_add)
{
  int end, x, chop = 0, y;

  end = strlen (to_add) - 1;

  if (!in_string)
    {
      string_buffer[0] = '\0';
      string_start = lines;
    }

/* expand any \n marks */
  for (x = 0; x < (end - chop); x++)
    if (*(to_add + x) == '\\' && *(to_add + x + 1) == 'n')
      {
	*(to_add + x) = '\n';
	for (y = (x + 1); y < (end + 1 - chop); y++)
	  *(to_add + y) = *(to_add + y + 1);
	chop++;
      }
  end = strlen (to_add) - 1;

  if (*(to_add + end) == '\\')
    {
/* note get_word will nullify space at start of string */
      *(to_add + end) = ' ';
      *(to_add + end + 1) = '\0';
      in_string = 1;

      if ((end + 2 + strlen (string_buffer)) > STRING_BUFFER_LIMIT)
	{
	  warn ("string too long", lines);
	  fatal ("possible start of excessive string", string_start);
	}
    }
  else
    in_string = 0;

  strcat (string_buffer, to_add);
  if (!in_string)
    {

      x = strlen (string_buffer);
/* while(isspace(*(string_buffer+x))) x--; */
      while (*(string_buffer + x) < 33 || *(string_buffer + x) > 125)
	x--;

      *(string_buffer + x + 1) = '\0';

      if (!strcmp ("exits{", map_name) && in_mapping)
	prefix_exit (string_buffer);

      strcat (string_buffer, "|\n");
      fprintf (foutput, "%s", string_buffer);
      string_buffer[0] = '\0';
    }
}

char id[21], type[11], environment[21];
int typen = 0, flags = 0;

void 
process_input ()
{
  int len = 0, cum_len = 0, local_type = 0;
  char check[11];
  char buf[256];

  if (in_string)
    {
      add_string (line_buffer);
      line_buffer[0] = '\0';
      return;
    }
  else if (string_buffer[0] != '\0')
    {
      if (!strcmp ("exits{", map_name) && in_mapping)
	prefix_exit (string_buffer);

      fprintf (foutput, "%s|\n", string_buffer);
      string_buffer[0] = '\0';
    }

  check[0] = '\0';
  get_word (check, line_buffer, 10);

  if (!strcmp (check, "}") || *line_buffer == '}')
    {
      if (!in_mapping)
	warn ("end of mapping mark \"}\" outside of any mapping.", lines);
      else
	in_mapping = 0;
      line_buffer[0] = '\0';
      return;
    }

  switch (mode)
    {

    case MODE_ID:
      /* try and get id information */
      id[0] = '\0';
      type[0] = '\0';

      len = get_word (id, line_buffer, 20);
      cum_len += len;
      len = get_word (type, line_buffer + cum_len, 10);
      if (*type == '\0')
	{
	  fatal ("ID entry missing object type information", lines);
	  exit (0);
	}
      cum_len += len;
      environment[0] = '\0';
      if (len)
	get_word (environment, line_buffer + cum_len, 20);

      if (*environment == '\0')
	strcpy (environment, "-");

      check_for_dup (id);
      strcpy ((char *) obj_name[obj_count - 1], id);

      flags = 0;
/* container special case */
      if ((*type == 'c' || *type == 'C') && (*(type + 1) != 'h' && *(type + 1) != 'H'))
	{
	  flags = 16;
	  typen = 1;
	}
      else
	typen = get_obtype (type);

/* print out data */

      if (prefix[0] != '\0')
	fprintf (foutput, "%s%s %d %d - %s\n", prefix, id, flags, typen, environment);
      else
	fprintf (foutput, "%s %d %d - %s\n", id, flags, typen, environment);

      line_buffer[0] = '\0';
      mode = MODE_SHORT;
      return;
      break;

    case MODE_SHORT:
      strcpy (label, "short:");
      GEM
	mode = MODE_LONG;
      break;
    case MODE_LONG:
      strcpy (label, "long:");
      GEM
	mode = MODE_ANY;
      break;
    case MODE_ANY:
      label[0] = '\0';
      len = get_word (label, line_buffer, 20);
      break;
    }
  /* end of mode test switch */
/*
   buf[0]='\0';
   sprintf(buf,"         label=\"%s\"\n",label);
   printf(buf);
 */

/* Mapping */
  if (*(label + strlen (label) - 1) == '{')
    {
      if (in_mapping)
	fatal ("nested mappings are unsupported.", lines);
      else
	{
	  in_mapping = 1;
	  strcpy (map_name, label);
	}

      *(label + strlen (label) - 1) = '\0';

/* print out data for head of a mapping list */
      fprintf (foutput, "%s 8 29 0 1\n", label);
      line_buffer[0] = '\0';
      return;
    }

  else
/* Regular data set */
  if (*(label + strlen (label) - 1) == ':')
    {
      *(label + strlen (label) - 1) = '\0';

      local_type = get_type (label);

/* print the data out */
      fprintf (foutput, "%s 8 %d %d 0 ", label, local_type, in_mapping);

      if (local_type != 10)	/* if not string print data directly */
	{
	  if (!len)
	    {
	      buf[0] = '\0';
	      sprintf (buf, "variable \"%s\" without value!  will assume %s=0",
		       label, label);
	      warn (buf, lines);
	    }
	  else
	    {

	      if (!isdigit (*(line_buffer + len)))
		fprintf (foutput, "%d\n", 0);
	      else
		fprintf (foutput, "%s\n", line_buffer + len);
	    }
	  line_buffer[0] = '\0';
	  return;
	}
    }

  else if (*label != '\0')
    {
      buf[0] = '\0';
      sprintf (buf, "\"%s\" unknown symbol or data.", label);
      fatal (buf, lines);
      line_buffer[0] = '\0';
      return;
    }
  else if (*label == '\0')
    return;


/* if it is our first crack at the string we will get to here */
  if (len)
    add_string (line_buffer + len);
  line_buffer[0] = '\0';
}


void 
run ()
{
  int data_to_process = 0, problem_character = 0, len, x, stop, start = 0;
  char buf[256];
  char load_buffer[201];

  while (!feof (finput))
    {

      /* read in a line of data */
      load_buffer[0] = '\0';
      fgets (load_buffer, 200, finput);

      lines++;

/* ignore comments */
      if (*load_buffer == ';' || *load_buffer == '\0')
	continue;

/* increment object count and associated load info if new
   object
 */
      if (*load_buffer == '#')
	{
	  start_line[obj_count] = lines;
	  obj_count++;

	  if (obj_count > 1)
	    fprintf (foutput, "~\n");


	  if (obj_count > MAX_OBJECTS)
	    {
	      fatal ("too many objects", lines);

	      buf[0] = '\0';
	      sprintf (buf, "%d is the maximum of objects per file.", MAX_OBJECTS);
	      note (buf, lines);

	      exit (0);
	    }
	  mode = MODE_ID;
	}
      else
	{

/* do nothing if line is merely whitespace */

	  start = 0;
	  len = strlen (load_buffer);
	  data_to_process = 0;
	  for (x = 0; x < len && !data_to_process; x++)
	    /*    if(!isspace(*(load_buffer+x))) data_to_process=1; */
	    if (*(load_buffer + x) > 32 && *(load_buffer + x) < 126)
	      data_to_process = 1;
	    else
	      {
		start = x;

		*(load_buffer + x) = '\t';
	      }

/* if not just whitespace... */
	  if (data_to_process)
	    {
	      problem_character = 0;
	      for (x = 0; x < len; x++)
		if (*(load_buffer + x) == 27 || *(load_buffer + x) > 126 ||
		    *(load_buffer + x) == '|')
		  {
		    problem_character = 1;
		    *(load_buffer + x) = '?';
		  }
		else if (*(load_buffer + x) == '\n' || *(load_buffer + x) == '\r')
		  *(load_buffer + x) = '\0';

	      if (problem_character)
		{
		  warn ("illegal character(s) detected and nullified.", lines);
		  note ("illegal chatacter(s) are replaced with \"?\".", lines);
		}

	      strcpy (line_buffer, load_buffer + start);
	      process_input ();
	    }
	}
    }				/* end of while !feof */
}

void 
main (int argvc, char **argv)
{
  if (argvc < 3 || argvc > 4)
    {
      printf ("use: %s source output <prefix>\n\r", argv[0]);
      exit (0);
    }

  if (argvc == 4)
    {
      int fail = 0;
      if (*prefix == '~')
	{
	  printf ("Prefix cannot start with ~.  Sorry.\n\r");
	  fail = 1;
	}
      else
	{
	  int len, x;
	  len = strlen (prefix);
	  for (x = 0; x < len; x++)
	    if (*(prefix + x) == 27 || *(prefix + x) > 126)
	      {
		printf ("Illegal character ascii %d in prefix.\n\r", *(prefix + x));
		fail = 1;
	      }
	}

      if (fail)
	exit (1);

      strcpy (prefix, argv[3]);
    }
  else
    prefix[0] = '\0';


  if (!(finput = fopen (argv[1], "r")))
    {
      printf ("Could not open input file \"%s\"\n\r", argv[1]);
      exit (0);
    }
  if (!(foutput = fopen (argv[2], "a+")))
    {
      printf ("Could not open ouput file \"%s\"\n\r", argv[2]);
      exit (0);
    }

  map_name[0] = '\0';
  string_buffer[0] = '\0';

  printf ("HUB MUD English to Raw Data translator v%s.\n\
Input file=\"%s\"\n", VERSION, argv[1]);

  if (prefix[0] != '\0')
    printf ("Using ObjID prefix (\"%s\")\n", prefix);

  printf ("-----------------------------------------------\n");

/* Do the job */
  run ();

  fclose (finput);
  fclose (foutput);

  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 ("*** %d objects translated ***\n\r", obj_count);
  exit (0);

}