lpc4/lib/
lpc4/lib/doc/efun/
lpc4/lib/doc/lfun/
lpc4/lib/doc/operators/
lpc4/lib/doc/simul_efuns/
lpc4/lib/doc/types/
lpc4/lib/etc/
lpc4/lib/include/
lpc4/lib/include/arpa/
lpc4/lib/obj/d/
lpc4/lib/save/
lpc4/lib/secure/
lpc4/lib/std/
lpc4/lib/std/living/
#include "global.h"
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <math.h>
#include <setjmp.h>
#include <signal.h>
#include <ctype.h>
#if defined(sun)
#include <alloca.h>
#endif
#ifdef STDC_HEADERS
#include <stdarg.h>
#endif

#include "simulate.h"
#include "interpret.h"
#include "object.h"
#include "exec.h"
#include "lex.h"
#include "main.h"
#include "array.h"
#include "stralloc.h"
#include "backend.h"
#include "simul_efun.h"

#ifdef LACIP
#include "lacip/op.h"
#endif

#ifdef GETRUSAGE_THROUGH_PROCFS
#include <sys/procfs.h>
#include <sys/fcntl.h>
int proc_fd;

void open_proc_fd()
{
  char proc_name[20];

  sprintf(proc_name, "/proc/%05ld", (long)getpid());
  proc_fd = open(proc_name, O_RDONLY);
  if(proc_fd < 0)
  {
    fprintf(stderr, "Couldn't open %s\n", proc_name);
  }
}
#endif

extern char *prog;
extern int current_line;

extern int lasdebug;
int d_flag = 0;	/* Run with debug */
int t_flag = 0;	/* Disable heart beat and reset */
int comp_flag = 0; /* Trace compilations */
int T_flag = 0;
#ifdef WARN
int W_flag = 0;
#endif
int batch_mode=0; /* he he */

void init_stdin_handler();
extern int stdin_closed,stdinisatty,stdin_is_sock;

#ifdef YYDEBUG
extern int yydebug;
#endif

char *reserved_area=NULL;	/* reserved for malloc() */
struct svalue const_empty_string;

char *hostname;

double consts[NUM_CONSTS];

extern jmp_buf error_recovery_context;
extern int error_recovery_context_exists;

extern struct object *master_ob;
extern int eval_cost;


unsigned long signals;
RETSIGTYPE sig_lpc(int s)
{
  signal(s,sig_lpc);
  signals|=1<<s;
}

RETSIGTYPE sig_child(int s)
{
  wait(&s);
  signal(s,sig_child);
}

/* don't forget to change LF_MAX in exec.h */
char *initial_lfun_list[]=
{
  "catch_tell",
  "heart_beat",
  "init",
  "exit",
} ;

extern void init_stacks();
extern void init_sockets();

