#include "Parse.h"
#include <stdio.h>
#include <strings.h>
#include <ctype.h>
#include <errno.h>

extern char *malloc();

Trap Parse_NewMasterBlock(block)
     MasterBlock **block;
{
  *block = (MasterBlock *)malloc(sizeof(MasterBlock));
  if (*block == NULL)
    Return(MEMORY_ALLOC, sizeof(MasterBlock));

  bzero(*block, sizeof(MasterBlock));
  return OK;
}

/*
 * ERRORS:
 *
 * PARSE_TOOMANYTYPES: FATAL
 * There are already the maximum number of allowable types.
 */
Trap Parse_RegisterKeywords(block, type, words)
     MasterBlock *block;
     char *type;
     Keywords *words;
{
  if (block == NULL)
    Return(BOZO_NULLPOINTER, NULL);

  if (block->numTypes == PARSE_MAXTYPES)
    Return(PARSE_TOOMANYTYPES, block->numTypes);

  block->types[block->numTypes] = type;
  block->keywords[block->numTypes] = words;
  block->numTypes++;
  return OK;
}

/*
 * ERRORS:
 *
 * PARSE_BADKEYWORD: FATAL
 * An unknown keyword was encountered.
 */
Trap Parse_ParseLine(block, line)
     char *line;
     MasterBlock *block;
{
  int t, n, l;
  char *c;

  c = line;
  while (isspace(*c)) c++;
  if (*c == '\0')
    return OK;

  for (t = 0; t < block->numTypes; t++)
    for (n = 0; block->keywords[t][n].word != NULL; n++)
      {
	l = strlen(block->keywords[t][n].word);
	if (!strncasecmp(c, block->keywords[t][n].word, l)
	    && (isspace(c[l]) || c[l] == '\0'))
	  return block->keywords[t][n].parser(block, c + l);
      }

  Return(PARSE_BADKEYWORD, NULL);
}

/*
 * ERRORS:
 *
 * FILE_FOPEN: FATAL
 * Stream open returned NULL.
 *
 * FILE_FREAD: FATAL
 * Stream read returned NULL.
 */
Trap Parse_ParseFile(block, name)
     char *name;
     MasterBlock *block;
{
  FILE *f;
  char line[1024],*cp;
  int fe, num = 0;

  f = fopen(name, "r");
  if (f == NULL)
    Return(FILE_FOPEN, errno);

  while (!feof(f))
    {
      if (NULL == fgets(line, sizeof(line), f))
	{
	  fe = ferror(f);
	  if (feof(f))
	    {
	      fclose(f);
	      return Error_Exists;
	    }
	  fclose(f);
	  Return(FILE_FREAD, fe);
	}
      num++;

    line[strlen(line)-1] = '\0';  /*XXX strip newline */
    /*  cp = index(line,'\n');
      while (*cp == ' ') cp--; 
      *cp = '\0';     */
      if (Parse_ParseLine(block, line)
	  && Error_Severity == S_FATAL)
	{
	  fclose(f);
	  if (Error == PARSE_BADKEYWORD)
	    {
	      Error_Pop();
	      Return(PARSE_BADKEYWORD, num);
	    }
	  return CHECK;
	}
    }

  fclose(f); /* never reached... :-*/
  return Error_Exists;
}

/*
 * ERRORS:
 *
 * PARSE_ATTOP: FATAL
 * Tree already climbed.
 */
Trap Parse_UpBlock(mblock)
     MasterBlock *mblock;
{
  if (mblock == NULL)
    Return(BOZO_NULLPOINTER, NULL);

  if (mblock->current &&
      mblock->current->parent)
    mblock->current = mblock->current->parent;
  else
    Return(PARSE_ATTOP, NULL);

  return OK;
}

/*
 * ERRORS:
 *
 * PARSE_ATBOTTOM: FATAL
 * Tree already descended here.
 */
