#include <stdio.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#define MAXERROR 10
#include "Error.h"

#define MAXERRORS 1024
#define MAXCLASSES 128

#define skipwhite(s) while ((*s != '\0') && (isspace(*s) || *s == '*')) s++
#define skipwhitebutnotcr(s) while ((*s != '\0' && *s != '\n') && \
				    (isspace(*s) || *s == '*')) s++
#define skiptoE(s) while (s < endcomment && (*s != 'E')) s++
#define findstart(s) while (s < end && !(s[0] == '/' && s[1] == '*')) s++
#define findend(s) while ((*s != '\0') && !(s[0] == '*' && s[1] == '/')) s++

Card32 codes[MAXERRORS];
char *names[MAXERRORS];
char *descs[MAXERRORS];
int numErrors = 0;

char *classes[MAXCLASSES];

extern char *malloc();

dumpInclude(f)
     FILE *f;
{
  int i;

  fprintf(f, "#define MAXERROR %d\n", numErrors);

  for (i = 0; i < numErrors; i++)
    fprintf(f, "#define %s 0x%08x\n", names[i], codes[i]);
}

dumpC(f)
     FILE *f;
{
  int i;

  fprintf(f, "#include \"Error.h\"\n\n");

  fprintf(f, "Errer Errur[ERRORDEPTH];\n");
  fprintf(f, "int Error_StackTop = 0;\n");
  fprintf(f, "char *errtbl[MAXERROR+1] = {\n");
  for (i = 0; i < numErrors; i++)
    fprintf(f, "  \"%s\",\n", descs[i]);
  fprintf(f, "  \"Error number out of range\"\n");
  fprintf(f, "};\n");
}

addError(n, s, d, b)
     char *n, *s, *d;
     Card32 b;
{
  Card32 e;
  char *str;

  if (!strcasecmp("fatal", s))
    e = S_FATAL;
  else
    {
      if (!strcasecmp("warning", s))
	e = S_WARNING;
      else
	{
	  if (!strcasecmp("info", s))
	    e = S_INFO;
	  else
	    {
	      if (!strcasecmp("trivia", s))
		e = S_TRIVIA;
	      else
		{
		  fprintf(stderr, "unknown severity: %s for error %s\n",
			  s, n);
		  exit(20);
		}
	    }
	}
    }

  str = malloc(strlen(d) + 1);
  if (str == NULL)
    {
      fprintf(stderr, "couldn't malloc\n");
      exit(21);
    }

  strcpy(str, d);
  descs[numErrors] = str;

  str = malloc(strlen(n) + 1);
  if (str == NULL)
    {
      fprintf(stderr, "couldn't malloc\n");
      exit(21);
    }

  strcpy(str, n);

  names[numErrors] = str;
  codes[numErrors] = b | e | numErrors;
#ifdef DEBUG
  fprintf(stdout, "#define %s 0x%08x\n", names[numErrors], codes[numErrors]);
#endif
  numErrors++;
}

