/* 
 *  Copyright Colten Edwards (c) 1996
 */

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

#include <stdio.h>
#include <ctype.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>

#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <unistd.h>

#if defined(sparc) && defined(sun4c)
#include <sys/rusage.h>
#endif

#ifdef HAVE_ASSERT_H
# include <assert.h>
#endif

#include "irc.h"
#include "server.h"
#include "commands.h"
#include "vars.h"
#include "ircaux.h"
#include "lastlog.h"
#include "window.h"
#include "screen.h"
#include "whois.h"
#include "hook.h"
#include "input.h"
#include "ignore.h"
#include "keys.h"
#include "names.h"
#include "history.h"
#include "funny.h"
#include "ctcp.h"
#include "dcc.h"
#include "output.h"
#include "exec.h"
#include "notify.h"
#include "numbers.h"
#include "status.h"
#include "list.h"
#include "timer.h"
#include "misc.h"
#include "flood.h"
#include "parse.h"
#include "whowas.h"
#include "hash.h"
#include "fset.h"

char *alias_special_char(char **, char *, char *, char *, int *);

extern time_t start_time;
extern int in_server_ping;

int split_watch = 0;
int serv_action = 0;
int first_time = 0;

char *convertstring = NULL;
extern int in_cparse;

extern char *mircansi(char *);

extern NickTab *tabkey_array;

extern Ignore *ignored_nicks;

#ifdef REVERSE_WHITE_BLACK
static const char *color_str[] = {
    "\e[0m", "\e[0;34m", "\e[0;32m", "\e[0;36m", "\e[0;31m", "\e[0;35m", "\e[0;33m", "\e[0;30m",
    "\e[1;37m", "\e[1;34m", "\e[1;32m", "\e[1;36m", "\e[1;31m", "\e[1;35m", "\e[1;33m", "\e[1;30m", "\e[0m",
    "\e[0;47m", "\e[0;41m", "\e[0;42m", "\e[0;43m", "\e[0;44m", "\e[0;45m", "\e[0;46m", "\e[0;40m",
    "\e[1;47m", "\e[1;41m", "\e[1;42m", "\e[1;43m", "\e[1;44m", "\e[1;45m", "\e[1;46m", "\e[1;40m",
    "\e[7m", "\e[1m", "\e[5m", "\e[4m"
};

#else

static const char *color_str[] = {
    "\e[0;30m", "\e[0;34m", "\e[0;32m", "\e[0;36m", "\e[0;31m", "\e[0;35m", "\e[0;33m", "\e[0m",
    "\e[1;30m", "\e[1;34m", "\e[1;32m", "\e[1;36m", "\e[1;31m", "\e[1;35m", "\e[1;33m", "\e[1;37m", "\e[0m",
    "\e[0;40m", "\e[0;41m", "\e[0;42m", "\e[0;43m", "\e[0;44m", "\e[0;45m", "\e[0;46m", "\e[0;47m",
    "\e[1;40m", "\e[1;41m", "\e[1;42m", "\e[1;43m", "\e[1;44m", "\e[1;45m", "\e[1;46m", "\e[1;47m",
    "\e[7m", "\e[1m", "\e[5m", "\e[4m"
};
#endif

irc_server *tmplink = NULL, *server_last = NULL, *split_link = NULL, *map = NULL;

#define getrandom(min, max) ((rand() % (int)(((max)+1) - (min))) + (min))

char *awaymsg = NULL;

char *convert_time(time_t ltime)
{
    unsigned int days = 0, hours = 0, minutes = 0, seconds = 0;
    static char buffer[100];

    *buffer = '\0';
    seconds = ltime % 60;
    ltime = (ltime - seconds) / 60;
    minutes = ltime % 60;
    ltime = (ltime - minutes) / 60;
    hours = ltime % 24;
    days = (ltime - hours) / 24;
    sprintf(buffer, "%2udd %2udh %uldm %2uds", days, hours, minutes, seconds);
    return (*buffer ? buffer : empty_str);
}

int check_serverlag(void *args)
{
    char *servern = (char *) args;

    if (servern && *servern) {
	int i;

	for (i = 0; i < number_of_servers; i++) {
	    if ((!my_stricmp(servern, get_server_itsname(i)) || !my_stricmp(servern, get_server_name(i)))
		&& is_server_connected(i)) {
		server_list[i].lag_time = time(NULL);
		in_server_ping++;
		send_to_server(SERVER(from_server), "PING %lu %s", server_list[i].lag_time, servern);
		server_list[i].lag = -1;
		break;
	    }
	}
    }
    return 0;
}

int timer_unban(void *args)
{
    char *p = (char *) args;
    char *channel;
    struct channel *chan;
    char *ban;
    char *serv;
    int server = from_server;

    serv = next_arg(p, &p);
    if (my_atol(serv) != server)
	server = my_atol(serv);
    if (server < 0 || server > number_of_servers || !server_list[server].connected)
	server = from_server;
    channel = next_arg(p, &p);
    ban = next_arg(p, &p);
    if ((chan = (struct channel *) find_in_list((struct list **) &server_list[server].chan_list, channel, 0))
	&& ban_is_on_channel(ban, chan))
	send_to_server(SERVER(server), "MODE %s -b %s", channel, ban);
    new_free(&serv);
    return 0;
}

char *clear_server_flags(char *uh)
{
    while (uh && (*uh == '~' || *uh == '#' || *uh == '+' || *uh == '-' || *uh == '=' || *uh == '^'))
	uh++;
    return uh;
}

