//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 <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

#include <string.h>
#include "sixfourbit.h"
#include "pcode.h"
#include "read_pcode.h"
#include "global_bits.h"

/* ### local prototypes ### */

static void print_opcode (unsigned char op);

static void append_char (unsigned char c);
static void append_num (int64_t n);

static void extend_pcode (size_t len);

static signed int read_nobreak (int fd, /*@out@ */ void *ptr, size_t size);


/* ### some globals ### */

uint32_t ncontexts = 0;
segtable_t segment_table[256];
char pcode_module_table[256][256];
static unsigned long nsegments = 0;
unsigned char *pcode = NULL;
static size_t pcode_size = 0;
static size_t pcode_alloc = 0;
uint16_t stack_initial_alloc;
uint16_t stack_policy;

static unsigned long ola_ver, olc_ver;


/* ### insert code here ### */

void
read_pcode (char *filename)
{
  uint32_t version = 0;
  int pfile;
  int i, j;
  signed int rv;
  unsigned char opcode;
  unsigned long offset;

  do
    {
      errno = 0;
      pfile = open (filename, O_RDONLY);
      if (pfile == -1 && errno != EAGAIN && errno != EINTR && errno != 0)
	{
	  fatal ("Could not open pcode file \"%s\" for reading: %s",
		 filename, strerror (errno));
	}

    }
  while (pfile == -1);

  /* *** pcode version *** */
  rv = read_nobreak (pfile, &version, sizeof (uint32_t));
  if (rv != 0)
    {
      fatal ("Could not read pcode version from \"%s\": %s",
	     filename, (rv == -1) ? strerror (errno) : "Unexpected EOF");
    }
  version = FTOHL (version);
  if (version != 1)
    {
      fatal ("Wrong P-Code file version: %d!\nExpected version 1.",
	     (int) version);
    }
  if (verbose != 0)
    printf ("P-Code version %d.\n", (int) version);

  /* *** ola version *** */
  rv = read_nobreak (pfile, &ola_ver, sizeof (uint32_t));
  if (rv != 0)
    {
      fatal ("Could not read ola version from \"%s\": %s",
	     filename, (rv == -1) ? strerror (errno) : "Unexpected EOF");
    }
  ola_ver = FTOHL (ola_ver);
  if (verbose != 0)
    printf ("Created by ola version %lu.%02lu\n", ola_ver / 100,
	    ola_ver % 100);

  /* *** olc version *** */
  rv = read_nobreak (pfile, &olc_ver, sizeof (uint32_t));
  if (rv != 0)
    {
      fatal ("Could not read olc version from \"%s\": %s",
	     filename, (rv == -1) ? strerror (errno) : "Unexpected EOF");
    }
  olc_ver = FTOHL (olc_ver);
  if (verbose != 0)
    printf ("Created by olc version %lu.%02lu\n", olc_ver / 100,
	    olc_ver % 100);

  /* *** stack size *** */
  rv = read_nobreak (pfile, &stack_initial_alloc, sizeof (uint16_t));
  if (rv != 0)
    {
      fatal ("Could not read stack size from \"%s\": %s",
	     filename, (rv == -1) ? strerror (errno) : "Unexpected EOF");
    }
  stack_initial_alloc = FTOHS (stack_initial_alloc);
  if (verbose != 0)
    printf ("Initial stack size of %d.\n", (int) stack_initial_alloc);

  /* *** stack policy *** */
  rv = read_nobreak (pfile, &stack_policy, sizeof (uint16_t));
  if (rv != 0)
    {
      fatal ("Could not read stack policy from \"%s\": %s",
	     filename, (rv == -1) ? strerror (errno) : "Unexpected EOF");
    }
  stack_policy = FTOHS (stack_initial_alloc);
  if (verbose != 0)
    {
      printf ("Stack policy: ");
      switch (stack_policy)
	{
	case 0:
	  printf ("grow\n");
	  break;
	case 1:
	  printf ("die\n");
	  break;
	default:
	  printf ("unknown\n");
	}
    }

  /* *** context count *** */
  rv = read_nobreak (pfile, &ncontexts, sizeof (uint32_t));
  if (rv != 0)
    {
      fatal ("Could not read context count from \"%s\": %s",
	     filename, (rv == -1) ? strerror (errno) : "Unexpected EOF");
    }
  ncontexts = FTOHL (ncontexts);
  if (verbose != 0)
    printf ("%d context%s.\n", (int) ncontexts, (ncontexts == 1) ? "" : "s");

  /* *** module table *** */
  for (i = 0; i < 256; i++)
    {
      for (j = 0; j < 256; j++)
	{
	  rv = read_nobreak (pfile, &pcode_module_table[i][j], sizeof (char));
	  if (rv != 0)
	    {
	      fatal ("Could not read module table entry %d from \"%s\": %s",
		     i, filename,
		     (rv == -1) ? strerror (errno) : "Unexpected EOF");
	    }
	  if (pcode_module_table[i][j] == '\0')
	    break;
	}
      if (j == 0)
	break;
      if (verbose != 0)
	printf ("Read module %d name: %s\n", i, pcode_module_table[i]);
    }
  if (verbose != 0)
    printf ("Read %d module name%s.\n", i, (i == 1) ? "" : "s");

  /* *** pcode segment table *** */
  memset (segment_table, 0, sizeof (segtable_t) * 256);
  for (i = 0; i < 256; i++)
    {
      rv = read_nobreak (pfile, &segment_table[i].pcode, sizeof (uint32_t));
      if (rv != 0)
	{
	  fatal
	    ("Could not read pcode segment table offset entry %d from \"%s\": %s",
	     i, filename, (rv == -1) ? strerror (errno) : "Unexpected EOF");
	}
      segment_table[i].pcode =
	(char *) FTOHL ((unsigned long) segment_table[i].pcode);

      for (j = 0; j < 256; j++)
	{
	  rv = read_nobreak (pfile, &segment_table[i].name[j], sizeof (char));
	  if (rv != 0)
	    {
	      fatal
		("Could not read segment table name entry %d from \"%s\": %s",
		 i, filename,
		 (rv == -1) ? strerror (errno) : "Unexpected EOF");
	    }
	  if (segment_table[i].name[j] == '\0')
	    break;
	}

      if (segment_table[i].pcode == NULL)
	break;
      if (verbose != 0)
	printf ("Segment %d (%s) at offset %ld in file.\n",
		i, segment_table[i].name, (long) segment_table[i].pcode);
    }
  nsegments = i;
  if (verbose != 0)
    printf ("%lu segment%s in total.\n", nsegments,
	    (nsegments == 1) ? "" : "s");

  /* *** pcode *** */
  offset = (unsigned long int) lseek (pfile, 0, SEEK_CUR);
  for (;;)
    {
      rv = read_nobreak (pfile, &opcode, sizeof (unsigned char));
      if (rv == 1)		/* EOF */
	break;
      if (rv == -1)
	{
	  fatal ("Could not read pcode at byte %ld in file %s",
		 (long int) lseek (pfile, 0, SEEK_CUR), filename);
	}
      switch (opcode)
	{
	case pCALL:
	case pALARM:
	case pSIGNAL:
	case pSIGCHILD:
	case pGET:
	case pSET:
	case pGETPID:
	case pSETPID:
	case pRETURN:
	case pINST:
	case pDINST:
	case pDROP:
	case pDUP:
	case pCONTEXT:
	case pIF:
	case pFI:
	case pFOR:
	case pROF:
	case pLT:
	case pGT:
	case pNOT:
	case pEQ:
	case pAND:
	case pOR:
	case pPLUS:
	case pMINUS:
	case pMULT:
	case pDIV:
	case pNEG:
	  append_char (opcode);
	  print_opcode (opcode);
	  break;
	case pNUM:
	  append_char (opcode);
	  print_opcode (opcode);
	  {
	    int64_t num;
	    rv = read_nobreak (pfile, &num, sizeof (int64_t));
	    if (rv != 0)
	      {
		fatal ("Could not read pcode at byte %ld in file %s",
		       (long int) lseek (pfile, 0, SEEK_CUR), filename);
	      }
	    num = FTOHQ (num);
	    append_num (num);
	    if (verbose != 0)
	      printf ("%s ", QTOSTR (num));
	  }
	  break;
	case '(':
	  append_char (opcode);
	  print_opcode (opcode);
	  for (;;)
	    {
	      unsigned char t;
	      rv = read_nobreak (pfile, &t, sizeof (char));
	      if (rv != 0)
		{
		  fatal ("Could not read pcode at byte %ld in file %s",
			 (long int) lseek (pfile, 0, SEEK_CUR), filename);
		}
	      if (t == '\0')
		{
		  append_char (t);
		  print_opcode (')');
		  break;
		}
	      else
		{
		  append_char (t);
		  print_opcode (t);
		}
	    }
	  break;
	default:
	  printf ("\nUnknown Opcode: %X\n", (unsigned int) opcode);
	}
    }
  if (verbose != 0)
    printf ("\n");

  if (pcode_size == 0)
    {
      fatal ("Could not load pcode!\n");
    }
  if (verbose != 0)
    printf ("%d byte%s of pcode loaded at %p\n",
	    (int) pcode_size, (pcode_size == 1) ? "" : "s", pcode);
  (void) close (pfile);

  for (i = 0; (unsigned long int) i < nsegments; i++)
    {
      segment_table[i].pcode += (long) pcode;
      segment_table[i].pcode -= offset;
      if (verbose != 0)
	printf ("Segment %d (%s) relocated to %p\n",
		i, segment_table[i].name, segment_table[i].pcode);
    }
}

