#include "config.h"
#include <sys/types.h>
#ifndef LATTICE
#include <sys/socket.h>
#endif
#include <stdio.h>
#if defined(__386BSD__) || defined(SunOS_5)
#include <stdlib.h>
#include <unistd.h>
#endif
#include <string.h>
#include <math.h>
#include <setjmp.h>
#include <signal.h>
#if defined(sun)
#include <alloca.h>
#endif
#ifndef LATTICE
#include <varargs.h>
#else
#include "amiga.h"
#endif
#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "lex.h"
#include "sent.h"
#include "md.h"
#include "arch.h" /* after config.h */

extern char *prog;

extern int current_line;

int d_flag = 0;	/* Run with debug */
int t_flag = 0;	/* Disable heart beat and reset */
int e_flag = 0;	/* Load empty, without castles. */
int comp_flag = 0; /* Trace compilations */
int max_cost;
int time_to_swap;
int time_to_clean_up;
char *default_fail_message;

#ifdef YYDEBUG
extern int yydebug;
#endif

int port_number;
int boot_time;
int max_array_size;
int max_string_length;
char *master_file_name;
int reserved_size;
char *reserved_area;	/* reserved for MALLOC() */
char *mud_lib;

struct svalue const0, const1, const0u, const0n;

double consts[NUM_CONSTS];

extern jmp_buf error_recovery_context;
extern int error_recovery_context_exists;

extern struct object *master_ob;

#ifndef NO_IP_DEMON
void init_addr_server();
#endif /* NO_IP_DEMON */

#ifdef TRAP_CRASHES
static void sig_usr1();
static void sig_term();
static void sig_int();
#ifndef DEBUG
static void sig_int(), sig_hup(),
            sig_abrt(), sig_segv(), sig_ill(), sig_bus(), sig_iot();
#endif /* DEBUG */
#endif

#ifdef DEBUG_MACRO
/* used by debug.h: please leave this in here -- Tru (you can change its
   value if you like).
*/
int debug_level = 32768;
#endif /* DEBUG_MACRO */