/*  
 * (max server send) and max mirc color change is 256
 * so 256 * 8 should give us a safety margin for hackers.
 * BIG_BUFFER is 1024 * 3 is 3072 whereas 256*8 is 2048
 */

/*static char newline[3*BIG_BUFFER_SIZE+1]; */
static char newline1[3 * BIG_BUFFER_SIZE + 1];

char *mircansi(char *line)
{
/* mconv v1.00 (c) copyright 1996 Ananda, all rights reserved.  */
/* -----------------------------------------------------------  */
/* mIRC->ansi color code convertor:     12.26.96                */
/* map of mIRC color values to ansi color codes                 */
/* format: ansi fg color        ansi bg color                   */
/* modified Colten Edwards                                      */
    static const struct {
	const char *fg, *bg;
    } codes[16] = {
	{
	"\e[1;37m", "\e[47m"},	/* white */
	{
	"\e[0;30m", "\e[40m"},	/* black (grey for us) */
	{
	"\e[0;34m", "\e[44m"},	/* blue */
	{
	"\e[0;32m", "\e[42m"},	/* green */
	{
	"\e[0;31m", "\e[41m"},	/* red */
	{
	"\e[0;33m", "\e[43m"},	/* brown */
	{
	"\e[0;35m", "\e[45m"},	/* magenta */
	{
	"\e[1;31m", "\e[41m"},	/* bright red */
	{
	"\e[1;33m", "\e[43m"},	/* yellow */
	{
	"\e[1;32m", "\e[42m"},	/* bright green */
	{
	"\e[0;36m", "\e[46m"},	/* cyan */
	{
	"\e[1;36m", "\e[46m"},	/* bright cyan */
	{
	"\e[1;34m", "\e[44m"},	/* bright blue */
	{
	"\e[1;35m", "\e[45m"},	/* bright magenta */
	{
	"\e[1;30m", "\e[40m"},	/* dark grey */
	{
	"\e[0;37m", "\e[47m"}	/* grey */
    };
    register char *sptr = line, *dptr = newline1;
    short code;

    if (!*line)
	return empty_str;
    *newline1 = 0;
    while (*sptr) {
	if (*sptr == 0x3 && isdigit(sptr[1])) {
	    sptr++;
	    code = atoi(sptr);
	    if (code > 15 || code <= 0)
		continue;
	    while (isdigit(*sptr))
		sptr++;
	    strcpy(dptr, codes[code].fg);
	    while (*dptr)
		dptr++;
	    if (*sptr == ',') {
		sptr++;
		code = atoi(sptr);
		if (code >= 0 && code <= 15) {
		    strcpy(dptr, codes[code].bg);
		    while (*dptr)
			dptr++;
		}
		while (isdigit(*sptr))
		    sptr++;
	    }
	} else if (*sptr == 0x3) {
	    strcpy(dptr, "\e[0m");
	    while (*dptr)
		dptr++;
	    sptr++;
	} else
	    *dptr++ = *sptr++;
    }
    *dptr = 0;
    return newline1;
}

/* Borrowed with permission from FLiER */
char *stripansicodes(const char *line)
{
    register char *tstr;
    register char *nstr;
    int gotansi = 0;

    tstr = (char *) line;
    nstr = newline1;
    while (*tstr) {
	if (*tstr == 0x1B)
	    gotansi = 1;
	if (gotansi && isalpha(*tstr))
	    gotansi = 0;
	else if (!gotansi) {
	    *nstr = *tstr;
	    nstr++;
	}
	tstr++;
    }
    *nstr = 0;
    return newline1;
}

char *stripansi(char *line)
{
    register char *cp;
    char *newline;

    newline = m_strdup(line);
    for (cp = newline; *cp; cp++)
	if (*cp < 31 && *cp > 13)
	    if (*cp != 1 && *cp != 15 && *cp != 22)
		*cp = (*cp & 127) | 64;
    return newline;
}

int check_split(char *nick, char *reason, char *chan)
{
    char *bogus = get_string_var(FAKE_SPLIT_PATS_VAR);
    char *Reason = m_strdup(reason);
    char *tmp;

    tmp = Reason;
    if (word_count(Reason) > 3)
	goto fail_split;
    if (match("%.% %.%", Reason) && !strstr(Reason, "))")) {
	char *host1 = next_arg(Reason, &Reason);
	char *host2 = next_arg(Reason, &Reason);

	if (!my_stricmp(host1, host2))
	    goto fail_split;
	if (match(host1, "*..*") || match(host2, "*..*"))
	    goto fail_split;
	if (bogus) {
	    char *copy = NULL;
	    char *b_check;
	    char *temp;

	    malloc_strcpy(&copy, bogus);
	    temp = copy;
	    while ((b_check = next_arg(copy, &copy))) {
		if (match(b_check, host1) || match(b_check, host2)) {
		    new_free(&temp);
		    goto fail_split;
		}
	    }
	    new_free(&temp);
	}
	new_free(&tmp);
	return 1;
    }
  fail_split:
    new_free(&tmp);
    return 0;
}

void clear_array(NickTab ** tmp)
{
    NickTab *t, *q;

    for (t = *tmp; t;) {
	q = t->next;
	new_free(&t->nick);
	new_free(&t->type);
	new_free(&t);
	t = q;
    }
    *tmp = NULL;
}