void
print_opcode (unsigned char op)
{
  if (verbose == 0)
    return;
  switch (op)
    {
    case pCALL:
      printf ("call ");
      break;
    case pALARM:
      printf ("alarm ");
      break;
    case pSIGNAL:
      printf ("signal ");
      break;
    case pSIGCHILD:
      printf ("sigchild ");
      break;
    case pGET:
      printf ("get ");
      break;
    case pSET:
      printf ("set ");
      break;
    case pGETPID:
      printf ("getpid ");
      break;
    case pSETPID:
      printf ("setpid ");
      break;
    case pRETURN:
      printf ("return ");
      break;
    case pINST:
      printf ("inst ");
      break;
    case pDINST:
      printf ("dinst ");
      break;
    case pDROP:
      printf ("drop ");
      break;
    case pDUP:
      printf ("dup ");
      break;
    case pCONTEXT:
      printf ("context ");
      break;
    case pIF:
      printf ("{ ");
      break;
    case pFI:
      printf ("} ");
      break;
    case pFOR:
      printf ("[ ");
      break;
    case pROF:
      printf ("] ");
      break;
    case pLT:
      printf ("< ");
      break;
    case pGT:
      printf ("> ");
      break;
    case pNOT:
      printf ("! ");
      break;
    case pEQ:
      printf ("= ");
      break;
    case pAND:
      printf ("& ");
      break;
    case pOR:
      printf ("| ");
      break;
    case pPLUS:
      printf ("+ ");
      break;
    case pMINUS:
      printf ("- ");
      break;
    case pMULT:
      printf ("* ");
      break;
    case pDIV:
      printf ("/ ");
      break;
    case pNEG:
      printf ("~ ");
      break;
    case pNUM:
      printf ("#");
      break;
    case '(':
      printf ("(");
      break;
    case ')':
      printf (") ");
      break;
    default:
      printf ("%c", op);
    }
}

void
append_char (unsigned char c)
{
  extend_pcode (sizeof (unsigned char));
  pcode[pcode_size] = c;
  pcode_size += sizeof (unsigned char);
}

void
append_num (int64_t n)
{
  extend_pcode (sizeof (int64_t));
  memcpy (&pcode[pcode_size], &n, sizeof (int64_t));
  pcode_size += sizeof (int64_t);
}

void
extend_pcode (size_t len)
{
  if (pcode == NULL)
    {
      pcode = malloc (4096);
      pcode_alloc = 4096;
    }
  if ((pcode_size + len) > pcode_alloc)
    {
      pcode = realloc (pcode, pcode_alloc * 2);
      pcode_alloc *= 2;
    }
  if (pcode == NULL)
    {
      fatal ("Out of Memory while loading pcode!");
    }
}

static signed int
read_nobreak (int fd, /*@out@ */ void *ptr, size_t size)
{
  int rv;
  size_t chunksize = 0;

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