int main(argc, argv)
     int argc;
     char **argv;
{
  extern int MudOS_is_being_shut_down;
  extern int current_time;
  int i, new_mudlib = 0, got_defaults = 0;
  int no_ip_demon = 0;
  char *p;
  char version_buf[80];
  struct svalue *ret;
  void init_strings(), init_otable();
  int dtablesize;
#ifdef SAVE_BINARIES
  void init_binaries();
#endif
#if !defined(LATTICE) && !defined(OLD_ULTRIX) && !defined(sequent)
  void tzset();
#endif
  struct lpc_predef_s predefs;
#ifdef GCMALLOC
  extern void gc_init();
  gc_init();
#endif /* GCMALLOC */
#ifdef WRAPPEDMALLOC
  wrappedmalloc_init();
#endif /* WRAPPEDMALLOC */
#ifdef DEBUGMALLOC
  MDinit();
#endif

#if (defined(PROFILING) && !defined(PROFILE_ON) && defined(HAS_MONCONTROL))
  moncontrol(0);
#endif
#if !defined(OLD_ULTRIX) && !defined(LATTICE) && !defined(sequent)
  tzset();
#endif
  boot_time = get_current_time();
  get_version(version_buf);
  printf("%s (%s)\n",version_buf, ARCH);

  const0.type = T_NUMBER; const0.u.number = 0;
  const1.type = T_NUMBER; const1.u.number = 1;
  /* const0u used by undefinedp() */
  const0u.type = T_NUMBER; const0u.subtype = T_UNDEFINED;
  const0u.u.number = 0;
  /* const0n used by nullp() */
  const0n.type = T_NUMBER; const0n.subtype = T_NULLVALUE;
  const0n.u.number = 0;
  /*
   * 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 config.h.\n");
    exit(-1);
  }
#ifdef DRAND48
  srand48(get_current_time());
#else
#ifdef RANDOM
  srandom(get_current_time());
#else
  fprintf(stderr,"Warning: no random number generator specified!\n");
#endif
#endif /* DRAND48 */
  current_time = get_current_time();;
  /*
   * Initialize the microsecond clock.
   */
  init_usec_clock();

  /* read in the configuration file */

  got_defaults = 0;
  for(i=1; (i < argc) && !got_defaults; i++){
    if(argv[i][0] != '-'){
      set_defaults(argv[i]);
      got_defaults = 1;
    }
  }
  if(!got_defaults){
    fprintf(stderr,
	    "You must specify the configuration filename as an argument.\n");
    exit(-1);
  }
  init_strings(); /* in stralloc.c */
  init_otable();  /* in otable.c */

  /*
   * We estimate that we will need MAX_USERS + MAX_EFUN_SOCKS + 10 file
   * descriptors if the maximum number of users were to log in and all LPC
   * sockets were in use.  This is a pretty close estimate.
   */
  dtablesize = MAX_USERS + MAX_EFUN_SOCKS + 10;

  /*
   * If our estimate is larger than FD_SETSIZE, then we need more file
   * descriptors than the operating system can handle.  This is a problem
   * that can be resolved by decreasing MAX_USERS, MAX_EFUN_SOCKS, or both.
   */
  if (dtablesize > FD_SETSIZE) {
    fprintf(stderr,
	    "Warning: File descriptor requirements exceed system capacity!\n");
    fprintf(stderr,
	    "         Configuration exceeds system capacity by %d descriptor(s).\n",
	    FD_SETSIZE - dtablesize);
  }
#ifdef HAS_SETDTABLESIZE
  /*
    * If the operating system supports setdtablesize() then we can request
    * the number of file descriptors we really need.  First check to see if
    * wee already have enough.  If so dont bother the OS. If not, attempt to
    * allocate the number we estimated above.  There are system imposed limits
    * on file descriptors, so we may not get as many as we asked for.  Check to
    * make sure we get enough.
    */
  if (getdtablesize() < dtablesize)
    if (setdtablesize(dtablesize) < dtablesize) {
      fprintf(stderr,
        "Warning: Could not allocate enough file descriptors!\n");
      fprintf(stderr,
        "         setdtablesize() could not allocate %d descriptor(s).\n",
        getdtablesize() - dtablesize);
    }

  /*
   * Just be polite and tell the administrator how many he has.
   */
  fprintf(stderr, "%d file descriptors were allocated, (%d were requested).\n",
    getdtablesize(), dtablesize);
#endif

  time_to_clean_up = TIME_TO_CLEAN_UP;
  port_number = PORTNO;
  time_to_swap = TIME_TO_SWAP;
  max_cost = MAX_COST;
  reserved_size = RESERVED_SIZE;
  max_array_size = MAX_ARRAY_SIZE;
  max_string_length = MAX_STRING_LENGTH;
  master_file_name = (char *)MASTER_FILE;
  mud_lib = (char *)MUD_LIB;
  set_inc_list(INCLUDE_DIRS);

  if(reserved_size > 0)
    reserved_area = (char *)DMALLOC(reserved_size,69,"main.c: reserved_area");
  for(i=0; i < sizeof consts / sizeof consts[0]; 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 = &predefs;
	tmp->flag = argv[i]+2;
	tmp->next = lpc_predefs;
	lpc_predefs = tmp;
	continue;
      }
      fprintf(stderr, "Illegal flag syntax: %s\n", argv[i]);
      exit(-1);
    case 'N':
      no_ip_demon++; continue;
    case 'm':
      mud_lib = string_copy(argv[i]+2);
      if (chdir(mud_lib) == -1) {
	fprintf(stderr, "Bad mudlib directory: %s\n", mud_lib);
	exit(-1);
      }
      new_mudlib = 1;
      break;
    }
  }
  if (!new_mudlib && chdir(mud_lib) == -1) {
    fprintf(stderr, "Bad mudlib directory: %s\n", mud_lib);
    exit(-1);
  }

#ifdef SAVE_BINARIES
  init_binaries(argc, argv);
#endif

#ifndef NO_IP_DEMON
  if(!no_ip_demon)
    init_addr_server(ADDR_SERVER_IP,ADDR_SERVER_PORT);