void userage(const char *command, const char *use)
{
    if (do_hook(USAGE_LIST, "%s %s", command, use ? use : "No Help Available for this command"))
	put_it("%s",
	       convert_output_format(get_fset_var(FORMAT_USAGE_FSET), "%s %s", command,
				     convert_output_format(use ? use : "%WNo Help available for this command", NULL, NULL)));
}

char *random_str(int min, int max)
{
    int i, ii;
    static char str[BIG_BUFFER_SIZE + 1];

    i = getrandom(min, max);
    for (ii = 0; ii < i; ii++)
	str[ii] = (char) getrandom(97, 122);
    str[ii] = '\0';
    return str;
}

int rename_file(char *old_file, char **new_file)
{
    char *tmp = NULL, *new_f = NULL;
    char c = 'a';
    FILE *fp;

    if (get_string_var(DCC_DLDIR_VAR))
	malloc_sprintf(&tmp, "%s/%%c%s", get_string_var(DCC_DLDIR_VAR), *new_file);
    else
	malloc_sprintf(&tmp, "%%c%s", *new_file);
    malloc_sprintf(&new_f, tmp, c);
    while ((fp = fopen(new_f, "r")) != NULL) {
	fclose(fp);
	c++;
	sprintf(new_f, tmp, c);
    }
    if (fp != NULL)
	fclose(fp);
    new_free(&tmp);
    new_free(&new_f);
    malloc_sprintf(new_file, "%c%s", c, *new_file);
    return 0;
}

int isme(char *nick)
{
    return ((my_stricmp(nick, get_server_nickname(from_server)) == 0) ? 1 : 0);
}

void clear_link(irc_server ** serv1)
{
    irc_server *temp = *serv1, *hold;

    while (temp != NULL) {
	hold = temp->next;
	new_free(&temp->name);
	new_free(&temp->link);
	new_free(&temp->time);
	new_free(&temp);
	temp = hold;
    }
    *serv1 = NULL;
}

irc_server *add_server(irc_server ** serv1, char *channel, char *arg, int hops, char *time)
{
    irc_server *serv2;

    serv2 = new_malloc(sizeof(irc_server));
    serv2->next = *serv1;
    malloc_strcpy(&serv2->name, channel);
    malloc_strcpy(&serv2->link, arg);
    serv2->hopcount = hops;
    serv2->time = m_strdup(time);
    *serv1 = serv2;
    return serv2;
}

int find_server(irc_server * serv1, char *channel)
{
    register irc_server *temp;

    for (temp = serv1; temp; temp = temp->next) {
	if (!my_stricmp(temp->name, channel))
	    return 1;
    }
    return 0;
}

void add_split_server(char *name, char *link, int hops)
{
    irc_server *temp;

    temp = add_server(&split_link, name, link, hops, update_clock(GET_TIME));
    temp->status = SPLIT;
}

irc_server *check_split_server(char *server)
{
    register irc_server *temp;

    for (temp = split_link; temp; temp = temp->next)
	if (!my_stricmp(temp->name, server))
	    return temp;
    return NULL;
}

void remove_split_server(char *server)
{
    irc_server *temp;

    if ((temp = (irc_server *) remove_from_list((struct list **) &split_link, server))) {
	new_free(&temp->name);
	new_free(&temp->link);
	new_free(&temp->time);
	new_free(&temp);
    }
}

void parse_364(char *channel, char *args, char *subargs)
{
    if (!*channel || !*args || from_server < 0)
	return;

    add_server(&tmplink, channel, args, atol(subargs), update_clock(GET_TIME));
}

void parse_365(char *channel, char *args, char *subargs)
{
    register irc_server *serv1;

    for (serv1 = server_last; serv1; serv1 = serv1->next) {
	if (!find_server(tmplink, serv1->name)) {
	    if (!(serv1->status & SPLIT))
		serv1->status = SPLIT;
	    if (serv1->count)
		continue;
	    serv1->time = m_strdup(update_clock(GET_TIME));
	    if (do_hook(LLOOK_SPLIT_LIST, "%s %s %d %s", serv1->name, serv1->link, serv1->hopcount, serv1->time))
		put_it("%s",
		       convert_output_format(get_fset_var(FORMAT_NETSPLIT_FSET), "%s %s %s %d", serv1->time,
					     serv1->name, serv1->link, serv1->hopcount));
	    serv1->count++;
	} else {
	    if (serv1->status & SPLIT) {
		serv1->status = ~SPLIT;
		if (do_hook(LLOOK_JOIN_LIST, "%s %s %d %s", serv1->name, serv1->link, serv1->hopcount, serv1->time))
		    put_it("%s",
			   convert_output_format(get_fset_var(FORMAT_NETJOIN_FSET), "%s %s %s %d",
						 serv1->time, serv1->name, serv1->link, serv1->hopcount));
		serv1->count = 0;
	    }
	}
    }
    for (serv1 = tmplink; serv1; serv1 = serv1->next) {
	if (!find_server(server_last, serv1->name)) {
	    if (first_time == 1) {
		if (do_hook(LLOOK_ADDED_LIST, "%s %s %d", serv1->name, serv1->link, serv1->hopcount))
		    put_it("%s",
			   convert_output_format(get_fset_var(FORMAT_NETADD_FSET), "%s %s %s %d",
						 serv1->time, serv1->name, serv1->link, serv1->hopcount));
		serv1->count = 0;
	    }
	    add_server(&server_last, serv1->name, serv1->link, serv1->hopcount, update_clock(GET_TIME));
	}
    }
    first_time = 1;
    clear_link(&tmplink);
}

