/*
 * Original from ircII: a new irc client.  I like it.  I hope you will too!
 * Written By Michael Sandrof
 * Copyright(c) 1990 
 * 
 * Modified by Rex Feany <laeos@laeos.net> for Xaric
 *
 * 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 of the License, 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.
 *
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/types.h>

#ifdef USING_CURSES
# include <curses.h>
#endif

#include "irc.h"
#include "log.h"
#include "dcc.h"
#include "misc.h"
#include "hook.h"
#include "keys.h"
#include "exec.h"
#include "vars.h"
#include "names.h"
#include "build.h"
#include "newio.h"
#include "flood.h"
#include "input.h"
#include "timer.h"
#include "output.h"
#include "status.h"
#include "screen.h"
#include "server.h"
#include "window.h"
#include "notify.h"
#include "whowas.h"
#include "ircaux.h"
#include "history.h"
#include "numbers.h"
#include "ircterm.h"
#include "commands.h"
#include "tcommand.h"

/** Global Variables **/

/* These are all just "handy" variables. */
char zero_str[] = "0", one_str[] = "1", space_str[] = " ", on_str[] = "ON", off_str[] = "OFF", empty_str[] = "";

char *invite_channel = NULL,	/* last channel of an INVITE */
    *ircrc_file = NULL,		/* full path .ircrc file */
    *my_path = NULL,		/* path to users home dir */
    *ircservers_file = NULL,	/* name of server file */
    nickname[NICKNAME_LEN + 1],	/* users nickname */
    realname[REALNAME_LEN + 1],	/* real name of user */
    username[NAME_LEN + 1],	/* usernameof user */
    *send_umode = NULL,		/* sent umode */
    *who_name = NULL,		/* extra /who switch info */
    *who_file = NULL,		/* extra /who switch info */
    *who_server = NULL,		/* extra /who switch info */
    *who_host = NULL,		/* extra /who switch info */
    *who_nick = NULL,		/* extra /who switch info */
    *who_real = NULL,		/* extra /who switch info */
    *cut_buffer = NULL,		/* global cut_buffer */
    *prog_name = NULL,		/* argv[0] */
    *line_thing = NULL,		/* show_numeric_str, say( ), put at begin of line */
    oper_command = 0,		/* true just after an oper() command is given.  Used to tell the difference between an incorrect
				 * password generated by an oper() command and one generated when connecting to a new server */
    def_hostname[NAME_LEN+1],	/* gethostname() return */
    *local_host_name = NULL;	/* host the user would like to use (-H, /hostname) */

int irc_port = IRC_PORT,	/* port of ircd */
    strip_ansi_in_echo,		/* used in the send_text() */
    current_on_hook = -1, current_numeric,	/* this is negative of the current numeric! */
    key_pressed = 0, waiting_out = 0,	/* used by /WAIT command */
    waiting_in = 0,		/* used by /WAIT command */
    who_mask = 0,		/* keeps track of which /who switchs are set */
    away_set = 0,		/* set if there is an away message anywhere */
#ifdef HAVE_SSL
    do_use_ssl = 0,
#endif
    need_redraw = 0;		/* set whenever the terminal is reset */

static int
 load_ircrc = 1,		/* load ircrc file? */
    load_ircservers = 1;	/* load servers file? */

struct in_addr MyHostAddr;	/* The local machine address */
struct in_addr LocalHostAddr;

time_t idle_time = 0, start_time;

/* where TRACE() output should go */
static FILE *traceout;

/* for TRACE(), xaric -d */
void trace(const char *fmt, ...)
{
    if (traceout) {
	va_list ma;
	va_start(ma, fmt);
	vfprintf(traceout, fmt, ma);
	va_end(ma);
    }
}

