/* << ACE >>

   -- Amiga BASIC Compiler --

   ** Parser: miscellaneous functions **
   ** Copyright (C) 1998 David Benn
   ** 
   ** This program is free software; you can redistribute it and/or
   ** modify it under the terms of the GNU General Public License
   ** as published by the Free Software Foundation; either version 2
   ** of the License, or (at your option) any later version.
   **
   ** This program is distributed in the hope that it will be useful,
   ** but WITHOUT ANY WARRANTY; without even the implied warranty of
   ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   ** GNU General Public License for more details.
   **
   ** You should have received a copy of the GNU General Public License
   ** along with this program; if not, write to the Free Software
   ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

   Author: David J Benn
   Date: 26th October-30th November, 1st-13th December 1991,
   14th,20th-27th January 1992, 
   2nd-17th, 21st-29th February 1992, 
   1st,13th,14th,22nd,23rd March 1992,
   21st,22nd April 1992,
   2nd,3rd,11th,15th,16th May 1992,
   7th,8th,9th,11th,13th,14th,28th,29th,30th June 1992,
   2nd-8th,14th-19th,26th-29th July 1992,
   1st-3rd,7th,8th,9th August 1992,
   6th,7th,21st,22nd December 1992,
   12th February 1993,
   1st March 1993,
   20th,30th June 1993,
   1st,2nd July 1993,
   26th October 1993,
   14th,16th,17th,24th,25th December 1993,
   2nd,5th,7th-9th,13th,14th January 1994,
   6th,7th,15th,16th,26th,27th February 1994,
   4th,13th,30th April 1994,
   14th,15th May 1994,
   12th,21st,22nd,25th June 1994,
   10th,14th,24th July 1994,
   3rd,22nd August 1994,
   2nd,3rd,10th September 1994,
   6th October 1995
 */

#include "acedef.h"
#include <string.h>

#ifdef __GNUC__
#define upper( a ) ( a >= 'a' && a <= 'z' ) ? ( a - 'a' + 'A' ) : ( a )

char *strupr( char *string )
{
    char *s;

    for( s = string; *s; s++ )
        *s = upper( *s );

    return string;
}

char *strrev( char *string )
{
    char temp;
    int i = 0, ln = strlen( string ), j = ln-1;

    for( ; i < j ; i++, j-- )
    {
        temp = string[i];
        string[i] = string[j];
        string[j] = temp;
    }

    return string;
}
#endif

/* locals */
static char *frame_ptr[] = {"(a4)", "(a5)"};
/*static	struct stat	_dta;		 local DTA buffer */
#define ERROR -1
char	_numstr[] = "0123456789ABCDEF";

/* externals */
extern BOOL report_errors;
extern int sym;
extern int lastsym;
extern int lev;
extern SHORT shortval;
extern LONG longval;
extern char id[MAXIDSIZE];
extern char ut_id[MAXIDSIZE];
extern char lastline[MAXLINELEN];
extern char string_const_start[7];
extern char string_const_end[4];
extern BOOL have_lparen;
extern BOOL end_of_source;
extern BOOL break_opt;
extern BOOL asm_comments;
extern BOOL error_log;
extern BOOL make_icon;
extern BOOL list_source;
extern BOOL optimise_opt;
extern BOOL wdw_close_opt;
extern BOOL module_opt;
extern FILE *err_log;
extern int idtype[31];
extern long strconstcount;
extern long tempstrcount;
extern long tempshortcount;
extern long templongcount;
extern char tempstrname[80], tempstrlabel[80];
extern char tempshortname[80], tempshortlabel[80];
extern char templongname[80], templonglabel[80];

char *ultoa(register unsigned long n,
            register char *buffer,
            register int radix)
{
	register char *p = buffer;
	char *strrev();

	do
		{
		*p++ = _numstr[n % radix];	/* grab each digit */
		}
		while((n /= radix) > 0);
	*p = '\0';
	return(strrev(buffer));			/* reverse and return it */
}

  
char *ltoa( register long n,
           register char *buffer,
           int radix)
{
	register char *p = buffer;

	if (n < 0)
		{
		*p++ = '-';
		n = -n;
		}
	ultoa(n, p, radix);
	return(buffer);
}

char *itoa(long n,
           char *buffer,
           int radix)
{
	char *ltoa();

	return(ltoa(((long) n), buffer, radix));
}