void log_toggle(int flag, struct channel *chan)
{
    char *logfile;

    if (((logfile = get_string_var(MSGLOGFILE_VAR)) == NULL)) {
	bitchsay("You must set the MSGLOGFILE first!");
	set_int_var(MSGLOG_VAR, 0);
	return;
    }
    logmsg(LOG_CURRENT, NULL, NULL, flag ? 1 : 2);
}

void not_on_a_channel(Window * win)
{
    if (win)
	message_to(win->refnum);
    bitchsay("You're not on a channel!");
    message_to(0);
}

int are_you_opped(char *channel)
{
    return is_chanop(channel, get_server_nickname(from_server));
}

void error_not_opped(char *channel)
{
    say("You're not opped on %s", channel);
}

int freadln(FILE * stream, char *lin)
{
    char *p;

    do
	p = fgets(lin, BIG_BUFFER_SIZE / 2, stream);
    while (p && (*lin == '#'));

    if (!p)
	return 0;
    chop(lin, 1);
    return 1;
}

/* wtf is this doing? */
char *get_reason(char *nick)
{
    static char reason[BIG_BUFFER_SIZE / 2 + 1];
    char *temp = get_string_var(DEFAULT_REASON_VAR);

    strncpy(reason,
	    stripansicodes(convert_output_format(temp, "%s %s", nick ? nick : "error", get_server_nickname(from_server))),
	    sizeof(reason) - 1);
    *reason = '\0';

    return reason;
}

char *get_signoffreason(char *nick)
{
    static char reason[BIG_BUFFER_SIZE / 2 + 1];

    *reason = '\0';

    strncpy(reason,
	    stripansicodes(convert_output_format
			   (get_string_var(SIGNOFF_REASON_VAR), "%s %s", nick ? nick : "error",
			    get_server_nickname(from_server))), sizeof(reason) - 1);
    return reason;
}

char *rights(char *string, int num)
{
    if (strlen(string) < num)
	return string;
    return (string + strlen(string) - num);
}

int numchar(char *string, char c)
{
    int num = 0;

    while (*string) {
	if (tolower(*string) == tolower(c))
	    num++;
	string++;
    }
    return num;
}

char *cluster(char *hostname)
{
    static char result[BIG_BUFFER_SIZE + 1];
    char temphost[BIG_BUFFER_SIZE + 1];
    char *host;

    if (!hostname)
	return NULL;
    host = temphost;
    *result = 0;
    memset(result, 0, sizeof(result));
    memset(temphost, 0, sizeof(temphost));
    if (strchr(hostname, '@')) {
	if (*hostname == '~')
	    hostname++;
	strcpy(result, hostname);
	*strchr(result, '@') = '\0';
	if (strlen(result) > 9) {
	    result[8] = '*';
	    result[9] = '\0';
	}
	strcat(result, "@");
	if (!(hostname = strchr(hostname, '@')))
	    return NULL;
	hostname++;
    }
    strcpy(host, hostname);

    if (*host && isdigit(*(host + strlen(host) - 1))) {
	/* Thanks icebreak for this small patch which fixes this function */
	int i;
	char *tmp;
	char count = 0;

	tmp = host;
	while ((tmp - host) < strlen(host)) {
	    if ((tmp = strchr(tmp, '.')) == NULL)
		break;
	    count++;
	    tmp++;
	}
	tmp = host;
	for (i = 0; i < count; i++)
	    tmp = strchr(tmp, '.') + 1;
	*tmp = '\0';
	strcat(result, host);
	strcat(result, "*");
    } else {
	char *tmp;
	int num;

	num = 1;
	tmp = rights(host, 3);
	if (my_stricmp(tmp, "com") &&
	    my_stricmp(tmp, "edu") && my_stricmp(tmp, "net") && (stristr(host, "com") || stristr(host, "edu")))
	    num = 2;
	while (host && *host && (numchar(host, '.') > num)) {
	    if ((host = strchr(host, '.')) != NULL)
		host++;
	    else
		return (char *) NULL;
	}
	strcat(result, "*");
	if (my_stricmp(host, temphost))
	    strcat(result, ".");
	strcat(result, host);
    }
    return result;
}

static int cparse_recurse = -1;