#endif /* NO_IP_DEMON */

  set_simul_efun(SIMUL_EFUN);

  if(SETJMP(error_recovery_context)){
    clear_state();
    add_message("Anomaly in the fabric of world space.\n");
  } else {
    error_recovery_context_exists = 1;
    master_ob = load_object(master_file_name,0);
  }
  error_recovery_context_exists = 0;
  if (master_ob == 0) {
    fprintf(stderr, "The file master file must be loadable.\n");
    exit(-1);
  }
      /*
	* Make sure master_ob is never made a dangling pointer.
	* Look at apply_master_ob() for more details.
	*/
  add_ref(master_ob, "main");
  ret = apply_master_ob("get_root_uid", 0);
  if (ret == 0 || ret->type != T_STRING) {
    fprintf(stderr, "get_root_uid() in the master file does not work\n");
    exit(-1);
  }
  master_ob->uid = set_root_uid (ret->u.string);
  master_ob->euid = master_ob->uid;
  set_root_author (ret->u.string);
  master_ob->flags |= O_MASTER;
  ret = apply_master_ob("get_bb_uid", 0);
  if (ret == 0 || ret->type != T_STRING) {
    fprintf(stderr, "get_bb_uid() in the master file does not work\n");
    exit(-1);
  }
  set_backbone_uid(ret->u.string);
  set_backbone_domain(ret->u.string);
  for (i=1; i < argc; i++) {
    if (argv[i][0] != '-') {
	  continue;
	} else {
	  /*
	   * Look at flags. -m and -o has already been tested.
	   */
	  switch(argv[i][1]) {
	  case 'f':
	    push_constant_string(argv[i]+2);
	    (void)apply_master_ob("flag", 1);
	    if (MudOS_is_being_shut_down) {
	      fprintf(stderr, "Shutdown by master object.\n");
	      exit(0);
	    }
	    continue;
	  case 'e':
	    e_flag++; continue;
	  case 'D':
	    continue;
	  case 'N':
	    continue;
	  case 'p':
	    port_number = atoi(argv[i]+2);
	    continue;
	  case 'm':
	    continue;
	  case 'd':
	    d_flag++; continue;
	  case 'c':
	    comp_flag++; continue;
	  case 't':
	    t_flag++; continue;
#ifdef YYDEBUG
	  case 'y':
	    yydebug = 1; continue;
#endif /* YYDEBUG */
	  default:
	    fprintf(stderr, "Unknown flag: %s\n", argv[i]);
	    exit(-1);
	  }
	}
      }
    if (MudOS_is_being_shut_down)
	exit(1);
  if (strlen(DEFAULT_FAIL_MESSAGE))
     default_fail_message = DEFAULT_FAIL_MESSAGE;
  else
     default_fail_message = "What?";
  restore_stat_files();
  preload_objects(e_flag);
#ifdef TRAP_CRASHES
    signal(SIGUSR1, sig_usr1);
    signal(SIGTERM, sig_term);
    signal(SIGINT, sig_int);
#ifndef DEBUG
#if defined(SIGABRT) && !defined(LATTICE)
    signal(SIGABRT, sig_abrt);
#endif
#ifdef SIGIOT
    signal(SIGIOT, sig_iot);
#endif
    signal(SIGHUP, sig_hup);
#ifdef SIGBUS
    signal(SIGBUS, sig_bus);
#endif
#ifndef LATTICE
    signal(SIGSEGV, sig_segv);
    signal(SIGILL, sig_ill);
#endif
#endif /* DEBUG */
#endif
    backend();
    return 0;
}

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

  len = strlen(str);
  if (len > max_string_length) {
     len = max_string_length;
  }
  p = DXALLOC(len + 1, 70, "string_copy");
  (void)strncpy(p, str, len);
  p[len] = '\0'; /* strncpy doesn't put on \0 if 'from' too long */
  return p;
}

void debug_message(va_alist)
  va_dcl
{
    static FILE *fp = NULL;
    char deb[100];
  va_list args;
  char *fmt;

    if (fp == NULL) 
      {
	sprintf(deb,"%s/debug.log",LOG_DIR);
	if (deb[0] == '/')
	  strcpy (deb, deb+1);
	fp = fopen(deb, "w");
	if (fp == NULL) 
	  {
	    perror(deb);
	    abort();
	  }
      }
  va_start(args);
  fmt = va_arg(args, char *);
  vfprintf(fp, fmt, args);
  va_end(args);
    (void)fflush(fp);
}