/* redefine ZC's CTRL-C testing function to do nothing 
long 
Chk_Abort ()
{
  return 0;
}
*/


/* functions */
void make_temp_long (void)
{
  /* make a long integer BSS object for temporary storage
     of actual value parameters */
  char numbuf[40], storesize[40];

  itoa (templongcount++, numbuf, 10);
  strcpy (templongname, "_templong");
  strcat (templongname, numbuf);

  strcpy (templonglabel, templongname);
  strcat (templonglabel, ":\0");

  strcpy (storesize, "ds.l 1 ");
  enter_BSS (templonglabel, storesize);
}

void make_temp_short (void)
{
  /* make a short integer BSS object for temporary storage
     of actual value parameters */
  char numbuf[40], storesize[40];

  itoa (tempshortcount++, numbuf, 10);
  strcpy (tempshortname, "_tempshort");
  strcat (tempshortname, numbuf);

  strcpy (tempshortlabel, tempshortname);
  strcat (tempshortlabel, ":\0");

  strcpy (storesize, "ds.l 1 ");
  enter_BSS (tempshortlabel, storesize);
}

void make_temp_string (void)
{
/* need a unique BSS string store for ALL string functions to prevent 
   overwriting of string data */
  char numbuf[40], sizebuf[40], storesize[40];

  itoa (tempstrcount++, numbuf, 10);
  strcpy (tempstrname, "_tempstring");
  strcat (tempstrname, numbuf);

  strcpy (tempstrlabel, tempstrname);
  strcat (tempstrlabel, ":\0");

  strcpy (storesize, "ds.b ");
  itoa (MAXSTRLEN, sizebuf, 10);
  strcat (storesize, sizebuf);
  enter_BSS (tempstrlabel, storesize);
}

void make_string_const (char *string)
{
  char *strbuf, buf[80], strlabel[80], strname[80];

  itoa (strconstcount++, buf, 10);
  /* label for DATA section */
  strcpy (strlabel, "_stringconst");
  strcat (strlabel, buf);
  /* name for reference in code */
  strcpy (strname, strlabel);
  /* complete string label */
  strcat (strlabel, ":\0");
  /* actual string constant */
  strbuf = (char *) alloc (strlen (string) + 10, MEMF_ANY);
  /* +10 is for string_const_start/end (9) & '\0' */
  strcpy (strbuf, string_const_start);
  strcat (strbuf, string);
  strcat (strbuf, string_const_end);
  enter_DATA(strlabel, strbuf);
  /*FreeMem(strbuf,strlen(string)+10); */
  /* push its address onto stack */
  gen ("pea", strname, "  ");
}

void make_label_from_linenum (int intconst, char *buf)
{
/* turns a line number into a label */

  switch (intconst)
    {
    case shortconst:
      sprintf (buf, "_LINE%d", shortval);
      break;
    case longconst:
      sprintf (buf, "_LINE%ld", longval);
      break;
    }
}

LONG max_array_ndx (SYM * curr)
{
/* Returns # of linear elements in an array.
   eg: DIM X(10,10) yields 121 elements: 0..10, 0..10 -> 11 * 11 
 */
  int i;
  LONG max = 1;

  for (i = curr->dims; i >= 0; i--)
    max *= curr->index[i];
  return (max);
}

void push_indices (SYM * curr)
{
/* put array indices onto stack */
  int ndxcount = 0;

  if (!have_lparen)
    insymbol ();
  else
    have_lparen = FALSE;	/* don't want to leave this as TRUE, else if
				   push_indices() called from factor() etc, 
				   insymbol() won't be called here! */
  if (sym != lparen)
    _error (14);
  else
    {
      do
	{
	  insymbol ();
	  make_sure_short (expr ());
	  ndxcount++;
	}
      while ((sym == comma) && (ndxcount <= curr->dims));

      /* too few indices: comma expected (ndxcount should now be > curr->dims) */
      if (ndxcount <= curr->dims)
	_error (16);

      /* too many indices or syntax error */
      if (sym != rparen)
	_error (9);
    }
}