char *convert_output_format(const char *format, const char *str, ...)
{
    static char buffer[10 * BIG_BUFFER_SIZE + 1];
    char buffer2[2 * BIG_BUFFER_SIZE + 1];
    enum color_attributes this_color = BLACK;
    const char *t;
    char *s;
    char *copy = NULL;
    char *tmpc = NULL;
    int old_who_level = who_level;
    int bold = 0;
    int arg_flags;
    int do_color = get_int_var(DISPLAY_ANSI_VAR);

    malloc_strcpy(&copy, format);
    memset(buffer2, 0, BIG_BUFFER_SIZE / 4);

    if (cparse_recurse < 10)
	cparse_recurse++;

    if (str) {
	va_list args;

	va_start(args, str);
	vsnprintf(buffer2, sizeof(buffer2), str, args);
	va_end(args);
    }

    s = buffer + (BIG_BUFFER_SIZE * cparse_recurse);
    memset(s, 0, BIG_BUFFER_SIZE / 4);
    tmpc = copy;
    if (!tmpc)
	goto done;
    while (*tmpc) {
	if (*tmpc == '%') {
	    tmpc++;
	    switch (*tmpc) {
	    case '%':
		*s++ = *tmpc;
		break;
	    case 'n':
		this_color = NO_COLOR;
		break;
	    case 'W':
		this_color = WHITEB;
		break;
	    case 'w':
		this_color = WHITE;
		break;
	    case 'K':
		this_color = BLACKB;
		break;
	    case 'k':
		this_color = BLACK;
		break;
	    case 'G':
		this_color = GREENB;
		break;
	    case 'g':
		this_color = GREEN;
		break;
	    case 'Y':
		this_color = YELLOWB;
		break;
	    case 'y':
		this_color = YELLOW;
		break;
	    case 'C':
		this_color = CYANB;
		break;
	    case 'c':
		this_color = CYAN;
		break;
	    case 'B':
		this_color = BLUEB;
		break;
	    case 'b':
		this_color = BLUE;
		break;
	    case 'P':
	    case 'M':
		this_color = MAGENTAB;
		break;
	    case 'p':
	    case 'm':
		this_color = MAGENTA;
		break;
	    case 'R':
		this_color = REDB;
		break;
	    case 'r':
		this_color = RED;
		break;
	    case '0':
		this_color = bold ? BACK_BBLACK : BACK_BLACK;
		bold = 0;
		break;
	    case '1':
		this_color = bold ? BACK_BRED : BACK_RED;
		bold = 0;
		break;
	    case '2':
		this_color = bold ? BACK_BGREEN : BACK_GREEN;
		bold = 0;
		break;
	    case '3':
		this_color = bold ? BACK_BYELLOW : BACK_YELLOW;
		bold = 0;
		break;
	    case '4':
		this_color = bold ? BACK_BBLUE : BACK_BLUE;
		bold = 0;
		break;
	    case '5':
		this_color = bold ? BACK_BMAGENTA : BACK_MAGENTA;
		bold = 0;
		break;
	    case '6':
		this_color = bold ? BACK_BCYAN : BACK_CYAN;
		bold = 0;
		break;
	    case '7':
		this_color = bold ? BACK_BWHITE : BACK_WHITE;
		bold = 0;
		break;
	    case '8':
		this_color = REVERSE_COLOR;
		bold = 0;
		break;
	    case '9':
		this_color = BOLD_COLOR;
		bold ^= 1;
		break;
	    case 'F':
		this_color = BLINK_COLOR;
		break;
	    case 'U':
		this_color = UNDERLINE_COLOR;
		break;
	    default:
		*s++ = *tmpc;
		continue;
	    }
	    if (do_color) {
		for (t = color_str[(int) this_color]; *t; t++, s++)
		    *s = *t;
	    }
	    tmpc++;
	    continue;
	} else if (*tmpc == '$') {
	    char *new_str = NULL;

	    tmpc++;
	    in_cparse++;
	    tmpc = alias_special_char(&new_str, tmpc, buffer2, NULL, &arg_flags);
	    in_cparse--;
	    if (new_str)
		strcat(s, new_str);
	    new_free(&new_str);
	    while (*s) {
		if (*s == -1)
		    *s = ' ';
		s++;
	    }
	    if (!tmpc)
		break;
	    continue;
	} else
	    *s = *tmpc;
	tmpc++;
	s++;
    }
    *s = 0;
  done:
    s = buffer + (BIG_BUFFER_SIZE * cparse_recurse);
    if (*s)
	strcat(s, color_str[NO_COLOR]);
    who_level = old_who_level;
    new_free(&copy);

    cparse_recurse--;
    return s;
}

int matchmcommand(char *origline, int count)
{
    int startnum = 0;
    int endnum = 0;
    char *tmpstr;
    char tmpbuf[BIG_BUFFER_SIZE];

    strcpy(tmpbuf, origline);
    tmpstr = tmpbuf;
    if (*tmpstr == '*')
	return (1);
    while (tmpstr && *tmpstr) {
	startnum = 0;
	endnum = 0;
	if (tmpstr && *tmpstr && *tmpstr == '-') {
	    while (tmpstr && *tmpstr && !isdigit(*tmpstr))
		tmpstr++;
	    endnum = atoi(tmpstr);
	    startnum = 1;
	    while (tmpstr && *tmpstr && isdigit(*tmpstr))
		tmpstr++;
	} else {
	    while (tmpstr && *tmpstr && !isdigit(*tmpstr))
		tmpstr++;
	    startnum = atoi(tmpstr);
	    while (tmpstr && *tmpstr && isdigit(*tmpstr))
		tmpstr++;
	    if (tmpstr && *tmpstr && *tmpstr == '-') {
		while (tmpstr && *tmpstr && !isdigit(*tmpstr))
		    tmpstr++;
		endnum = atoi(tmpstr);
		if (!endnum)
		    endnum = 1000;
		while (tmpstr && *tmpstr && isdigit(*tmpstr))
		    tmpstr++;
	    }
	}
	if (count == startnum || (count >= startnum && count <= endnum))
	    return (1);
    }
    if (count == startnum || (count >= startnum && count <= endnum))
	return (1);
    return (0);
}

struct channel *prepare_command(int *active_server, char *channel, int need_op)
{
    int server = 0;
    struct channel *chan = NULL;

    if (!channel && !curr_scr_win->current_channel) {
	if (need_op != 3)
	    not_on_a_channel(curr_scr_win);
	return NULL;
    }
    server = curr_scr_win->server;
    *active_server = server;
    if (!(chan = lookup_channel(channel ? channel : curr_scr_win->current_channel, server, 0))) {
	if (need_op != 3)
	    not_on_a_channel(curr_scr_win);
	return NULL;
    }
    if (need_op == NEED_OP && chan && !chan->chop) {
	error_not_opped(chan->channel);
	return NULL;
    }
    return chan;
}