/* Display the startup message */
static void startup_message(void)
{
    int old = strip_ansi_in_echo;

    strip_ansi_in_echo = 0;

    /* black magic. leave me alone. */
    put_it(empty_str);

    put_fmt_str("%g***%n", NULL);
    put_fmt_str("%g***%C $0-", "%s", PACKAGE_STRING);
    put_fmt_str("%g***%n", NULL);
    put_fmt_str("%g***%n Xaric is free software, covered by the GNU General Public License,", NULL);
    put_fmt_str("%g***%n and you are welcome to change it and/or distribute copies of it", NULL);
    put_fmt_str("%g***%n under certain conditions.", NULL);
    put_fmt_str("%g***%n Type \"/help copying\" to see the conditions.", NULL);
    put_fmt_str("%g***%n", NULL);
    put_fmt_str("%g***%n There is absolutely no warranty for Xaric.", NULL);
    put_fmt_str("%g***%n Type \"/help warranty\" for details.", NULL);
    put_fmt_str("%g***%n", NULL);

    strip_ansi_in_echo = old;
}

static void usage(void) __attribute__ ((noreturn));

/* Print out the usage message, and die */
static void usage(void)
{
    static char help[] =
	"Usage: %s [OPTION]... [nick [server list]...]\n"
	"   -n               do not load your " IRCRC_NAME " file.\n"
	"   -S               do not load your " IRCSERVERS_NAME " file.\n"
	"   -h               this help text.\n"
	"   -v               display xaric version and exit.\n"
	"   -f               your terminal users flow control (^s/^q), so xaric shouldn't.\n"
	"   -s               Use SSL.\n"
	"   -F               your terminal does not use flow control.\n"
	"   -H <hostname>    uses the virtual hostname if possible.\n"
	"   -d <string>      set debug options (see documentation).\n"
	"   -p <port>        set default port to use (normally 6667).\n"
	"   -l <file>        loads file instead of " IRCRC_NAME ".\n"
	"   -r <file>        loads file as list of servers, instead of " IRCSERVERS_NAME ".\n\n"
	"Xaric uses the values in IRCSERVER, IRCPORT, IRCRC, IRCNICK, IRCNAME\n"
	"IRCHOST, IRCUMODE, IRCSERVERS\n\n"
	"Command line arguments override environment variables.\n\n" "Report bugs to bugs@xaric.org\n";

    fprintf(stderr, help, prog_name);

    exit(EXIT_FAILURE);
}

/* Parse command line arguments. */
static void parse_args(int argc, char *argv[])
{
    static const char optstr[] = "vhH:p:fFl:r:nsSd:";

    while (1) {
	int c = getopt(argc, argv, optstr);

	if (-1 == c)
	    break;

	switch (c) {
	case 'v':		/* print version */
	    puts(PACKAGE_STRING);
	    putchar('\n');
	    exit(0);

	case 'H':		/* Set local hostname, for vhost stuff */
	    malloc_strcpy(&local_host_name, optarg);
	    break;

	case 'p':		/* Default port to use */
	    irc_port = my_atol(optarg);
	    break;

	case 'f':		/* Use flow control */
	    term_set_flow_control(ON);
	    break;

	case 'F':		/* dont use flow control */
	    term_set_flow_control(OFF);
	    break;

	case 'l':		/* Load some file instead of ~/.ircrc */
	    malloc_strcpy(&ircrc_file, optarg);
	    break;

	case 'r':		/* Load list of servers from this file */
	    malloc_strcpy(&ircservers_file, optarg);

	case 'n':		/* do not load ircrc */
	    load_ircrc = 0;
	    break;

	case 'S':		/* do not load .ircservers file */
	    load_ircservers = 0;
	    break;

	case 'd':		/* set server debug */
	    if (traceout)
		fclose(traceout);
	    if (! (traceout = fopen(optarg, "a"))) {
		fprintf(stderr, "%s: unable to open '%s' for -d output: %s\n",
			prog_name,
			optarg,
			strerror(errno));
		exit(1);
	    }
	    TRACE("trace logging enabled\n");
	    break;

	case 's':		/* use ssl */
	    do_use_ssl = 1;
	    break;

	case '?':		/* unknown option */
	case ':':		/* missing argument */
	case 'h':		/* help */
	default:
	    usage();		/* does not return */
	}
    }

    /* first non-option argument is a nickname */
    if (optind < argc)
	strmcpy(nickname, argv[optind++], NICKNAME_LEN);

    /* the rest are servers */
    while (optind < argc)
	build_server_list(argv[optind++]);

}