void debug_message_svalue(v)
    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\"", v->u.string);
	return;
    case T_OBJECT:
	debug_message("OBJ(%s)", v->u.ob->name);
	return;
    case T_LVALUE:
	debug_message("Pointer to ");
	debug_message_svalue(v->u.lvalue);
	return;
    default:
	debug_message("<INVALID>\n");
	return;
    }
}

int slow_shut_down_to_do = 0;

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

    if (going_to_exit)
	exit(3);
    if (size == 0)
	fatal("Tried to allocate 0 bytes.\n");
    p = (char *)DMALLOC(size, 71, "main.c: xalloc");
    if (p == 0) {
	if (reserved_area) {
	    FREE(reserved_area);
	    p = "Temporarily out of MEMORY. Freeing reserve.\n";
	    write(1, p, strlen(p));
	    reserved_area = 0;
	    slow_shut_down_to_do = 6;
	    return xalloc(size);	/* Try again */
	}
	going_to_exit = 1;
	p = "Totally out of MEMORY.\n";
	write(1, p, strlen(p));
	(void)dump_trace(0);
	exit(2);
    }
    return p;
}

#ifdef TRAP_CRASHES

/* send this signal when the machine is about to reboot.  The script
   which restarts the MUD should take an exit code of 1 to mean don't
   restart
 */

static void
sig_usr1()
{
	push_string("Host machine shutting down", STRING_CONSTANT);
	push_undefined();
	push_undefined();
	current_object = master_ob;
	apply_master_ob("crash", 3);
	fprintf(stderr,"Received SIGUSR1, calling exit(-1)\n");
	exit(-1);
}

static void sig_term() {
    crash_MudOS("Process terminated");
}

static void sig_int() {
    crash_MudOS("Process interrupted");
}

#ifndef DEBUG

static void sig_segv() {
    crash_MudOS("Segmentation fault");
}

static void sig_bus() {
    crash_MudOS("Bus error");
}

static void sig_ill() {
    crash_MudOS("Illegal instruction");
}

static void sig_hup() {
    crash_MudOS("Hangup!");
}

static void sig_abrt() {
    crash_MudOS("Aborted");
}

static void sig_iot() {
    crash_MudOS("Aborted(IOT)");
}

#endif /* !DEBUG */

#endif /* TRAP_CRASHES */

static int crash_condition = 0; 

void crash_MudOS(str)
     char *str;
{
#ifdef DROP_CORE
   char buf[SMALL_STRING_SIZE];
#endif

  /* Something really, really bad just happened.  Nothing we can do about it,
     so tell the master object to clean up, and exit. */
  if(crash_condition){
    fprintf(stderr,"Too many simultaneous fatal errors!\n");
    fprintf(stderr,"Exiting before crash could be called successfully.\n");
    fprintf(stderr,"Dying: %s\n", str);
    exit(-3);
  }
  else {
	/* restore default action for SIGILL/SIGABRT so we don't loop when
	   crash_MudOS calls abort() at the end of this function.
	*/
#ifdef SIGABRT
	signal(SIGABRT, SIG_DFL);
#endif
#ifdef SIGILL
	signal(SIGILL, SIG_DFL);
#endif
#ifdef SIGIOT
	signal(SIGIOT, SIG_DFL);
#endif

    fprintf(stderr, "Shutting down: %s\n", str);
    crash_condition++;
{
	char tmp[10];
	gets(tmp);
}
    save_stat_files();
    push_string(str, STRING_CONSTANT);
	if (command_giver) {
		push_object(command_giver);
	} else {
		push_undefined();
	}
	if (current_object) {
		push_object(current_object);
	} else {
		push_undefined();
	}
    /* 
     * set the current object to be the master object.
     * since we're crashing, we don't need to save the actual current
     * object. This is so crash() can be static.
     */
    current_object = master_ob;
    apply_master_ob("crash", 3);
  }
#ifdef DROP_CORE
  strncpy(buf,mud_lib,SMALL_STRING_SIZE);
  strcat(buf,"/cores");
  if (chdir(buf) == -1) {
     chdir(mud_lib);
  }
     abort();
#else
     exit(-2);
#endif
}