/* XXX nasty!!! */
char *make_channel(char *chan)
{
    static char buffer[BIG_BUFFER_SIZE + 1];

    *buffer = 0;
    if (*chan != '#' && *chan != '&' && *chan != '+' && *chan != '*')
	snprintf(buffer, IRCD_BUFFER_SIZE - 2, "#%s", chan);
    else
	strncpy(buffer, chan, BIG_BUFFER_SIZE);
    return buffer;
}

void add_to_irc_map(char *server1, char *distance)
{
    irc_server *tmp, *insert, *prev;
    int dist = 0;

    if (distance)
	dist = atoi(distance);
    tmp = new_malloc(sizeof(irc_server));
    malloc_strcpy(&tmp->name, server1);
    tmp->hopcount = dist;
    if (!map) {
	map = tmp;
	return;
    }
    for (insert = map, prev = map; insert && insert->hopcount < dist;) {
	prev = insert;
	insert = insert->next;
    }
    if (insert && insert->hopcount >= dist) {
	tmp->next = insert;
	if (insert == map)
	    map = tmp;
	else
	    prev->next = tmp;
    } else
	prev->next = tmp;
}

void show_server_map(void)
{
    int prevdist = 0;
    irc_server *tmp;
    char tmp1[80];
    char tmp2[BIG_BUFFER_SIZE + 1];
    char *ascii = "-> ";

    if (map)
	prevdist = map->hopcount;

    for (tmp = map; tmp; tmp = map) {
	map = tmp->next;
	if (!tmp->hopcount || tmp->hopcount != prevdist)
	    strmcpy(tmp1, convert_output_format("%K[%G$0%K]", "%d", tmp->hopcount), 79);
	else
	    *tmp1 = 0;
	snprintf(tmp2, BIG_BUFFER_SIZE, "$G %%W$[-%d]1%%c $0 %s", tmp->hopcount * 3, tmp1);
	put_it("%s", convert_output_format(tmp2, "%s %s", tmp->name, prevdist != tmp->hopcount ? ascii : empty_str));
	prevdist = tmp->hopcount;
	new_free(&tmp->name);
	new_free(&tmp);
    }
}

extern int timed_server(void *);
extern int in_timed_server;
void check_server_connect(int server)
{
    if ((server == -1) || (!in_timed_server && server_list[server].last_msg + 50 < time(NULL))) {
	add_timer("", 10, 1, timed_server, m_strdup("0"), NULL);
	in_timed_server++;
    }
}