void get_abs_ndx (SYM * curr)
{
/* calculate absolute pointer into array from multiple dimensions */
  int i, ndx_mult = 1;
  char mulbuf[40], srcbuf[40];

  gen ("moveq", "#0", "d7");

  /* pop indices from stack one at a time */
  for (i = curr->dims; i >= 0; i--)
    {
      sprintf (mulbuf, "#%ld", ndx_mult);

      gen ("move.w", "(sp)+", "d1");
      gen ("ext.l", "d1", "  ");
      gen ("move.l", "d1", "-(sp)");	/* push next index after coercing to long */
      gen ("move.l", mulbuf, "-(sp)");	/* push cumulative index */
      gen ("jsr", "lmulu", "  ");
      gen ("add.l", "#8", "sp");

      gen ("add.l", "d0", "d7");
      ndx_mult *= curr->index[i];
    }

  /* mutiply offset by data type size */
  if (curr->type == stringtype)
    {
      /* multiply d7 (containing absolute index) by string element size */

      /* #string_element_size */
      sprintf (srcbuf, "#%ld", curr->numconst.longnum);

      /* calculate absolute offset */
      gen ("move.l", "d7", "-(sp)");
      gen ("move.l", srcbuf, "-(sp)");
      gen ("jsr", "lmulu", "  ");	/* d7*MAXSTRLEN */
      gen ("add.l", "#8", "sp");
      gen ("move.l", "d0", "d7");
    }
  else if (curr->type == shorttype)
    gen ("lsl.l", "#1", "d7");	/* d7*2 */
  else
    /* long or single */
    gen ("lsl.l", "#2", "d7");	/* d7*4 */

  /* unsigned multiplication XREF */
  enter_XREF ("lmulu");
}

int push_struct (SYM * item)
{
/* push either the address of 
   a structure variable or the
   value of one of its members.
 */
  SYM *structype;
  char addrbuf[40], absbuf[40], numbuf[40];
  STRUCM *member;
  BOOL found = FALSE;
  int mbr_type = undefined;

  insymbol ();

  if (sym == memberpointer)
    {
      /* push value of a member */
      structype = item->other;	/* pointer to structure type definition */

      insymbol ();

      if (sym != ident)
	_error (7);
      else
	{
	  /* does member exist? */
	  member = structype->structmem->next;
	  while ((member != NULL) && (!found))
	    {
	      if (strcmp (member->name, id) == 0)
		found = TRUE;
	      else
		member = member->next;
	    }

	  /* dereference it? */
	  if (!found)
	    _error (67);	/* not a member! */
	  else
	    {
	      /* save member type */
	      mbr_type = member->type;

	      /* address of structure */
	      itoa (-1 * item->address, addrbuf, 10);
	      strcat (addrbuf, frame_ptr[lev]);

	      if (item->shared && lev == ONE)
		{
		  gen ("movea.l", addrbuf, "a0");	/* struct variable address */
		  gen ("movea.l", "(a0)", "a0");	/* start address of struct */
		}
	      else
		gen ("movea.l", addrbuf, "a0");		/* start address of struct */

	      /* offset from struct start */
	      if (mbr_type != stringtype)
		{
		  ltoa (member->offset, absbuf, 10);
		  strcat (absbuf, "(a0)");
		}

	      /* push value */
	      if (mbr_type == bytetype)
		{
		  gen ("move.b", absbuf, "d0");
		  gen ("ext.w", "d0", "  ");
		  gen ("move.w", "d0", "-(sp)");
		  mbr_type = shorttype;		/* byte */
		}
	      else if (mbr_type == shorttype)
		gen ("move.w", absbuf, "-(sp)");	/* short */
	      else if (mbr_type == stringtype)
		{
		  sprintf (numbuf, "#%ld", member->offset);
		  gen ("adda.l", numbuf, "a0");
		  gen ("move.l", "a0", "-(sp)");	/* push string address */
		}
	      else
		gen ("move.l", absbuf, "-(sp)");	/* long, single */
	    }
	}
      insymbol ();
      return (mbr_type);
    }
  else
    {
      /* push address of structure */
      itoa (-1 * item->address, addrbuf, 10);
      strcat (addrbuf, frame_ptr[lev]);

      if (item->shared && lev == ONE)
	{
	  gen ("movea.l", addrbuf, "a0");	/* address of structure variable */
	  gen ("move.l", "(a0)", "-(sp)");	/* start address of structure */
	}
      else
	gen ("move.l", addrbuf, "-(sp)");

      return (longtype);
    }
}

