#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <memory.h>
#if defined(_SEQUENT_)
#include <malloc.h>
#endif
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <setjmp.h>
#include <varargs.h>
#include <signal.h>
#if defined(sun)
#include <alloca.h>
#endif
#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "lex.h"
#include "exec.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 s_flag = 0; /* Make statistics and dump to /MUDstatistics */
int comp_flag = 0; /* Trace compilations */
int driver_mtime;

#ifdef YYDEBUG
extern int yydebug;
#endif

int port_number = PORTNO;
#ifdef CATCH_UDP_PORT
int udp_port = CATCH_UDP_PORT;
#endif
#ifdef SERVICE_PORT
int service_port = SERVICE_PORT;
#endif
char *reserved_area;	/* reserved for malloc() */
struct svalue const0, const1;

double consts[5];

extern jmp_buf error_recovery_context;
extern int error_recovery_context_exists;
extern struct object *vbfc_object;

extern struct object *master_ob;

static void start_ip_demon();
static void
update_current_time()
{
    extern int current_time, get_current_time();
    
    current_time = get_current_time();
}
int 
main(int argc, char **argv)
{
    extern void init_shared_strings();
    extern int game_is_being_shut_down;
    extern int current_time;
    int i, new_mudlib = 0;
    int no_ip_demon = 0;
    char *p;
    struct svalue *ret;
    extern char* make_shared_string(char *);
    extern struct svalue catch_value;
    extern void init_cfuns();

    char *dpath;
    struct stat c_st;

#ifdef MALLOC_debugmalloc
    extern void malloc_setup_hook();
    malloc_setup_hook();
#endif

#ifdef MALLOC_gc
    extern void gc_init();
    gc_init();
#endif

#ifdef MALLOC_sysmalloc
    extern void sysmalloc_init();
    sysmalloc_init();
#endif
    
#ifdef MALLOC_bibopmalloc
    extern void bibop_init();
    bibop_init();
#endif

#ifdef _SEQUENT_
    setdtablesize(1024);
#endif

    const0.type = T_NUMBER; const0.u.number = 0;
    const1.type = T_NUMBER; const1.u.number = 1;
    catch_value = const0;
    
    /*
     * 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, "No random generator specified!\n");
#endif /* RANDOM */
#endif /* DRAND48 */
    current_time = get_current_time();;
    signal(SIGALRM, update_current_time);
    ualarm(1000000/TIME_RES, 1000000/TIME_RES);


    if (RESERVED_SIZE > 0)
	reserved_area = malloc(RESERVED_SIZE);
    for (i=0; i < sizeof consts / sizeof consts[0]; i++)
	consts[i] = exp(- i / 900.0);
    init_num_args();
    reset_machine(1);
    init_cfuns();

    /*
     * Set up the signal handling.
     */
    init_signals();

#ifdef BINARIES
    /*
     * Find the modification time of the driver. For reasons of binary
     * integrity we only accept compiled binaries younger than the
     * driver. This is not foolproof, we might start an old version, but
     * this will normally work fine.
     */
    dpath = (char *) xalloc(strlen(argv[0]) + 1);
    strcpy(dpath, argv[0]);
    if (stat(dpath, &c_st) != -1)
	driver_mtime = c_st.st_mtime;
    else
    {	
	fprintf(stderr,"Can't find myself %s, ignoring old binaries.\n",
		dpath);
	driver_mtime = current_time;
    }
    free(dpath);
#endif
    
    /*
     * 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 (atoi(argv[i]))
	    port_number = atoi(argv[i]);
	else 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 *)
		    xalloc(sizeof(struct lpc_predef_s));
		if (!tmp) fatal("alloca 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);
	case 'N':
	    no_ip_demon++; continue;
	case 'm':
	    if (chdir(argv[i]+2) == -1)
	    {
	        fprintf(stderr, "Bad mudlib directory: %s\n", argv[i]+2);
		exit(1);
	    }
	    new_mudlib = 1;
	    break;
	}
    }

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

    /* Initialize swap */
    init_swap();

#ifndef NO_IP_DEMON
    if (!no_ip_demon)
	start_ip_demon();
#endif

    if (setjmp(error_recovery_context))
    {
	clear_state();
	add_message("Anomaly in the fabric of world space.\n");
    } 
    else
    {
	error_recovery_context_exists = 1;
	get_simul_efun();
	master_ob = load_object("secure/master", 1, 0);
	if (master_ob)
	{
	    extern void master_ob_loaded(void);
	    /*
	     * Make sure master_ob is never made a dangling pointer.
	     * Look at apply_master_ob() for more details.
	     */
	    add_ref(master_ob, "main");
	    master_ob->prog->flags |= PRAGMA_RESIDENT;
	    master_ob_loaded();
		create_object(master_ob);
	    clear_state();
	}
    }
    error_recovery_context_exists = 0;
    if (master_ob == 0) 
    {
	fprintf(stderr, "The file secure/master must be loadable.\n");
	exit(1);
    }
    set_inc_list(apply_master_ob(M_DEFINE_INCLUDE_DIRS, 0));
    {
	struct svalue* ret;

	ret = apply_master_ob(M_PREDEF_DEFINES, 0);
	if (ret && ret->type == T_POINTER)
	{
	    int i;
	    struct lpc_predef_s *tmp;

	    for (i = 0; i < ret->u.vec->size; i++)
		if (ret->u.vec->item[i].type == T_STRING)
		{
		    tmp = (struct lpc_predef_s *)
			xalloc(sizeof(struct lpc_predef_s));
		    tmp->flag = string_copy(ret->u.vec->item[i].u.string);
		    tmp->next = lpc_predefs;
		    lpc_predefs = tmp;
		}
	}
    }
    for (i = 1; i < argc; i++)
    {
	if (atoi(argv[i]))
	    ;
	else if (argv[i][0] != '-')
	{
	    fprintf(stderr, "Bad argument %s\n", argv[i]);
	    exit(1);
	}
	else 
	{
	    /*
	     * Look at flags. -m has already been tested.
	     */
	    switch(argv[i][1])
	    {
	    case 'f':
		push_string(argv[i]+2, STRING_MALLOC);
		(void)apply_master_ob(M_FLAG, 1);
		if (game_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 'm':
		continue;
	    case 'd':
		d_flag = atoi(argv[i] + 2);
		continue;
	    case 'c':
		comp_flag++; continue;
	    case 't':
		t_flag++; continue;
	    case 'S':
		s_flag++; 
		mudstatus_set(1, -1, -1); /* Statistics, default limits */
		continue;
#ifdef CATCH_UDP_PORT
	    case 'u':
		udp_port = atoi (&argv[i][2]);
		continue;
#endif
#ifdef SERVICE_PORT
	    case 'p':
		service_port = atoi (&argv[i][2]);
		continue;
#endif
#ifdef YYDEBUG
	    case 'y':
		yydebug = 1; continue;
#endif
	    default:
		fprintf(stderr, "Unknown flag: %s\n", argv[i]);
		exit(1);
	    }
	}
    }

    /* See to it that the mud name is always defined in compiled files
    */
    ret = apply_master_ob(M_GET_MUD_NAME, 0);

    if (ret && ret->type == T_STRING)
    {
	struct lpc_predef_s *tmp;
		
	tmp = (struct lpc_predef_s *)
	    alloca(sizeof(struct lpc_predef_s));
	if (!tmp) 
	    fatal("alloca failed");
	tmp->flag = string_copy(ret->u.string);
	tmp->next = lpc_predefs;
	lpc_predefs = tmp;
    }

    ret = apply_master_ob(M_GET_VBFC_OBJECT, 0);
    if (ret && ret->type == T_OBJECT)
    {
	vbfc_object = ret->u.ob;
	vbfc_object->ref++;
    }
    else
	vbfc_object = 0;

    if (game_is_being_shut_down)
	exit(1);
    preload_objects(e_flag);
    apply_master_ob(M_FINAL_BOOT, 0);
    
    backend();
    return 0;
}

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

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

/*VARARGS1*/
void
debug_message(va_alist)
va_dcl
{
    va_list argp;
    char *fmt, *f;

    static FILE *fp = NULL;
    char deb[100];
    char name[100];

    if (fp == NULL) {
#ifdef _SEQUENT_
	strcpy(deb, "log/debug.log");
#else
	gethostname(name,sizeof name);
	if ((f = strchr(name, '.')) != NULL)
	    *f = '\0';
	sprintf(deb,"%s.debug.log",name);
#endif
	fp = fopen(deb, "w");
	if (fp == NULL) {
	    perror(deb);
	    abort();
	}
    }

    va_start(argp);
    fmt = va_arg(argp, char *);
    vfprintf(fp, fmt, argp);
    (void)fflush(fp);
}

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\"", 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;

#if 1 
#ifdef malloc
#undef malloc
#endif
#endif

/*#define MALLOCSIZE*/

extern void *bmalloc();

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

    if (going_to_exit)
	exit(3);
    if (size == 0)
	size = 1;
#ifdef MALLOCSIZE
    {
#define MSMAX 1000
	static int msize[MSMAX];
	static int mbig;
	static int mcnt;

	if (size > MSMAX) mbig++;
	else msize[size]++;
	if (++mcnt % 50000 == 0) {
	    int i; 
	    fprintf(stderr, "Malloc stats (%d):\n", mcnt);
	    for(i = 0; i<MSMAX; i++)
		if (msize[i]) fprintf(stderr, "%4d %5d\n", i, msize[i]);
	    fprintf(stderr, ">%d %d\n", i, mbig);
	}
    }
#endif
    p = (char *)malloc(size);
    if (p == 0)
    {
	if (reserved_area)
	{
	    extern void look_for_objects_to_swap(int);
	    extern struct object *swap_ob;

	    free(reserved_area);
	    reserved_area = 0;
	    p = "Temporary out of MEMORY. Freeing reserve.\n";
	    write(1, p, strlen(p));
	    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;
}

#ifndef NO_IP_DEMON
static void
start_ip_demon()
{
    extern FILE *f_ip_demon, *f_ip_demon_wr;
    char path[100];
    int tochild[2], fromchild[2];
    int pid;
    char c;

    if(pipe(tochild) < 0)
        return;
    if(pipe(fromchild) < 0)
    {
        close(tochild[0]);
        close(tochild[1]);
        return;
    }
    if((pid = fork()) == 0)
    {
	/* Child */
        dup2(tochild[0], 0);
        dup2(fromchild[1], 1);
        close(tochild[0]);
        close(tochild[1]);
        close(fromchild[0]);
        close(fromchild[1]);
	if (strlen(BINDIR) + 7 <= sizeof path) {
	    sprintf(path, "%s/hname", BINDIR);
	    execl((char *)path, "hname", 0);
	}
	write(1, "0", 1);	/* indicate failure */
        fprintf(stderr, "exec of hname failed.\n");
        _exit(1);
    }
    if(pid == -1)
    {
        close(tochild[0]);
        close(tochild[1]);
        close(fromchild[0]);
        close(fromchild[1]);
        return;
    }
    close(tochild[0]);
    close(fromchild[1]);
    read(fromchild[0], &c, 1);
    if (c == '0')
    {
        close(tochild[1]);
        close(fromchild[0]);
	return;
    }
    f_ip_demon_wr = fdopen(tochild[1], "w");
    f_ip_demon = fdopen(fromchild[0], "r");
    if (f_ip_demon == NULL || f_ip_demon_wr == NULL)
    {
	f_ip_demon = NULL;
        close(tochild[1]);
        close(fromchild[0]);
    }
}
#endif