const char *country(const char *hostname)
{
    typedef struct _domain {
	const char *code;
	const char *country;
    } Domain;
    static const Domain domain[] = {
	{"AD", "Andorra"},
	{"AE", "United Arab Emirates"},
	{"AF", "Afghanistan"},
	{"AG", "Antigua and Barbuda"},
	{"AI", "Anguilla"},
	{"AL", "Albania"},
	{"AM", "Armenia"},
	{"AN", "Netherlands Antilles"},
	{"AO", "Angola"},
	{"AQ", "Antarctica (pHEAR)"},
	{"AR", "Argentina"},
	{"AS", "American Samoa"},
	{"AT", "Austria"},
	{"AU", "Australia"},
	{"AW", "Aruba"},
	{"AZ", "Azerbaijan"},
	{"BA", "Bosnia and Herzegovina"},
	{"BB", "Barbados"},
	{"BD", "Bangladesh"},
	{"BE", "Belgium"},
	{"BF", "Burkina Faso"},
	{"BG", "Bulgaria"},
	{"BH", "Bahrain"},
	{"BI", "Burundi"},
	{"BJ", "Benin"},
	{"BM", "Bermuda"},
	{"BN", "Brunei Darussalam"},
	{"BO", "Bolivia"},
	{"BR", "Brazil"},
	{"BS", "Bahamas"},
	{"BT", "Bhutan"},
	{"BV", "Bouvet Island"},
	{"BW", "Botswana"},
	{"BY", "Belarus"},
	{"BZ", "Belize"},
	{"CA", "Canada"},
	{"CC", "Cocos Islands"},
	{"CF", "Central African Republic"},
	{"CG", "Congo"},
	{"CH", "Switzerland"},
	{"CI", "Cote D'ivoire"},
	{"CK", "Cook Islands"},
	{"CL", "Chile"},
	{"CM", "Cameroon"},
	{"CN", "China"},
	{"CO", "Colombia"},
	{"CR", "Costa Rica"},
	{"CS", "Former Czechoslovakia"},
	{"CU", "Cuba"},
	{"CV", "Cape Verde"},
	{"CX", "Christmas Island"},
	{"CY", "Cyprus"},
	{"CZ", "Czech Republic"},
	{"DE", "Germany"},
	{"DJ", "Djibouti"},
	{"DK", "Denmark"},
	{"DM", "Dominica"},
	{"DO", "Dominican Republic"},
	{"DZ", "Algeria"},
	{"EC", "Ecuador"},
	{"EE", "Estonia"},
	{"EG", "Egypt"},
	{"EH", "Western Sahara"},
	{"ER", "Eritrea"},
	{"ES", "Spain"},
	{"ET", "Ethiopia"},
	{"FI", "Finland"},
	{"FJ", "Fiji"},
	{"FK", "Falkland Islands"},
	{"FM", "Micronesia"},
	{"FO", "Faroe Islands"},
	{"FR", "France"},
	{"FX", "France, Metropolitan"},
	{"GA", "Gabon"},
	{"GB", "Great Britain"},
	{"GD", "Grenada"},
	{"GE", "Georgia"},
	{"GF", "French Guiana"},
	{"GH", "Ghana"},
	{"GI", "Gibraltar"},
	{"GL", "Greenland"},
	{"GM", "Gambia"},
	{"GN", "Guinea"},
	{"GP", "Guadeloupe"},
	{"GQ", "Equatorial Guinea"},
	{"GR", "Greece"},
	{"GS", "S. Georgia and S. Sandwich Isls."},
	{"GT", "Guatemala"},
	{"GU", "Guam"},
	{"GW", "Guinea-Bissau"},
	{"GY", "Guyana"},
	{"HK", "Hong Kong"},
	{"HM", "Heard and McDonald Islands"},
	{"HN", "Honduras"},
	{"HR", "Croatia"},
	{"HT", "Haiti"},
	{"HU", "Hungary"},
	{"ID", "Indonesia"},
	{"IE", "Ireland"},
	{"IL", "Israel"},
	{"IN", "India"},
	{"IO", "British Indian Ocean Territory"},
	{"IQ", "Iraq"},
	{"IR", "Iran"},
	{"IS", "Iceland"},
	{"IT", "Italy"},
	{"JM", "Jamaica"},
	{"JO", "Jordan"},
	{"JP", "Japan"},
	{"KE", "Kenya"},
	{"KG", "Kyrgyzstan"},
	{"KH", "Cambodia"},
	{"KI", "Kiribati"},
	{"KM", "Comoros"},
	{"KN", "St. Kitts and Nevis"},
	{"KP", "North Korea"},
	{"KR", "South Korea"},
	{"KW", "Kuwait"},
	{"KY", "Cayman Islands"},
	{"KZ", "Kazakhstan"},
	{"LA", "Laos"},
	{"LB", "Lebanon"},
	{"LC", "Saint Lucia"},
	{"LI", "Liechtenstein"},
	{"LK", "Sri Lanka"},
	{"LR", "Liberia"},
	{"LS", "Lesotho"},
	{"LT", "Lithuania"},
	{"LU", "Luxembourg"},
	{"LV", "Latvia"},
	{"LY", "Libya"},
	{"MA", "Morocco"},
	{"MC", "Monaco"},
	{"MD", "Moldova"},
	{"MG", "Madagascar"},
	{"MH", "Marshall Islands"},
	{"MK", "Macedonia"},
	{"ML", "Mali"},
	{"MM", "Myanmar"},
	{"MN", "Mongolia"},
	{"MO", "Macau"},
	{"MP", "Northern Mariana Islands"},
	{"MQ", "Martinique"},
	{"MR", "Mauritania"},
	{"MS", "Montserrat"},
	{"MT", "Malta"},
	{"MU", "Mauritius"},
	{"MV", "Maldives"},
	{"MW", "Malawi"},
	{"MX", "Mexico"},
	{"MY", "Malaysia"},
	{"MZ", "Mozambique"},
	{"NA", "Namibia"},
	{"NC", "New Caledonia"},
	{"NE", "Niger"},
	{"NF", "Norfolk Island"},
	{"NG", "Nigeria"},
	{"NI", "Nicaragua"},
	{"NL", "Netherlands"},
	{"NO", "Norway"},
	{"NP", "Nepal"},
	{"NR", "Nauru"},
	{"NT", "Neutral Zone (pHEAR)"},
	{"NU", "Niue"},
	{"NZ", "New Zealand"},
	{"OM", "Oman"},
	{"PA", "Panama"},
	{"PE", "Peru"},
	{"PF", "French Polynesia"},
	{"PG", "Papua New Guinea"},
	{"PH", "Philippines"},
	{"PK", "Pakistan"},
	{"PL", "Poland"},
	{"PM", "St. Pierre and Miquelon"},
	{"PN", "Pitcairn"},
	{"PR", "Puerto Rico"},
	{"PT", "Portugal"},
	{"PW", "Palau"},
	{"PY", "Paraguay"},
	{"QA", "Qatar"},
	{"RE", "Reunion"},
	{"RO", "Romania"},
	{"RU", "Russian Federation"},
	{"RW", "Rwanda"},
	{"SA", "Saudi Arabia"},
	{"Sb", "Solomon Islands"},
	{"SC", "Seychelles"},
	{"SD", "Sudan"},
	{"SE", "Sweden"},
	{"SG", "Singapore"},
	{"SH", "St. Helena"},
	{"SI", "Slovenia"},
	{"SJ", "Svalbard and Jan Mayen Islands"},
	{"SK", "Slovak Republic"},
	{"SL", "Sierra Leone"},
	{"SM", "San Marino"},
	{"SN", "Senegal"},
	{"SO", "Somalia"},
	{"SR", "Suriname"},
	{"ST", "Sao Tome and Principe"},
	{"SU", "Former USSR"},
	{"SV", "El Salvador"},
	{"SY", "Syria"},
	{"SZ", "Swaziland"},
	{"TC", "Turks and Caicos Islands"},
	{"TD", "Chad"},
	{"TF", "French Southern Territories"},
	{"TG", "Togo"},
	{"TH", "Thailand"},
	{"TJ", "Tajikistan"},
	{"TK", "Tokelau"},
	{"TM", "Turkmenistan"},
	{"TN", "Tunisia"},
	{"TO", "Tonga"},
	{"TP", "East Timor"},
	{"TR", "Turkey"},
	{"TT", "Trinidad and Tobago"},
	{"TV", "Tuvalu"},
	{"TW", "Taiwan"},
	{"TZ", "Tanzania"},
	{"UA", "Ukraine"},
	{"UG", "Uganda"},
	{"UK", "United Kingdom"},
	{"UM", "US Minor Outlying Islands"},
	{"US", "United States of America"},
	{"UY", "Uruguay"},
	{"UZ", "Uzbekistan"},
	{"VA", "Vatican City State"},
	{"VC", "St. Vincent and the grenadines"},
	{"VE", "Venezuela"},
	{"VG", "British Virgin Islands"},
	{"VI", "US Virgin Islands"},
	{"VN", "Vietnam"},
	{"VU", "Vanuatu"},
	{"WF", "Wallis and Futuna Islands"},
	{"WS", "Samoa"},
	{"YE", "Yemen"},
	{"YT", "Mayotte"},
	{"YU", "Yugoslavia"},
	{"ZA", "South Africa"},
	{"ZM", "Zambia"},
	{"ZR", "Zaire"},
	{"ZW", "Zimbabwe"},
	{"COM", "Internic Commercial"},
	{"EDU", "Educational Institution"},
	{"GOV", "Government"},
	{"INT", "International"},
	{"MIL", "Military"},
	{"NET", "Internic Network"},
	{"ORG", "Internic Non-Profit Organization"},
	{"RPA", "Old School ARPAnet"},
	{"ATO", "Nato Fiel"},
	{"MED", "United States Medical"},
	{"ARPA", "Reverse DNS"},
	{NULL, NULL}
    };
    static char unknown[] = "unknown";
    const char *p;
    int i = 0;

    if (!hostname || !*hostname || isdigit(hostname[strlen(hostname) - 1]))
	return unknown;
    if ((p = strrchr(hostname, '.')))
	p++;
    else
	p = hostname;
    for (i = 0; domain[i].code; i++)
	if (!my_stricmp(p, domain[i].code))
	    return domain[i].country;
    return unknown;
}

