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

 caller.c

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

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

/************************************************************************
 *                                                                      *
 * fpl.library - A shared library interpreting script langauge.         *
 * Copyright (C) 1992-1994 FrexxWare                                    *
 * Author: Daniel Stenberg                                              *
 *                                                                      *
 * This program is free software; you may redistribute for non          *
 * commercial purposes only. Commercial programs must have a written    *
 * permission from the author to use FPL. FPL is *NOT* public domain!   *
 * Any provided source code is only for reference and for assurance     *
 * that users should be able to compile FPL on any operating system     *
 * he/she wants to use it in!                                           *
 *                                                                      *
 * You may not change, resource, patch files or in any way reverse      *
 * engineer anything in the FPL package.                                *
 *                                                                      *
 * 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.                 *
 *                                                                      *
 * Daniel Stenberg                                                      *
 * Ankdammsgatan 36, 4tr                                                *
 * S-171 43 Solna                                                       *
 * Sweden                                                               *
 *                                                                      *
 * FidoNet 2:201/328    email:dast@sth.frontec.se                       *
 *                                                                      *
 ************************************************************************/

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

int CXBRK(void) { return(0); }  /* Disable Lattice/SAS 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>

#if defined(__GCC_NEW_VARARGS__) /* && defined(__GCC__) */
/*
 * If you want this file to be compiled using the va_* macros instead of the
 * dirty "address of parameter version", define as below!
 * (Without this define, SunOS 4.1.x versions will likely crash!)
 */

#define VARARG_FUNCTIONS /* should be defined before the includes below */
#endif


#define REG(x)
#define TRUE  1
#define FALSE 0
#include "../include/libraries/FPL.h"
#include "../include/clib/FPL_protos.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,
  FN_OPENL,
  FN_CLOSEL
  };

/**********************************************************************
 *
 * 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

  if(argc<2) {
    printf("Usage: SFPL <FPL program file name>\n");
#if defined(AMIGA) && defined(SHARED)
    CloseLibrary((struct Library *)FPLBase);
#endif
    return 0;
  }

  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, FPLCACHE_EXPORTS,
		  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) {
    char *string=NULL;
    end=fplExecuteFileTags(key, argv[1],
			   FPLTAG_STRING_RETURN, &string,
                           FPLTAG_DONE);
    if(string) {
      printf("The program returned '%s'\n", string);
      fplFreeString(key, string);
    }
  }

#if 0  
  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);
#endif

  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");

  return end;
}

#ifndef AMIGA /* if not using SAS/C on Amiga */

/******************************************************/
/* Parameter list frontends of the library functions: */
/******************************************************/

#if VARARG_FUNCTIONS

/*
 * The following stub functions to the actual library functions was done
 * to make this run under SunOS 4.1.x using gcc. Thanks to Bernd Noll
 * (b_noll@sun.rhrk.un) for this contribution!
 */

long fplExecuteScriptTags(void *anchor, char **program, long lines, ...)
{
  va_list tags;
  long ret;
  va_start(tags, lines); /* get parameter list */
  ret = fplExecuteScript(anchor, program, lines, (unsigned long *)tags);
  va_end(tags)
  return ret;
}

long fplExecuteFileTags(void *anchor, char *program, ...)
{
  va_list tags;
  long ret;
  va_start(tags, program); /* get parameter list */
  ret = fplExecuteFile(anchor, program, (unsigned long *)tags);
  va_end(tags)
  return ret;
}

void *fplInitTags(long (*func)(struct fplArgument *), ...)
{
  va_list tags;
  long ret;
  va_start(tags, func); /* get parameter list */
  ret = fplInit(func, (unsigned long *)tags);
  va_end(tags)
  return ret;
}

long fplResetTags(void *anchor, ...)
{
  va_list tags;
  long ret;
  va_start(tags, anchor); /* get parameter list */
  ret = fplReset(anchor, (unsigned long *)tags);
  va_end(tags)
  return ret;
}

long fplSendTags(void *anchor, ...)
{
  va_list tags;
  long ret;
  va_start(tags, anchor); /* get parameter list */
  ret = fplSend(anchor, (unsigned long *)tags);
  va_end(tags)
  return ret;
}

long fplAddFunctionTags(void *anchor, char *name, long ID, char rtrn,
                        char *format, ...)
{
  va_list tags;
  long ret;
  va_start(tags, format); /* get parameter list */
  ret = fplAddFunction(anchor, name, ID, rtrn, format, (unsigned long *)tags);
  va_end(tags)
  return ret;
}

#else /* VARARG_FUNCTIONS */

long fplExecuteScriptTags(void *anchor, char **program, long lines,
                          unsigned long tags, ...)
{
  return(fplExecuteScript(anchor, program, lines, (unsigned long *)&tags));
}

long fplExecuteFileTags(void *anchor, char *program, unsigned long tags, ...)
{
  return(fplExecuteFile(anchor, program, (unsigned long *)&tags));
}

void *fplInitTags(long (*func)(struct fplArgument *), unsigned long tags, ...)
{
  return(fplInit(func, (unsigned long *)&tags));
}

long fplResetTags(void *anchor, unsigned long tags, ...)
{
  return(fplReset(anchor, &tags));
}

long fplSendTags(void *anchor, unsigned long tags, ...)
{
  return(fplSend(anchor, &tags));
}

long fplAddFunctionTags(void *anchor, char *name, long ID, char rtrn,
                        char *format, unsigned long tags, ...)
{
  return(fplAddFunction(anchor, name, ID, rtrn, format, &tags));
}

#endif

#endif

long ASM func(REG(a0) struct fplArgument *arg)
{
  int ret;
  long col;
  char *name;
  char *string;
  void *anchor=arg->key;
  char systemline[80];
  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(FPLERR_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=FPLERR_OUT_OF_MEMORY;
    return(ret);

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

#ifdef HIJACK
  /*
   * Not implemented, and not subject to either in the nearest future!
   */
  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;
#endif
  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;
#ifdef COMPILE_AVAIL
  case FPL_COMPILE: /* for experiments! */
    if(arg->format[0]==FPL_STRARG)
      printf("%s", (char *)arg->funcdata);
    else
      printf("%d", (int)arg->funcdata);
    break;
#endif
  case FN_GETINT:
    if(arg->argc)
      printf("%s", (char *)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;
#if 0
  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"); */
  }
#endif

  (*(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);
}