/* sniff out user information from passwd file */
static void get_user_info(void)
{
    struct passwd *entry;
    char *ptr;

    if (!(entry = getpwuid(getuid())))
	return;

    if (!*realname && entry->pw_gecos && *(entry->pw_gecos)) {
#ifdef GECOS_DELIMITER
	if ((ptr = index(entry->pw_gecos, GECOS_DELIMITER)))
	    *ptr = (char) 0;
#endif
	if ((ptr = strchr(entry->pw_gecos, '&')) == NULL)
	    strmcpy(realname, entry->pw_gecos, REALNAME_LEN);
	else {
	    int len = ptr - entry->pw_gecos;

	    if (len < REALNAME_LEN && *(entry->pw_name)) {
		char *q = realname + len;

		strmcpy(realname, entry->pw_gecos, len);
		strmcat(realname, entry->pw_name, REALNAME_LEN);
		strmcat(realname, ptr + 1, REALNAME_LEN);
		if (islower(*q) && (q == realname || isspace(*(q - 1))))
		    *q = toupper(*q);
	    } else
		strmcpy(realname, entry->pw_gecos, REALNAME_LEN);
	}
    }

    if (entry->pw_name && *(entry->pw_name) && !*username)
	strmcpy(username, entry->pw_name, NAME_LEN);
    if (entry->pw_dir && *(entry->pw_dir))
	malloc_strcpy(&my_path, entry->pw_dir);
}

/* Load data in from environment variables. */
/* We expect this to initilize most globals. */
static void load_xaric_environment(void)
{
    char *ptr;

    if ((ptr = getenv("IRCNICK")))
	strmcpy(nickname, ptr, NICKNAME_LEN);

    if ((ptr = getenv("IRCUMODE")))
	malloc_strcpy(&send_umode, ptr);

    if ((ptr = getenv("HOME")))
	malloc_strcpy(&my_path, ptr);

    if ((ptr = getenv("IRCRC")))
	malloc_strcpy(&ircrc_file, ptr);

    if ((ptr = getenv("IRCSERVERS")))
	malloc_strcpy(&ircservers_file, ptr);

    if ((ptr = getenv("IRCNAME")) || (ptr = getenv("NAME")))
	strmcpy(realname, ptr, REALNAME_LEN);

    if ((ptr = getenv("IRCUSER")) || (ptr = getenv("USER")))
	strmcpy(username, ptr, NAME_LEN);

    if ((ptr = getenv("IRCHOST")))
	local_host_name = m_strdup(ptr);

    if ((ptr = getenv("IRCPORT")))
	irc_port = my_atol(ptr);

    if ((ptr = getenv ("IRCPATH"))) {
	set_string_var(LOAD_PATH_VAR, ptr);
    } else {
	malloc_strcpy (&ptr, ".:~/.irc:");
	puts(XARIC_DATA_PATH);
	malloc_strcat (&ptr, XARIC_DATA_PATH);
	malloc_strcat (&ptr, "script");
	set_string_var(LOAD_PATH_VAR, ptr);
	new_free(&ptr);
    }

    if ((ptr = getenv("IRCSERVER")))
	build_server_list(ptr);
}

/* Make sure all the global variables are initilized. */
static void load_xaric_finish(void)
{
    if (!*nickname)
	strmcpy(nickname, username, sizeof(nickname));

    if (!check_nickname(nickname)) {
	fprintf(stderr, "Illegal nickname %s\n", nickname);
	fprintf(stderr, "Please restart IRC II with a valid nickname\n");
	exit(1);
    }

    if (!*realname)
	strmcpy(realname, "*Unknown*", REALNAME_LEN);

    if (!*username)
	strmcpy(username, "*Unknown*", NAME_LEN);

    if (!my_path || !*my_path)
	malloc_strcpy(&my_path, "/");

    if (!ircservers_file)
	malloc_strcpy(&ircservers_file, IRCSERVERS_NAME);

    if (!ircrc_file)
	malloc_strcpy(&ircrc_file, IRCRC_NAME);
}

/* load irc servers file */
static void load_xaric_servers(void)
{
    /* load server files here */

    if (load_ircservers)
	read_server_file(ircservers_file);

#ifdef DEFAULT_SERVER
    if (server_list_size() == 0) {
	char *ptr = m_strdup(DEFAULT_SERVER);

	build_server_list(ptr);
	new_free(&ptr);
	/* XXX build_server_list should take const */
    }
#endif

    if (server_list_size() == 0)
	ircpanic("I have know no servers!");
}