char *do_nslookup(char *host)
{
    struct hostent *temp;
    struct in_addr temp1;

    if (!host)
	return NULL;

    if (isdigit(*(host + strlen(host) - 1))) {
	temp1.s_addr = inet_addr(host);
	alarm(1);
	temp = gethostbyaddr((char *) &temp1.s_addr, sizeof(temp1.s_addr), AF_INET);
	alarm(0);
    } else {
	alarm(1);
	temp = gethostbyname(host);
	alarm(0);
    }
    if (do_hook
	(NSLOOKUP_LIST, "%s %s %s", host, temp ? temp->h_name : "",
	 temp ? (char *) inet_ntoa(*(struct in_addr *) temp->h_addr) : "")) {
	if (!temp)
	    bitchsay("Error looking up %s", host);
	else
	    bitchsay("%s is %s (%s)", host, temp->h_name, (char *) inet_ntoa(*(struct in_addr *) temp->h_addr));
    }
    return ((char *) (temp ? temp->h_name : host));
}

int charcount(char *string, char what)
{
    int x = 0;
    char *place = string;

    while (*place)
	if (*place++ == what)
	    x++;

    return x;
}

/* Given a string, remove stuff at the end */
/**
 * str_terminate_at - terminate string at chars
 * @string: string to terminate
 * @chars: what characters we terminate at
 *
 * Terminate the string at the last occurence of any
 * of the characters in chars.
 *
 * For example, terminating on "\r\n" will remove 
 * those characters from the end of the string.
 **/
char *str_terminate_at(char *string, const char *chars)
{
    char *ptr;

    assert(string);
    assert(chars);

    while (*chars) {
	if ((ptr = strrchr(string, *chars)) != NULL)
	    *ptr = '\0';
	chars++;
    }
    return string;
}

#ifdef HAVE_PTHREADS
#include <pthread.h>
/*------------------
 | Experemental.. nslookup now splits off a thread to do the work
 */

void *do_nslookup_blah(void *arg)
{
    struct hostent *temp;
    struct in_addr temp1;
    char *host = (char *) arg;

    if (!host)
	pthread_exit(NULL);

    if (isdigit(*(host + strlen(host) - 1))) {
	temp1.s_addr = inet_addr(host);
	temp = gethostbyaddr((char *) &temp1.s_addr, sizeof(temp1.s_addr), AF_INET);
    } else {
	temp = gethostbyname(host);
    }
    if (do_hook
	(NSLOOKUP_LIST, "%s %s %s", host, temp ? temp->h_name : "",
	 temp ? (char *) inet_ntoa(*(struct in_addr *) temp->h_addr) : "")) {
	if (!temp)
	    bitchsay("Error looking up %s", host);
	else
	    bitchsay("%s is %s (%s)", host, temp->h_name, (char *) inet_ntoa(*(struct in_addr *) temp->h_addr));
    }
    new_free(&arg);
    pthread_exit(NULL);
    return NULL;
}

/* arg == hostname */
void do_nslookup_thread(void *arg)
{
    pthread_t b;

    if (arg == NULL)
	return;

    pthread_create(&b, NULL, do_nslookup_blah, m_strdup(arg));
    pthread_detach(b);
}

#endif				/* HAVE_PTHREADS */
