/******************************************************************************
 *		           FREXX PROGRAMMING LANGUAGE    		      *
 ******************************************************************************

 caller.c

 For FPL debugging...
 Only part of the executable file version of FPL.

 *****************************************************************************/

/************************************************************************
 *                                                                      *
 * fpl.library - A run time library interpreting script langauge.       *
 * Copyright (C) 1992, 1993 FrexxWare                                   *
 * Author: Daniel Stenberg                                              *
 *                                                                      *
 * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                      *
 * Daniel Stenberg                                                      *
 * Birger Jarlsgatan 93b 3tr                                            *
 * 113 56 Stockholm                                                     *
 * Sweden                                                               *
 *                                                                      *
 * FidoNet 2:201/328                                                    *
 *                                                                      *
 ************************************************************************/

#ifdef AMIGA
#include <exec/types.h>
#include <proto/exec.h>

int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */
int chkabort(void) { return(0); }  /* really */
#ifdef SHARED
#include <exec/libraries.h>
#include <libraries/dos.h>

#include "/include/pragmas/FPL_pragmas.h"
#include "/include/clib/FPL_protos.h"
#include "FPL.h"
struct Library *FPLBase = NULL;
#endif

#define REG(x) register __ ## x

#elif defined(UNIX) /* #ifdef AMIGA */
#include <sys/types.h>
#define REG(x)
#include "../include/clib/FPL_protos.h"
#include "../include/libraries/FPL.h"
#endif

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

#if defined(AMIGA) && defined(SHARED)
#define CALLER __saveds
#define ASM __asm
#else
#define CALLER
#define ASM
#endif

long ASM func(REG(a0) struct fplArgument *);
long ASM inter(REG(a0) void *);
void CALLER ASM MyFree(REG(a1) void *, REG(d0) long);
void CALLER ASM *MyAlloc(REG(d0) long);
long ASM newline(REG(a0) void *);

int memory_counter=0;
int maxmemory_counter=0;
int mallocs=0;

int newlines=0;

enum myfunctions {
  FN_GETINT, FN_GETSTRING, FN_OUTPUT, FN_EXECUTE, FN_PRINTF, FN_TEST
  };

/**********************************************************************
 *
 * int main(int, char **)
 *
 * This function is not included in the run time library version.
 *
 ******/

void *key;