/* irc_exit: cleans up and leaves */
void irc_exit(char *reason, char *formated)
{
    int old_window_display = window_display;

    do_hook(EXIT_LIST, "%s", reason);
    close_server(-1, reason);
    put_it("%s", formated ? formated : reason);
    logger(curr_scr_win, NULL, 0);
    if (get_int_var(MSGLOG_VAR))
	log_toggle(0, NULL);

    clean_up_processes();
    cursor_to_input();		/* Needed so that ircII doesn't gobble the last line of the kill. */
    term_cr();
    term_clear_to_eol();
    term_reset();

    /* Debugging sanity. */
    window_display = 0;
    set_lastlog_size(curr_scr_win, NULL, 0);
    set_history_size(curr_scr_win, NULL, 0);
    remove_channel(NULL, 0);
    window_display = old_window_display;
    clear_bindings();
    clear_sets();

    fprintf(stdout, "\r");
    fflush(stdout);
    exit(0);
}

#ifdef HAVE_SSL
static void init_ssl(void)
{
    char *entropy = malloc(100);
    int i;

    for (i = 0; i < 100; i++)
	entropy[i] = (char) getrandom(0, 255);

    /* Many systems don't have /dev/random so we seed */
    RAND_seed(entropy, 100);
    SSLeay_add_ssl_algorithms();
    SSL_load_error_strings();
    free(entropy);
}
#endif				/* HAVE_SSL */

/* initilize global variables, parse command line, etc */
static void xaric_init(int argc, char *argv[])
{
    get_user_info();

#ifdef HAVE_SSL
    init_ssl();
#endif

    load_xaric_environment();
    parse_args(argc, argv);
    init_hostname();
    load_xaric_finish();
    load_xaric_servers();

    if (init_screen()) {
	fprintf(stderr, "%s: Woops! Couldn't init the terminal!\n", prog_name);
	exit(1);
    }

    signals_init();
    init_variables();
    init_keys_1();
    init_commands();
    build_status(curr_scr_win, NULL, 0);
    update_input(UPDATE_ALL);

    startup_message();
}

/* new irc_io modularized stuff */

/* 
 * GetLineStruct is what is "under" your current input line, and the function
 * we're supposed to call when you press return.  This is different from
 * AddWaitPrompt which does functionally the same thing but doesnt cause
 * recursive calls to io.
 */
struct GetLineStruct {
    int done;
    void (*func) (char, char *);
    char *saved_input;
    char *saved_prompt;
    int recursive_call;
    struct GetLineStruct *prev;
    struct GetLineStruct *next;
};
typedef struct GetLineStruct GetLine;
GetLine *GetLineStack = NULL;

/* when you press return, you call this. */
extern void get_line_return(char unused, char *not_used)
{
    GetLine *stuff;

    /* get the last item on the stack */
    if ((stuff = GetLineStack) == NULL)
	return;

    /* 
     * If we're NOT the main() call, then undo all that we messed up coming in. If stuff->done gets set to 1 when recursive_call is
     * zero, then something is VERY wrong. We can set stuff->prev->next to null because the call to get_line() holds a pointer to
     * stuff, so when it unrecurses, it will free it. */
    if (stuff->func) {
	not_used = NULL;
	(stuff->func) (unused, not_used);
    }
    if (stuff->recursive_call) {
	stuff->done = 1;
	set_input(stuff->saved_input);
	set_input_prompt(curr_scr_win, stuff->saved_prompt, 0);
	new_free(&(stuff->saved_input));
	new_free(&(stuff->saved_prompt));
	stuff->next->prev = NULL;
	GetLineStack = stuff->next;
    }

    update_input(UPDATE_ALL);

    /* We cant delete stuff here becuase the get_line function still needs to look at stuff->done.  So we let it delete the items off
     * the list.  But we removed it from the list, so we wont accidentally use it later. */
    return;
}

/* This is a wrapper for io().  Only two functions at any time are allowed
 * to call it, and main() is one of those two.  When you call it, you have
 * the option to change the input prompt and the input buffer.  You also
 * give it a function to call when it gets a return.  Only main() is 
 * allowed to call it with an new_input of -1, which tells it that it is
 * at the lowest level of parsing, by which i mean that noone is waiting
 * for anything, since there is no recursion going on.
 */