void change_id_type (int newtype)
{
  int i, first, last;

  /* 
     ** Change the data type of a range.
     **
     ** Note that "A-_" is acceptable
     ** since ASC("_") > ASC("Z"). 
   */
  do
    {
      insymbol ();
      if (sym == ident)
	first = id[0] - 'A';
      else
	_error (7);
      insymbol ();
      if (sym == minus)
	{
	  /* range */
	  insymbol ();
	  if (sym == ident)
	    last = id[0] - 'A';
	  else
	    _error (7);
	  if (first > last)
	    _error (31);
	  else
	    for (i = first; i <= last; i++)
	      idtype[i] = newtype;
	  insymbol ();
	}
      else
	/* just a single one (letter or underscore) */
	idtype[first] = newtype;
    }
  while (sym == comma);
}

void gen_branch (char *branch, char *labname)
{
  char lablabel[MAXIDSIZE + 1], destbuf[3];

  /* generate a jsr/jmp instruction */

  sprintf (lablabel, "%s:", labname);

  if (!exist (lablabel, label))
    strcpy (destbuf, "* ");	/* for later check */
  else
    strcpy (destbuf, "  ");	/* label already defined */

  gen (branch, labname, destbuf);
}

void assem (void)
{
/* 
   ** ASSEM..END ASSEM -- inline assembly code inclusion.
 */

  report_errors = FALSE;	/* suppress "unknown symbol" errors. */

  do
    {
      insymbol ();

      /* generate code? */
      if (sym == endofline && !end_of_source &&
	  lastsym != assemsym)
	gen (lastline, "  ", "  ");
    }
  while (sym != endsym && !end_of_source);

  insymbol ();
  if (sym != assemsym)
    _error (73);
  else
    insymbol ();

  report_errors = TRUE;
}

void parse_option_list (void)
{
  char letter;
  BOOL activate;
/* OPTION <switch>+|-[,<switch>+|-..] */

  do
    {
      insymbol ();

      if (sym == ident && strlen (ut_id) == 1)
	{
	  letter = ut_id[0];

	  insymbol ();
	  if (sym == plus || sym == minus)
	    {
	      switch (sym)
		{
		case plus:
		  activate = TRUE;
		  break;
		case minus:
		  activate = FALSE;
		  break;
		}

	      switch (letter)
		{
		case 'b':
		  break_opt = activate;
		  break;
		case 'c':
		  asm_comments = activate;
		  break;
		case 'E':
		  if (activate && !error_log)
		    {
		      err_log = fopen ("ace.err", "w");
		      if (err_log == NULL)
			puts ("Unable to open error log: ace.err!");
		      else
			error_log = TRUE;
		    }
		  else if (!activate && error_log)
		    {
		      if (err_log)
			{
			  fclose (err_log);
			  err_log = NULL;
			  error_log = FALSE;
			}
		      else
			puts ("Error log: ace.err not open!");
		    }
		  break;
		case 'i':
		  make_icon = activate;
		  break;
		case 'l':
		  list_source = activate;
		  break;
		case 'm':
		  module_opt = activate;
		  break;
		case 'O':
		  optimise_opt = activate;
		  break;
		case 'w':
		  wdw_close_opt = activate;
		  break;

		default:
		  _error (74);
		  break;
		}
	    }
	  else
	    _error (74);
	}
      else
	_error (74);		/* compiler directive expected */

      insymbol ();
    }
  while (sym == comma);
}

void MsgBox (void)
{
/*
   ** MsgBox _statement_.
   **
   **   MSGBOX <message>,<button-text>
   **
   ** See also basfun.c for MsgBox *function*. 
 */

  insymbol ();

  if (expr () != stringtype)	/* message */
    _error (4);
  else
    {
      if (sym != comma)
	_error (16);
      else
	{
	  insymbol ();
	  if (expr () != stringtype)	/* response text */
	    _error (4);
	  else
	    {
	      /* no second button! (pass NULL) */
	      gen ("move.l", "#0", "-(sp)");

	      /* call the function */
	      gen ("jsr", "_sysrequest", "  ");
	      gen ("add.l", "#12", "sp");
	      enter_XREF ("_sysrequest");
	      enter_XREF ("_IntuitionBase");
	    }
	}
    }
}

long fsize(char *name)
{
	if(access(name, 0x00))
/*		return(_dta.st_size); */
	return(ERROR);
}