Trap Parse_Subblock(mblock)
     MasterBlock *mblock;
{
  if (mblock == NULL)
    Return(BOZO_NULLPOINTER, NULL);

  if (mblock->current &&
      mblock->current->child)
    mblock->current = mblock->current->child;
  else
    Return(PARSE_ATBOTTOM, NULL);

  return OK;
}

/*
 * ERRORS:
 *
 * PARSE_ATEND: FATAL
 * No more leaves here.
 */
Trap Parse_NextBlock(mblock)
     MasterBlock *mblock;
{
  if (mblock == NULL)
    Return(BOZO_NULLPOINTER, NULL);

  if (mblock->current &&
      mblock->current->next)
    mblock->current = mblock->current->next;
  else
    Return(PARSE_ATEND, NULL);

  return OK;
}

Trap Parse_NewSubblock(mblock, type, block)
     MasterBlock *mblock;
     char *type;
     ParseBlock **block;
{
  ParseBlock *new;

  if (mblock == NULL)
    Return(BOZO_NULLPOINTER, NULL);

  if (mblock->current == NULL)
    return Parse_NewBlock(mblock, type, block);

  if ((mblock->current != NULL) && (mblock->current->child != NULL))
    {
      mblock->current = mblock->current->child;
      return Parse_NewBlock(mblock, type, block);
    }

  new = (ParseBlock *)malloc(sizeof(ParseBlock));
  if (new == NULL)
    Return(MEMORY_ALLOC, sizeof(ParseBlock));
  bzero(new, sizeof(ParseBlock));

  if (type)
    new->blocktype = type;
  else
    new->blocktype = "undefined";

  new->parent = mblock->current;
  mblock->current->child = new;
  mblock->current = new;

  return OK;
}

Trap Parse_NewBlock(mblock, type, block)
     MasterBlock *mblock;
     char *type;
     ParseBlock **block;
{
  ParseBlock *new, *w;

  if (mblock == NULL)
    Return(BOZO_NULLPOINTER, NULL);

  new = (ParseBlock *)malloc(sizeof(ParseBlock));
  if (new == NULL)
    Return(MEMORY_ALLOC, sizeof(ParseBlock));
  bzero(new, sizeof(ParseBlock));

  if (type)
    new->blocktype = type;
  else
    new->blocktype = "undefined";

  if (mblock->current != NULL)
    {
      w = mblock->current;
      while (w->next != NULL)
	w = w->next;
      w->next = new;
      new->parent = w->parent;
    }
  else
    mblock->first = new;

  mblock->current = new;
  if (block != NULL)
    *block = new;
  return OK;
}

/*
 * ERRORS:
 *
 * PARSE_NODATA: INFO
 * No data of the specified type was available.
 *
 * PARSE_NEWDATA: INFO
 * A new data field of the specified type was created.
 *
 * PARSE_NOBLOCKS: FATAL
 * There was no block to create the new data field under.
 */
Trap Parse_GetCurrentData(block, type, data, size)
     MasterBlock *block;
     char *type;
     caddr_t *data;
     int size;
{
  ParseData *d, **l;

  if (block == NULL)
    Return(BOZO_NULLPOINTER, NULL);

  if (block->first == NULL && size == 0)
    Return(PARSE_NODATA, NULL);

  if (block->first == NULL)
    Return(PARSE_NOBLOCKS, NULL);

  l = &(block->current->data);
  for (d = *l; d != NULL; l = &(d->next), d = d->next)
    if (!strcmp(d->type, type))
      {
	*data = d->data;
	return OK;
      }

  if (size == 0)
    Return(PARSE_NODATA, NULL);

  *l = (ParseData *)malloc(sizeof(ParseData));
  if (*l == NULL)
    Return(MEMORY_ALLOC, sizeof(ParseData));

  (*l)->type = type;
  (*l)->next = NULL;
  (*l)->data = (caddr_t)malloc(size);
  if ((*l)->data == NULL)
    {
      free(*l);
      *l = NULL;
      Return(MEMORY_ALLOC, size);
    }

  bzero((*l)->data, size);
  *data = (*l)->data;
  return OK;
}