int main(int argc, char **argv)
{
  long n, end=0;
  long count=0;
  int pre_mallocs;
  long pre_malloc;
  struct fplSymbol *symbols;
  long i;


#if defined(SHARED) && defined(AMIGA)
  if(!(FPLBase=OpenLibrary(FPLNAME, 5))) {
    printf("Error opening %s!\n", FPLNAME);
    return(-1);
  }
  printf("--> %s\n", FPLBase->lib_IdString);
#endif

  key=fplInitTags(func,
		  FPLTAG_INTERVAL, (unsigned long)inter, /* interval func */
		  FPLTAG_USERDATA, (unsigned long)&count, /* user data */
		  FPLTAG_INTERNAL_DEALLOC, (unsigned long)MyFree,
		  FPLTAG_INTERNAL_ALLOC, (unsigned long)MyAlloc,
		  FPLTAG_NEWLINE_HOOK, (long)newline,
		  FPLTAG_CACHEALLFILES, 1,
		  FPLTAG_ALLFUNCTIONS, 1,
		  FPLTAG_DONE);

  fplAddFunction(key, "getint",    FN_GETINT,    'I', "s", NULL);
  fplAddFunction(key, "getstring", FN_GETSTRING, 'S', "s", NULL);
  fplAddFunction(key, "output",	   FN_OUTPUT,    'I', "O", NULL);
  fplAddFunction(key, "executeFPL",FN_EXECUTE,   'I', "S", NULL);
  fplAddFunction(key, "printf",    FN_PRINTF,    'I', "So>", NULL);
  fplAddFunction(key, "Test",      FN_TEST,      'I', "SI", NULL);

  pre_mallocs=mallocs;
  pre_malloc=memory_counter;

#ifdef HIJACK
  end=fplExecuteFileTags(key, "cpp.FPL", FPLTAG_COMPILE, TRUE, FPLTAG_DONE);

  fplHijack(key, "__LINE__");
  fplHijack(key, "__TIME__");
  fplHijack(key, "__DATE__");
  fplHijack(key, "__FILE__");
#endif
  if(!end && argc>1) {
    int go=FALSE;
    if(argc>2)
      go = TRUE;
    end=fplExecuteFileTags(key, argv[1], FPLTAG_COMPILE, go, FPLTAG_DONE);
  }
  
  fplSendTags(key, FPLSEND_GETSYMBOL_FUNCTIONS, &symbols, FPLSEND_DONE);
  printf("\n---------------------\nAll exported functions:\n");
  for(i=0; i<symbols->num; i++)
    printf("%s ", symbols->array[i]);
  fplSendTags(key, FPLSEND_GETSYMBOL_FREE, symbols, FPLSEND_DONE);

  fplSendTags(key, FPLSEND_GETSYMBOL_VARIABLES, &symbols, FPLSEND_DONE);
  printf("\n---------------------\nAll exported variables:\n");
  for(i=0; i<symbols->num; i++)
    printf("%s ", symbols->array[i]);
  fplSendTags(key, FPLSEND_GETSYMBOL_FREE, symbols, FPLSEND_DONE);

  fplSendTags(key, FPLSEND_GETSYMBOL_CACHEDFILES, &symbols, FPLSEND_DONE);
  printf("\n---------------------\nAll cached files:\n");
  for(i=0; i<symbols->num; i++)
    printf("%s ", symbols->array[i]);
  fplSendTags(key, FPLSEND_GETSYMBOL_FREE, symbols, FPLSEND_DONE);


  fplSendTags(key,
	      FPLSEND_GETRETURNCODE, &n,
	      FPLSEND_FLUSHFILE, 0,
	      FPLSEND_FLUSHCACHE, 1,
	      FPLSEND_DONE);

  fplFree(key); /* free all shit FPL uses internally */

#if defined(AMIGA) && defined(SHARED)
  CloseLibrary((struct Library *)FPLBase);
#endif
  
  printf("\n-----------------------------------------\n");
  printf("Return code   :  %d\n", n);
  printf("Interval func :  %d\n", count);
  printf("Newlines      :  %d\n", newlines);
  printf("Pre mallocs   :  %d\n", pre_mallocs);
  printf("Pre memory use:  %d\n", pre_malloc);
  printf("Malloc        :  %d\n", mallocs-pre_mallocs);
  printf("memory use    :  %d\n", maxmemory_counter-pre_malloc);
  printf("Not freed mem :  %d\n", memory_counter);
  printf("-----------------------------------------\n");

  exit(end);
}