int main(int argc,char ** argv,char **env)
{
  struct svalue *ret;
  extern int game_is_being_shut_down;
  extern int current_time;
  int i, new_mudlib = 0;
  char *p;

#ifdef GETRUSAGE_THROUGH_PROCFS
  open_proc_fd();
#endif


#ifdef MALLOC_gc
  gc_init();
#endif
  SET_STR(&const_empty_string,make_shared_string(""));

  lfuns=(char **)xalloc(sizeof(char **)*NELEM(initial_lfun_list));
  for(i=0;i<NELEM(initial_lfun_list);i++)
    lfuns[i]=make_shared_string(initial_lfun_list[i]);
  num_lfuns=i;

  init_stacks();
  p=(char *)malloc(100);
  gethostname(p,100);
  p[100-1]=0;
  hostname=make_shared_string(p);
  free(p);
    
#ifdef debugmalloc
  malloc_debug(2);
#endif

  /*
   * Check that the definition of EXTRACT_UCHAR() is correct.
   */
  p = (char *)&i;
  *p = -10;
  if (EXTRACT_UCHAR(p) != 0x100 - 10) {
    fprintf(stderr, "Bad definition of EXTRACT_UCHAR() in machine.h.\n");
    exit(1);
  }
  my_srand(get_current_time());
  current_time = get_current_time();
  for (i=0; i < NELEM(consts); i++)
    consts[i] = exp(- i / 900.0);
  init_num_args();
  reset_machine(1);

  /*
   * The flags are parsed twice !
   * The first time, we only search for the -m flag, which specifies
   * another mudlib, and the D-flags, so that they will be available
   * when compiling master.c.
   */
  for (i=1; i < argc; i++)
  {
    if (argv[i][0] != '-') continue;

    switch(argv[i][1])
    {
    case 'D':
      if (argv[i][2])
      {
	/* Amylaar : allow flags to be passed down to
	   the LPC preprocessor */
	struct lpc_predef_s *tmp;
	  
	tmp = (struct lpc_predef_s *) malloc(sizeof(struct lpc_predef_s));
	if (!tmp) fatal("malloc failed");
	tmp->flag = argv[i]+2;
	tmp->next = lpc_predefs;
	lpc_predefs = tmp;
	continue;
      }
      fprintf(stderr, "Illegal flag syntax: %s\n", argv[i]);
      exit(1);
      break;

    case 'm':
      if (chdir(argv[i]+2) == -1)
      {
	fprintf(stderr, "Bad mudlib directory: %s\n", argv[i]+2);
	exit(1);
      }
      new_mudlib = 1;
      break;

    case 'I':
      stdin_is_sock=1;
      break;
      
    case 'C':
      batch_mode=1;
      i=argc;			/* the rest of the flags are 'mud only' */
    }
  }

  if(!batch_mode)
  {
    if (!new_mudlib && chdir(MUD_LIB) == -1)
    {
      fprintf(stderr, "Bad mudlib directory: %s\n", MUD_LIB);
      exit(1);
    }
  }

  if (RESERVED_SIZE > 0 && !batch_mode)
    reserved_area = malloc(RESERVED_SIZE);

  if(stdin_is_sock)
  {
    stdin_closed=1;
    stdinisatty=0;
  }else{
    init_stdin_handler();
  }

  if (setjmp(error_recovery_context))
  {
    clear_state();
    fprintf(stderr,"Fatal failiure when loading master object!\n");
  }else{
    error_recovery_context_exists = 1;
    assert_master_ob_loaded();
  }

  if((ret=apply_master_ob("get_cached_functions",0)))
  {
    if(!IS_ZERO(ret))
    {
      if(ret->type==T_POINTER)
      {
	int t;
	lfuns=(char **)realloc((char *)lfuns,sizeof(char **)*(ret->u.vec->size+num_lfuns));
	if(!lfuns)
	{
	  fprintf(stderr,"Couldn't allocate space for cached functions.\n");
	  exit(10);
	}
	for(i=0;i<ret->u.vec->size;i++)
	{
	  if(ret->u.vec->item[i].type!=T_STRING)
	  {
	    fprintf(stderr,"get_cached_function() returned an array containing non-strings.\n");
	    exit(10);
	  }
	  for(t=0;t<num_lfuns;t++)
	    if(lfuns[t]==strptr(ret->u.vec->item+i))
	      break;
	  if(t==num_lfuns)
	  {
	    lfuns[num_lfuns]=copy_shared_string(strptr(ret->u.vec->item+i));
	    num_lfuns++;
	  }
	}
      }else{
	fprintf(stderr,"Warning: get_cached_functions() in master.c doesn't return an array or zero.\n");
      }
    }
  }

  error_recovery_context_exists = 0;

  if(batch_mode)
  {
    push_new_shared_string(BINDIR);
    push_new_shared_string(MUD_LIB);
    set_inc_list(apply_master_ob("define_include_dirs", 2));
  }else{
    set_inc_list(apply_master_ob("define_include_dirs", 0));
  }

  init_sockets();		/* initialize efun sockets           */

  signal(SIGCHLD, sig_child);
  signal(SIGINT, sig_lpc);
  signal(SIGQUIT, sig_lpc);
  signal(SIGTERM, sig_lpc);
  signal(SIGUSR1, sig_lpc);
  signal(SIGUSR2, sig_lpc);
  signal(SIGPIPE, SIG_IGN);
#if 0
#ifndef DEBUG
  if(-1!=find_function("signal",master_ob->prog))
  {
    signal(SIGSEGV, sig_lpc);
    signal(SIGFPE, sig_lpc);
    signal(SIGBUS, sig_lpc);
  }
#endif
#endif

  for (i=1; i < argc; i++)
  {
    if (argv[i][0] != '-')
    {
      fprintf(stderr, "Bad argument %s\n", argv[i]);
      exit(1);
    } else {
      /*
       * Look at flags. -m and -o has already been tested.
       */
      switch(argv[i][1])
      {
      case 'f':
	eval_cost=0;
	push_new_shared_string(argv[i]+2);
	APPLY_MASTER_OB((void),"flag", 1);
	if (game_is_being_shut_down) {
	  fprintf(stderr, "Shutdown by master object.\n");
	  exit(0);
	}
	continue;

      case 'C':
      {
	struct vector *fo,*env_fo;
	int e;
	batch_mode=1;
	eval_cost=0;
	push_new_shared_string(argv[i]+2);
	i++;
	fo=allocate_array_no_init(argc-i,0);
	for(e=0;i<argc;i++,e++)
	{
	  SET_STR(fo->item+e,make_shared_string(argv[i]));
	}
	for(e=0;env[e];e++);

	env_fo=allocate_array_no_init(e,0);
	for(e=0;env[e];e++)
	{
	  SET_STR(env_fo->item+e,make_shared_string(env[e]));
	}
	  
	push_vector(fo);
	push_vector(env_fo);
	fo->ref--;
	env_fo->ref--;
	(void)apply_master_ob("batch", 3);
	if (game_is_being_shut_down) {
	  exit(0);
	}
	i=argc;			/* the rest of the flags are 'mud only' */
      }
      case 'I':
	push_number(batch_mode);
	(void)apply_master_ob("stdin_is_sock",1);
	continue;

      case 'D':
	continue;
      case 'N':
	continue;
      case 'm':
	continue;
#define OPTION(flag,variable) case flag: if(isdigit(argv[i][2])) variable=atoi(argv[i]+2); else variable++; break

        OPTION('l',lasdebug);
        OPTION('d',d_flag);
        OPTION('c',comp_flag);
        OPTION('t',t_flag);
        OPTION('T',T_flag);
#ifdef WARN
        OPTION('W',W_flag);
#endif
#ifdef YYDEBUG
        OPTION('y',yydebug);
#endif
      default:
	fprintf(stderr, "Unknown flag: %s\n", argv[i]);
	exit(1);
      }
    }
  }
    
  if (game_is_being_shut_down)
    exit(1);
#ifdef OPCPROF
  check_cost_for_instr(-1);
#endif
  backend();

  return 0;
}