void get_line(char *prompt, int new_input, void (*func) (char, char *))
{
    GetLine *stuff;

    if (GetLineStack && new_input == -1)
	ircpanic("Illegal call to get_line\n");

    /* initialize the new item. */
    stuff = new_malloc(sizeof(GetLine));
    stuff->done = 0;
    stuff->func = func;
    stuff->recursive_call = (new_input == -1) ? 0 : 1;
    stuff->saved_input = NULL;
    stuff->saved_prompt = NULL;
    stuff->prev = NULL;
    stuff->next = NULL;
    malloc_strcpy(&(stuff->saved_input), get_input());
    malloc_strcpy(&(stuff->saved_prompt), get_input_prompt());

    /* put it on the stack */
    if (GetLineStack) {
	stuff->next = GetLineStack;
	GetLineStack->prev = stuff;
    }
    GetLineStack = stuff;

    /* if its a global call, get the input prompt */
    if (new_input == -1)
	set_input_prompt(curr_scr_win, get_string_var(INPUT_PROMPT_VAR), 0);
    else
	set_input_prompt(curr_scr_win, prompt, 0);
    set_input(empty_str);

    /* ok.  we call io() until the user presses return, ending the input line.  get_line_return will then set get_line_done to one,
     * and we will stop getting characters and drop out. get_line_done NEVER sets this to one if we are in our call from main().
     * NEVER. */
    while (!stuff->done)
	io("get line");

    if (new_input == -1)
	ircpanic("get_line: input == -1 is illegal value");

    /* By the time we get here, stuff->done has been set to 1, which means that get_line_return has already freed the interesting
     * items in stuff and removed it from the list. Noone but us has a pointer to it, so we free it here. */
    new_free(&stuff->saved_input);
    new_free(&stuff->saved_prompt);
    new_free(&stuff);
}

/* This simply waits for a key to be pressed before it unrecurses.
 * It doesnt do anyting in particular with that key (it will go to 
 * the input buffer, actually)
 */
char get_a_char(void)
{
    key_pressed = 0;
    while (!key_pressed)
	io("get a char");
    update_input(UPDATE_ALL);
    return key_pressed;
}

/* 
 * io() is a ONE TIME THROUGH loop!  It simply does ONE check on the
 * file descriptors, and if there is nothing waiting, it will time
 * out and drop out.  It does everything as far as checking for exec,
 * dcc, ttys, notify, the whole ball o wax, but it does NOT iterate!
 * 
 * You should usually NOT call io() unless you are specifically waiting
 * for something from a file descriptor.  It doesnt look like bad things
 * will happen if you call this elsewhere, but its long time behavior has
 * not been observed.  It *does* however, appear to be much more reliable
 * then the old irc_io, and i even know how this works. >;-)
 */
extern void set_screens(fd_set *, fd_set *);