long ASM func(REG(a0) struct fplArgument *arg)
{
  int ret;
  long col;
  char *name;
  char *string;
  void *anchor=arg->key;
  switch(arg->ID) {
  case FN_PRINTF:
#if defined(AMIGA)
    vprintf(arg->argv[0], (char *)&arg->argv[1]);
#elif defined(UNIX)
    vfprintf(stderr, arg->argv[0], (char *)&arg->argv[1]);
#endif
    break;
  case FN_OUTPUT: /* output */
    if(arg->format[0]==FPL_STRARG)  /* we got a string! */
      string="%s";
    else
      string="%d";
#if defined(AMIGA)
    printf(string, arg->argv[0]);
#elif defined(UNIX)
    fprintf(stderr, string, arg->argv[0]);
#endif
    fplSendTags(anchor, FPLSEND_INT, 1, FPLSEND_DONE);
#if 0
    if(count++>10)
      return(FPL_PROGRAM_STOPPED);
#endif
    break;
  case FN_GETSTRING:
    if(string=(char *)fplAlloc(anchor, 64)) {
      if(arg->argc)
	printf("%s", arg->argv[0]);
      fgets(string, 64, stdin);
      ret=fplSendTags(arg->key,
		      FPLSEND_STRING, string,
		      FPLSEND_STRLEN, strlen(string)-1,
		      FPLSEND_DONE);
      fplDealloc(anchor, string);
    } else
      ret=FPL_OUT_OF_MEMORY;
    return(ret);

  case FN_EXECUTE:
    ret=fplExecuteFile(anchor, arg->argv[0], NULL);
    return(ret);
    break;

  case FPL_HIJACK_READ:
    if(!strcmp("__LINE__", arg->name)) {
      fplSendTags(anchor, FPLSEND_GETVIRLINE, &col, FPLSEND_DONE);
      fplSendTags(anchor, FPLSEND_INT, col, FPLSEND_DONE);
    } else if(!strcmp("__FILE__", arg->name)) {
      fplSendTags(anchor, FPLSEND_GETVIRFILE, &name, FPLSEND_DONE);
      if(*name=='\"') {
	name++;
	col=0;
	while(name[col] && name[col]!='\"')
	  col++;
      } else
	col=-1;
      fplSendTags(anchor, FPLSEND_STRING, name,
		  FPLSEND_STRLEN, col, FPLSEND_DONE);
    } else
      fprintf(stderr, "%s variable read!\n", arg->name);
    break;

  case FPL_GENERAL_ERROR:
    {
      char buffer[FPL_ERRORMSG_LENGTH];
      fplSendTags(anchor,
		  FPLSEND_GETVIRLINE, &col,
		  FPLSEND_GETVIRFILE, &name,
		  FPLSEND_DONE);
      if(*name=='\"') {
	ret=0;
	name++;
	while(name[ret] && name[ret]!='\"')
	  ret++;
	string=(char *)fplAlloca(anchor, ret+1);
	memcpy(string, name, ret);
	string[ret]='\0';
      } else {
	string=name;
	ret=0;
      }
      printf("\n>>> %s\n",
	     fplGetErrorMsg(arg->key, (long)arg->argv[0], buffer));
      printf(">>> Line %d in file \"%s\". <<<\n", col, string);
      if(ret)
	fplDealloca(anchor, string);
    }
    break;
  case FPL_UNKNOWN_FUNCTION:
    col=22; /* only to breakpoint */
    break;

  case FPL_COMPILE: /* for experiments! */
    if(arg->format[0]==FPL_STRARG)
      printf("%s", arg->funcdata);
    else
      printf("%d", arg->funcdata);
    break;
  case FN_GETINT:
    if(arg->argc)
      printf("%s", arg->argv[0]);
    scanf("%d", &ret);
    ret=fplSendTags(anchor, FPLSEND_INT, ret, FPLSEND_DONE);
    return(ret);
  }
  return(0);
}

long ASM inter(REG(a0) void *count)
{
  static line=-1;
  static char *name;
  long current_line;
  char *curr_name;

  fplSendTags(key, FPLSEND_GETVIRLINE, &current_line, FPLSEND_DONE);
  fplSendTags(key, FPLSEND_GETVIRFILE, &curr_name, FPLSEND_DONE);
  if(line!=current_line || name != curr_name) {
    line=current_line;
    name=curr_name;
/*    fprintf(stderr, " < %d %s > ", line, name?name:"unknwon"); */
  }

  (*(int *)count)++; /* just to count the number of times this routine has been
			called. */
  return(0);
}

void CALLER ASM MyFree(REG(a1) void *pntr, REG(d0) long size)
{
  memory_counter-=size;
  memset(pntr, 0xaa, size); /* mess up this area before free! */
  free(pntr);
}

long ASM newline(REG(a0) void *anchor)
{
  /* just for fun! */
  newlines++;
  return(0);
}

void CALLER ASM *MyAlloc(REG(d0) long size)
{
  void *mem;
  mallocs++;
  if((memory_counter+=size)>maxmemory_counter)
    maxmemory_counter=memory_counter;
#ifdef UNIX
  mem=malloc(size);
#else
  mem=AllocMem(size, 0);
#endif
  if(mem)
    memset(mem, 0xaa, size); /* mess up this area before free! */
  return (mem);
}
