//Copyright, 2001-2003, Astra Network Inc.  All Rights Reserved

//This source code has been published by Astra Network Inc. However, any
//use, reproduction, modification, distribution or transfer of this
//software, or any software which includes or is based upon any of this
//code, is only permitted if expressly authorized by a written license
//agreement from Astra. Contact your Astra representative directly for
//more information.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include "sixfourbit.h"
#include "tables.h"
#include "pcode.h"
#include "version.h"

/* for lex */
int yylex (void);

/* ### globals ### */

char *filename = "p.out";
char quiet = 0;

int write_nobreak (int fd, void *ptr, size_t size);


/* ### Code starts here ### */

int
write_nobreak (int fd, void *ptr, size_t size)
{
  int rv;
  size_t chunksize = 0;

  while (chunksize < size)
    {
      errno = 0;
      rv = write (fd, (char *) ptr + chunksize, size - chunksize);
      if (rv != -1)
	chunksize += rv;
      if ((chunksize < size) && errno != EAGAIN && errno != EINTR
	  && errno != 0)
	return -1;
    }
  return 0;
}

int
main (int argc, char *argv[])
{
  int i, j, c;
  int outfd;
  uint32_t t;
  uint16_t u;
  unsigned long offset;

  while ((c = getopt (argc, argv, "f:q")) != -1)
    {
      switch (c)
	{
	case 'f':
	  filename = optarg;
	  break;
	case 'q':
	  quiet++;
	  break;
	case '?':
	  fprintf (stderr, "%s [-f script] [-q]\n", argv[0]);
	  fprintf (stderr, "Options:\n");
	  fprintf (stderr,
		   " -f    Asm file to parse, stdin if unspecified\n");
	  fprintf (stderr, " -q    Quiet mode\n");
	  exit (-1);
	  break;

	}
    }

  if (quiet == 0)
    {
#ifdef BETA
      fprintf (stderr,
	       "HAFTA Overlord Assembler v%d.%02d %s "
	       "(Build: %s)\n", VERSION / 100, VERSION % 100, BETA, BUILD);
      fprintf (stderr, "This is a beta version. It is not for use in"
	       " production evironments.\n");
#else
      fprintf (stderr,
	       "HAFTA Overlord Assembler v%d.%02d (Build: %s)\n",
	       VERSION / 100, VERSION % 100, BUILD);
#endif /* #ifdef BETA */
      fprintf (stderr,
	       "Copyright 2002-2003, Astra Network Inc., "
	       "All rights reserved.\n");
    }

  memset (tpcode_table, 0xffffffff, 256 * sizeof (ttable_entry));
  yylex ();

  /* check for undefined references */
  for (i = 0; i < 256 && tpcode_table[i].name[0] != 0xff; i++)
    {
      if (tpcode_table[i].p_offset == 0xffffffff)
	{
	  printf ("Section \"%s\" referenced but not defined\n",
		  tpcode_table[i].name);
	  exit (1);
	}
    }

  /* blah blah blah open file */
  do
    {
      outfd = open (filename,
		    O_WRONLY | O_CREAT | O_TRUNC,
		    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
      if (outfd == -1 && errno != EAGAIN && errno != EINTR && errno != 0)
	{
	  printf ("Could not open output file \"%s\": %s\n",
		  filename, strerror (errno));
	  exit (1);
	}
    }
  while (outfd == -1);

  /* *** pcode version *** */
  t = HTOFL (PCODE_VERSION);
  if (write_nobreak (outfd, &t, sizeof (uint32_t)))
    {
      printf ("Could not write pcode version to output file \"%s\": %s\n",
	      filename, strerror (errno));
      exit (1);
    }

  /* *** ola version *** */
  t = HTOFL (VERSION);
  if (write_nobreak (outfd, &t, sizeof (uint32_t)))
    {
      printf ("Could not write ola version to output file \"%s\": %s\n",
	      filename, strerror (errno));
      exit (1);
    }

  /* *** olc version *** */
  t = HTOFL (olc_version);
  if (write_nobreak (outfd, &t, sizeof (uint32_t)))
    {
      printf ("Could not write olc version to output file \"%s\": %s\n",
	      filename, strerror (errno));
      exit (1);
    }

  /* *** stack size *** */
  u = HTOFS (stack_size);
  if (write_nobreak (outfd, &u, sizeof (uint16_t)))
    {
      printf ("Could not write stack size to output file \"%s\": %s\n",
	      filename, strerror (errno));
      exit (1);
    }

  /* *** stack policy *** */
  u = HTOFS (stack_death);
  if (write_nobreak (outfd, &u, sizeof (uint16_t)))
    {
      printf ("Could not write stack policy to output file \"%s\": %s\n",
	      filename, strerror (errno));
      exit (1);
    }

  /* *** context count *** */
  for (j = 0; j < 256; j++)
    if (ctx_table[j][0] == 0)
      break;
  t = HTOFL ((long) (j));

  if (write_nobreak (outfd, &t, sizeof (uint32_t)))
    {
      printf ("Could not write context count to output file \"%s\": %s\n",
	      filename, strerror (errno));
      exit (1);
    }

  /* *** module table *** */
  for (j = 0; j < 256; j++)
    {
      if (mod_table[j][0] == 0)
	break;
      else if (write_nobreak (outfd, mod_table[j], strlen (mod_table[j]) + 1))
	{
	  printf
	    ("Could not write module table entry %d to file \"%s\": %s\n", j,
	     filename, strerror (errno));
	  exit (1);
	}
    }

  if (write_nobreak (outfd, "", sizeof (char)))
    {
      printf ("Could not terminate module table in file \"%s\": %s\n",
	      filename, strerror (errno));
      exit (1);
    }

  /* *** pcode segment table *** */

  offset = lseek (outfd, 0, SEEK_CUR);	/* get the current file offset */
  for (j = 0; j < i; j++)
    offset += strlen (tpcode_table[j].name) + 1 + sizeof (uint32_t);
  offset += 1 + sizeof (uint32_t);

  for (j = 0; j < i; j++)
    {
      t = HTOFL (tpcode_table[j].p_offset + offset);

      if (write_nobreak (outfd, &t, sizeof (uint32_t)))
	{
	  printf
	    ("Could not write segment table offset entry %d to file \"%s\": %s\n",
	     j, filename, strerror (errno));
	  exit (1);
	}
      if (write_nobreak
	  (outfd, tpcode_table[j].name, strlen (tpcode_table[j].name) + 1))
	{
	  printf
	    ("Could not write segment table name entry %d to file \"%s\": %s\n",
	     j, filename, strerror (errno));
	  exit (1);
	}
    }
  t = 0;
  if (write_nobreak (outfd, &t, sizeof (uint32_t))
      || write_nobreak (outfd, &t, sizeof (char)))
    {
      printf ("Could not terminate segment table in file \"%s\": %s\n",
	      filename, strerror (errno));
      exit (1);
    }

  /* *** pcode *** */
  if (write_nobreak (outfd, p_code, p_code_cur))
    {
      printf ("Could not write pcode to file \"%s\": %s\n",
	      filename, strerror (errno));
      exit (1);
    }
  return 0;
}