void io(const char *what)
{
    static int first_time = 1, level = 0;
    static struct timeval cursor_timeout, clock_timeout, right_away, timer, *timeptr = NULL;
    int hold_over;
    fd_set rd, wd;
    static int old_level = 0;
    Screen *screen, *old_current_screen = current_screen;
    static const char *caller[51] = { NULL };	/* XXXX */
    static int last_warn = 0;
    time_t now = time(NULL);

    level++;

    if (level != old_level) {
	TRACE("Moving from io level [%d] to level [%d] from [%s]\n",
	      old_level, level, what);
	old_level = level;
    }

    if (level && (level - last_warn == 5)) {
	last_warn = level;
	yell("io's nesting level is [%d],  [%s]<-[%s]<-[%s]<-[%s]<-[%s]<-[%s]", level, what, caller[level - 1], caller[level - 2],
	     caller[level - 3], caller[level - 4]);
	if (level % 50 == 0)
	    ircpanic("Ahoy there matey!  Abandon ship!");
	return;
    } else if (level && (last_warn - level == 5))
	last_warn -= 5;

    caller[level] = what;

    /* first time we run this function, set up the timeouts */
    if (first_time) {
	first_time = 0;

	/* time before cursor jumps from display area to input line */
	cursor_timeout.tv_usec = 0L;
	cursor_timeout.tv_sec = 1L;

	/* 
	 * time delay for updating of internal clock
	 *
	 * Instead of looking every 15 seconds and seeing if
	 * the clock has changed, we now figure out how much
	 * time there is to the next clock change and then wait
	 * until then.  There is a small performance penalty 
	 * in actually calculating when the next minute will tick, 
	 * but that will be offset by the fact that we will only
	 * call select() once a minute instead of 4 times.
	 */
	clock_timeout.tv_usec = 0L;

	right_away.tv_usec = 0L;
	right_away.tv_sec = 0L;

	timer.tv_usec = 0L;
    }

    /* SET UP TIMEOUTS USED IN SELECTING */

    FD_ZERO(&wd);
    FD_ZERO(&rd);

    set_screens(&rd, &wd);
    set_dcc_bits(&rd, &wd);
    set_server_bits(&rd, &wd);
    set_process_bits(&rd);

    clock_timeout.tv_sec = (60 - now % 60);

    /* if the time changes, now - idle_time can be negative, and cause all kinds of problems */
    if (now > idle_time)
	clock_timeout.tv_sec += now - idle_time;

    if (!timeptr)
	timeptr = &clock_timeout;
    timer.tv_sec = TimerTimeout();
    if (timer.tv_sec <= timeptr->tv_sec)
	timeptr = &timer;
#if 0
    if ((hold_over = unhold_windows()) != 0)
	timeptr = &right_away;
#else
    hold_over = 0;
#endif

    /* go ahead and wait for some data to come in */
    switch (new_select(&rd, &wd, timeptr)) {
    case 0:
	break;
    case -1:
	{
	    /* if we just got a sigint */
	    if (cntl_c_hit) {
		key_pressed = 3;
		edit_char('\003');
		cntl_c_hit = 0;
	    } else if (errno != EINTR && errno > 0)
		yell("Select failed with [%s]", strerror(errno));
	    break;

	}

	/* we got something on one of the descriptors */
    default:
	{
	    set_current_screen(last_input_screen);
	    dcc_check(&rd, &wd);
	    do_server(&rd, &wd);
	    do_processes(&rd);
	    do_screens(&rd);
	    dcc_check_idle();
	    set_current_screen(old_current_screen);
	    break;
	}
    }
    ExecuteTimers();
    while (got_sigchild) {
	check_wait_status(-1);
	got_sigchild--;
    }

    if (!hold_over)
	cursor_to_input();
    timeptr = &clock_timeout;

    for (screen = screen_list; screen; screen = screen->next)
	if (screen->alive && is_cursor_in_display(screen))
	    timeptr = &cursor_timeout;

    if (get_int_var(LLOOK_VAR) && from_server > -1 && !server_list[from_server].link_look) {
	if (time(NULL) - server_list[from_server].link_look_time > get_int_var(LLOOK_DELAY_VAR)) {
	    server_list[from_server].link_look++;
	    send_to_server(SERVER(from_server), "LINKS");
	    server_list[from_server].link_look_time = time(NULL);
	}
    }
    if (update_clock(0)) {
	do_notify();
	clean_whowas_chan_list();
	clean_whowas_list();
	clean_flood_list();
	if (get_int_var(CLOCK_VAR)) {
	    update_all_status(curr_scr_win, NULL, 0);
	    cursor_to_input();
	}
	check_server_connect(from_server);
    }

    /* (set in term.c) -- we should redraw the screen here */
    if (need_redraw)
	refresh_screen(0, NULL);

    caller[level] = NULL;
    level--;
    return;
}

/* This is the main xaric "loop" */
static void xaric_main(void)
{
    if (load_ircrc)
	load_scripts();

    get_connected(0);
    set_input(empty_str);
    get_line(NULL, -1, send_line);
    ircpanic("get_line() returned");
}

int main(int argc, char *argv[], char *envp[])
{
    int retval = EXIT_FAILURE;

    srand((unsigned) time(NULL));
    time(&start_time);
    time(&idle_time);
    prog_name = argv[0];

    if (!isatty(0)) {
	fprintf(stderr, "Woops I need a tty!\n");
	exit(1);
    }
    xaric_init(argc, argv);
    xaric_main();
    return retval;
}