char *string_copy(char *str)
{
  char *p;

  p = xalloc(strlen(str)+1);
  (void)strcpy(p, str);
  return p;
}


void debug_message_va(char *a,va_list args)
{
  static FILE *fp = NULL;
  char deb[100];

  /* nej tack, jag k|r */
  if(batch_mode)
    return;

  if (fp == NULL)
  {
    sprintf(deb,"%s.debug.log",hostname);
    fp = fopen(deb, "w");
    if (fp == NULL)
    {
      perror(deb);
      abort();
    }
  }
  (void)VFPRINTF(fp, a, args);
  (void)fflush(fp);
}

void debug_message(char *a, ...)
{
  va_list args;
  va_start(args,a);
  debug_message_va(a,args);
  va_end(args);
}

#ifdef WARN
void warn(int w_level,char *a, ...)
{
  va_list args;
  if(W_flag<w_level) return;
  va_start(args,a);
  VFPRINTF(stderr,a,args);
  va_end(args);
}
#else
void warn(int w, char *a, ...) {}
#endif
void debug_message_svalue(struct svalue *v)
{
  if (v == 0)
  {
    debug_message("<NULL>");
    return;
  }
  switch(v->type)
  {
  case T_NUMBER:
    debug_message("%d", v->u.number);
    return;
  case T_STRING:
    debug_message("\"%s\"", strptr(v));
    return;
  case T_OBJECT:
    debug_message("OBJ(%s)", v->u.ob->prog->name);
    return;
  case T_LVALUE:
    debug_message("LVALUE");
    return;
  default:
    debug_message("<INVALID>");
    return;
  }
}

#ifdef malloc
#undef malloc
#endif

int slow_shut_down_to_do = 0;

char *xalloc(int size)
{
  extern void do_gc(int);
  char *p;
  static int going_to_exit;

  if (going_to_exit) exit(3);
  if (size == 0) fatal("Tried to allocate 0 bytes.\n");

  if((p = malloc(size))) return p;

  do_gc(10000);
  if((p = malloc(size))) return p;
  
  if (reserved_area)
  {
    free(reserved_area);
    p = "Temporary out of MEMORY. Freeing reserve.\n";
    write(1, p, strlen(p));
    reserved_area=0;
    slow_shut_down_to_do++;
    if((p = malloc(size))) return p;
  }
  going_to_exit = 1;
  p = "Totally out of MEMORY.\n";
  write(1, p, strlen(p));
  (void)dump_trace(0);
  exit(2);
  return 0;
}