main(argc, argv)
     int argc;
     char **argv;
{
  FILE *f;
  int d;
  char *output;
  char outputh[200], outputc[200];
  char *buf, *ptr, *end, *endcomment, *start;
  char *name, *severity, *description, *flag;
  Card32 flagbits;
  int wascr;
  struct stat info;
  int cc;

  if (*argv)
    argv++;
  if (*argv)
    {
      output = *argv;
      argv++;
    }

  if (!*argv)
    {
      fprintf(stderr, "usage: outputfile inputfile inputfile ...\n");
      exit(1);
    }

  while (*argv)
    {
      d = open(*argv, O_RDONLY, 0);
      if (d < 0)
	{
	  fprintf(stderr, "Couldn't open %s (errno %d)\n", *argv, errno);
	  exit(2);
	}

      if (fstat(d, &info))
	{
	  fprintf(stderr, "Couldn't stat %s (errno %d)\n", *argv, errno);
	  exit(3);
	}

      buf = malloc(info.st_size + 1);
      if (buf == NULL)
	{
	  fprintf(stderr, "Couldn't allocate %d bytes\n", info.st_size);
	  exit(4);
	}

      cc = read(d, buf, info.st_size);

      if (cc == -1)
	{
	  fprintf(stderr, "Couldn't read %s (errno %d)\n", *argv, errno);
	  exit(5);
	}

      if (cc != info.st_size)
	{
	  fprintf(stderr, "Didn't read expected number of bytes from %s\n",
		  *argv);
	  exit(6);
	}

      ptr = buf;
      end = buf + info.st_size;

      while (ptr < end)
	{
	  findstart(ptr);
	  ptr += 2;
	  if (ptr < end)
	    {
	      endcomment = ptr;
	      findend(endcomment);
	      if (endcomment < end)
		{
		  *endcomment = '\0';
		  while (ptr < endcomment)
		    {
		      skiptoE(ptr);
		      if (*ptr == 'E' && !strncmp(ptr, "ERRORS:", 7))
			{
			  ptr += 7;
			  skipwhite(ptr);
			  while (ptr < endcomment)
			    {
			      name = ptr;
			      while (!isspace(*ptr) && *ptr != ':' &&
				     *ptr != '\0')
				ptr++;
			      if (*ptr != ':')
				{
				  fprintf(stderr,
					  "premature ending comment in %s\n",
					  *argv);
				  exit(7);
				}
			      *ptr = '\0';
#ifdef DEBUG
			      fprintf(stdout, "Error: %s\n", name);
#endif
			      ptr++;
			      skipwhite(ptr);
			      severity = ptr;
			      while (!isspace(*ptr) && *ptr != '\0')
				ptr++;
			      if (*ptr == '\0')
				{
				  fprintf(stderr,
					  "bad comment in %s\n", *argv);
				  exit(8);
				}
			      wascr = (*ptr == '\n');
			      *ptr = '\0';
#ifdef DEBUG
			      fprintf(stdout, "Type: %s\n", severity);
#endif
			      flagbits = 0;

			      while (!wascr)
				{
				  flag = NULL;
				  ptr++;
				  skipwhitebutnotcr(ptr);

				  if (*ptr == '\n')
				    break;

				  flag = ptr;
				  while (!isspace(*ptr) && *ptr != '\0')
				    ptr++;

				  if (*ptr == '\0')
				    {
				      fprintf(stderr,
					      "bad comment in %s\n", *argv);
				      exit(8);
				    }

				  wascr = (*ptr == '\n');
				  *ptr = '\0';

				  if (!strcasecmp("log", flag))
				    flagbits |= LMASK;
				  else
				    {
				      if (!strcasecmp("debug", flag))
					flagbits |= DMASK;
				      else
					fprintf(stderr, "Unknown flag: %s\n",
						flag);
				    }
#ifdef DEBUG
				  if (flag)
				    fprintf(stdout, "Flag: %s\n", flag);
#endif
				}

			      ptr++;
			      skipwhite(ptr);
			      description = ptr;
			      while (*ptr != '\n' && *ptr != '\0')
				ptr++;
			      if (*ptr == '\0')
				{
				  fprintf(stderr,
					  "bad comment in %s\n", *argv);
				  exit(9);
				}
			      *ptr = '\0';
#ifdef DEBUG
			      fprintf(stdout, "Description: %s\n",
				      description);
#endif
			      ptr++;
			      skipwhite(ptr);
			      addError(name, severity, description, flagbits);
			    }
			}
		      else
			ptr++;
		    }
		  ptr = endcomment + 2;
		}
	    }
	}
      argv++;
    }

  sprintf(outputh, "%s.h", output);
  sprintf(outputc, "%s.c", output);

  f = fopen(outputh, "w");
  dumpInclude(f);
  fclose(f);

  f = fopen(outputc, "w");
  dumpC(f);
  fclose(f);
  exit(0);
}
