/* 
 *  Copyright Colten Edwards (c) 1996
 */
#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


#include "irc.h"
#include "server.h"
#include "commands.h"
#include "crypt.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 "alias.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 "userlist.h"
#include "bot.h"
#include "misc.h"
#include "flood.h"
#include "parse.h"
#include "whowas.h"
#include "hash2.h"
#include "cset.h"

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

extern int user_count;
extern int shit_count;
extern int bot_count;
extern time_t start_time;
extern int in_server_ping;

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

LastMsg last_msg[MAX_LAST_MSG] = { { NULL } };
LastMsg last_dcc[MAX_LAST_MSG] = { { NULL } };
LastMsg last_notice[MAX_LAST_MSG] = { { NULL } };
LastMsg last_servermsg[MAX_LAST_MSG] = { { NULL } };
LastMsg last_sent_msg[MAX_LAST_MSG] = {{ NULL }};
LastMsg last_sent_notice[MAX_LAST_MSG] = {{ NULL }};
LastMsg last_sent_topic[1] = {{ NULL }};
LastMsg last_sent_wall[1] = {{ NULL }};
LastMsg last_topic[1] =  {{ NULL }};
LastMsg last_wall[MAX_LAST_MSG] =  {{ NULL }};
LastMsg last_invite_channel[1] = {{ NULL }};
LastMsg last_ctcp[1] = {{ NULL }};
LastMsg last_sent_ctcp[1] = {{ NULL }};

char *convertstring = NULL;
extern int in_cparse;

extern char *mircansi (char *);
#define SPLIT 1


ChannelList default_statchan = { 0 };
ChannelList *idlechan_list = NULL;

extern NickTab *tabkey_array, *autoreply_array;

CloneList *clones = NULL;

SocketList sockets[FD_SETSIZE] = {{ 0, 0, 0, NULL }};

extern Ignore *ignored_nicks;

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

#else

char *color_str[] = {
"[0;30m","[0;34m","[0;32m","[0;36m","[0;31m","[0;35m","[0;33m","[0m",
"[1;30m","[1;34m","[1;32m","[1;36m","[1;31m","[1;35m","[1;33m","[1;37m", "[0m",
"[0;40m", "[0;41m", "[0;42m","[0;43m", "[0;44m","[0;45m","[0;46m", "[0;47m",
"[1;40m", "[1;41m", "[1;42m","[1;43m", "[1;44m","[1;45m","[1;46m", "[1;47m",
"[7m", "[1m", "[5m", "[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))
{
	time_t  days = 0,hours = 0,minutes = 0,seconds = 0;
	static char buffer[100];

	context;
	*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, "%2ldd %2ldh %2ldm %2lds", days, hours, minutes, seconds);
	return(*buffer ? buffer : empty_string);
}

BUILT_IN_COMMAND(do_uptime)
{
	context;
#ifdef ONLY_STD_CHARS
	put_it("%s",convert_output_format("%G--[ %WBitchX%g-%wClient%g-%RStatistics %G]------------------------------------------",NULL));
	put_it("%s",convert_output_format("%G| %CClient Version: %W$0 $1","%s %s", irc_version, internal_version));
	put_it("%s",convert_output_format("%G| %CClient Running Since %W$0-","%s",my_ctime(start_time)));
	put_it("%s",convert_output_format("%G| %CClient Uptime: %W$0-","%s",convert_time(time(NULL)-start_time)));
	put_it("%s",convert_output_format("%G| %CCurrent UserName: %W$0-","%s", username));
	put_it("%s",convert_output_format("%G| %CCurrent RealName: %W$0-","%s", realname));
	put_it("%s",convert_output_format("%G| %CLast Recv Message: %W$0-","%s",last_msg[0].last_msg?last_msg[0].last_msg:"None"));
	put_it("%s",convert_output_format("%G| %CLast Recv Notice: %W$0-","%s",last_notice[0].last_msg?last_notice[0].last_msg:"None"));
	put_it("%s",convert_output_format("%G| %CLast Sent Msg: %W$0-","%s",last_sent_msg?last_sent_msg:"None"));
	put_it("%s",convert_output_format("%G| %CLast Sent Notice: %W$0-","%s",last_sent_notice?last_sent_notice:"None"));
	put_it("%s",convert_output_format("%G| %CLast Channel invited to: %R$0-","%s",invite_channel?invite_channel:"None"));
	put_it("%s",convert_output_format("%G| %cTotal Users on Userlist: %K[%R$0%K]","%d",user_count));
	put_it("%s",convert_output_format("%G| %cTotal Users on Shitlist: %K[%R$0%K]","%d",shit_count));
	put_it("%s",convert_output_format("%G| %cTotal Bots on Botlist:   %K[%R$0%K]","%d",bot_count));

#else
	put_it("%s",convert_output_format("%GÚÄ[ %WBitchX%gÄ%wClient%gÄ%RStatistics %G]ÄÄÄÄ---%gÄ--ÄÄ%K-%gÄÄÄÄÄ--%GÄ--ÄÄ%K-%gÄÄÄÄÄÄÄ--- %K--%g  -",NULL));
	put_it("%s",convert_output_format("%G| %CClient Version: %W$0 $1","%s %s", irc_version, internal_version));
	put_it("%s",convert_output_format("%G³ %CClient Running Since %W$0-","%s",my_ctime(start_time)));
	put_it("%s",convert_output_format("%G| %CClient Uptime: %W$0-","%s",convert_time(time(NULL)-start_time)));
	put_it("%s",convert_output_format("%G³ %CCurrent UserName: %W$0-","%s", username));
	put_it("%s",convert_output_format("%G: %CCurrent RealName: %W$0-","%s", realname));
	put_it("%s",convert_output_format("%G. %CLast Recv Message: %W$0-","%s",last_msg[0].last_msg?last_msg[0].last_msg:"None"));
	put_it("%s",convert_output_format("%G: %CLast Recv Notice: %W$0-","%s",last_notice[0].last_msg?last_notice[0].last_msg:"None"));
	put_it("%s",convert_output_format("%G. %CLast Sent Msg: %W$0-","%s",last_sent_msg[0].last_msg?last_sent_msg[0].last_msg:"None"));
	put_it("%s",convert_output_format("%G: %CLast Sent Notice: %W$0-","%s",last_sent_notice[0].last_msg?last_sent_notice[0].last_msg:"None"));
	put_it("%s",convert_output_format("%G³ %CLast Channel invited to: %R$0-","%s",invite_channel?invite_channel:"None"));
	put_it("%s",convert_output_format("%G| %cTotal Users on Userlist: %K[%R$0%K]","%d",user_count));
	put_it("%s",convert_output_format("%G³ %cTotal Users on Shitlist: %K[%R$0%K]","%d",shit_count));
	put_it("%s",convert_output_format("%G| %cTotal Bots on Botlist:   %K[%R$0%K]","%d",bot_count));

#endif
}

/* extern_write -- controls whether others may write to our terminal or not. */
/* This is basically stolen from bsd -- so its under the bsd copyright */
BUILT_IN_COMMAND(extern_write)
{
	char *tty;
	struct stat sbuf;
	const int OTHER_WRITE = 020;
	int on = 0;
	  
	if (!(tty = ttyname(2)))
	{
		yell("Internal error: notify %s", "edwac@sasknet.sk.ca");
		yell("Error in ttyname()");
		return;
	}
	if (stat(tty, &sbuf) < 0)
	{
		yell("Internal error: notify %s", "edwac@sasknet.sk.ca");
		yell("Error in stat()");
		return;
	}
	if (!args || !*args)
	{
		if (sbuf.st_mode & 020)
			bitchsay("Mesg is \002On\002");
		else
			bitchsay("Mesg is \002Off\002");
		return;
	}
	if (!my_stricmp(args, "ON") || !my_stricmp(args, "YES"))
		on = 1;
	else if (!my_stricmp(args, "OFF") || !my_stricmp(args, "NO"))
		on = 0;
	else
	{
		userage("Mesg", helparg);
		return;
	}		
	switch (on)
	{
		case 1 :
			if (chmod(tty, sbuf.st_mode | OTHER_WRITE) < 0)
			{
				yell("Sorry, couldnt set your tty's mode");
				return;
			}
			bitchsay("Mesg is \002On\002");
			break;
		case 0 :
			if (chmod(tty, sbuf.st_mode &~ OTHER_WRITE) < 0)
			{
				yell("Sorry, couldnt set your tty's mode");
				return;
			}
			bitchsay("Mesg is \002Off\002");
			break;
	}
	
}


int check_serverlag _((void *args))
{
	char *servern = (char *)args;
	context;
	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);
				send_to_server("PING %lu %s", server_list[i].lag_time, servern);
				in_server_ping++;
				server_list[i].lag = -1;
				break;
			}
		}
	}
	return 0;
}

int timer_unban _((void *args))
{
	char *p = (char *)args;
	char *channel;
	ChannelList *chan;
	char *ban;
	char *serv;
	int server = from_server;
	context;
	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 = (ChannelList *)find_in_list((List **)&server_list[server].chan_list, channel, 0)) && ban_is_on_channel(ban, chan))
		my_send_to_server(server, "MODE %s -b %s", channel, ban);
	new_free(&serv);
	return 0;
}

int timer_idlekick _((void *args))
{
char *channel = (char *)args;
ChannelList *tmp = NULL;
int kick_count = 0;
UserList *user = NULL, *bot = NULL;

	context;
	if (channel && (tmp = lookup_channel(channel, from_server, CHAN_NOUNLINK)) && tmp->chop && tmp->max_idle && tmp->check_idle)
	{
		NickList *nick;
		for (nick = next_nicklist(tmp, NULL); nick; nick = next_nicklist(tmp, nick))
		{
			if (!my_stricmp(nick->nick, get_server_nickname(from_server)))
				continue;
			if ((nick->chanop || nick->voice) && !get_cset_int_var(tmp->csets, KICK_OPS_CSET))
				continue;
			if ((user=nick->userlist) && check_channel_match(user->channels, channel))
				continue;			
			if ((bot = nick->botlist) && check_channel_match(bot->channels, channel)) 
				continue;
			if (time(NULL) - nick->idle_time >= tmp->max_idle)
			{
				if (kick_count <= get_int_var(MAX_IDLEKICKS_VAR))
				{
					char *p = NULL;
					malloc_sprintf(&p, "%s %s*!*%s", channel, nick->nick, nick->host);
					send_to_server("MODE %s +b %s*!*%s", channel, nick->nick, nick->host);
					send_to_server("KICK %s %s :\002%s\002: (Idle Channel User)", channel, nick->nick, version);
					add_timer("", 60, 1, timer_unban, m_sprintf("%d %s", from_server, p), NULL);
					new_free(&p);
				}
				else
					break;
				kick_count++;
			}
		}
	}		
	if (tmp && tmp->max_idle && tmp->check_idle)
		add_timer("", get_int_var(IDLE_CHECK_VAR), 1, timer_idlekick, channel, NULL);
	else
		new_free(&channel);

	return 0;
}

BUILT_IN_COMMAND(addidle)
{
time_t default_idle = 10 * 60;
char *channel = NULL, *p;
time_t seconds = 0;
ChannelList *tmp, *new = NULL;

	context;
	if ((p = next_arg(args, &args)))
	{
		malloc_strcpy(&channel, make_channel(p));
		if (args && *args)
			seconds = atol(args);

		if (seconds < default_idle)
			seconds = default_idle;

		if (!(new = (ChannelList *)find_in_list((List **)&idlechan_list, channel, 0)))
		{
			new = (ChannelList *)new_malloc(sizeof(ChannelList));
			memcpy(new, &default_statchan, sizeof(ChannelList));
			malloc_strcpy(&new->channel, channel);
			add_to_list((List **)&idlechan_list, (List *)new);
		} 
		new->max_idle = seconds;
		new->check_idle = (my_strnicmp(command, "UN", 2) == 0) ? 0: 1;

		if (!new->check_idle)
		{
			bitchsay("Idle checking turned %s for %s",on_off(new->check_idle), channel); 
			if ((tmp = lookup_channel(channel, from_server, CHAN_NOUNLINK)))
				tmp->check_idle = tmp->max_idle = 0;
			new_free(&channel);
			new->max_idle = 0;
		} 
		else
		{
			if ((tmp = lookup_channel(channel, from_server, CHAN_NOUNLINK)))
			{
				if (new && new->check_idle)
				{
					tmp->max_idle = new->max_idle;
					tmp->check_idle = new->check_idle;
					add_timer("", get_int_var(IDLE_CHECK_VAR), 1, timer_idlekick, channel, NULL);
					bitchsay("Idle checking turned %s for %s %d mins",on_off(tmp->check_idle), channel, (int)(tmp->max_idle/60)); 
				} 
			} else
				new_free(&channel);
		}
	} else
		userage(command, helparg);
}

BUILT_IN_COMMAND(showidle)
{
ChannelList *tmp;
char *channel = NULL;
int count = 0;
NickList *nick;
time_t ltime;
int server;

	context;
	if (args && *args)
		channel = next_arg(args, &args);
	if (!(tmp = prepare_command(&server, channel, NO_OP)))
		return;

	for (nick = next_nicklist(tmp, NULL); nick; nick = next_nicklist(tmp, nick))
	{
		if (!count && do_hook(SHOWIDLE_HEADER_LIST, "%s %ld", tmp->channel, tmp->max_idle))
			put_it("%s", convert_output_format("$G %W$a%n: Idle check for %W$0%n Max Idle Time %K[%W$1- %K]", "%s %s", tmp->channel, convert_time(tmp->max_idle)));
		ltime = time(NULL) - nick->idle_time;
		if (do_hook(SHOWIDLE_LIST, "%s %s %d %ld", nick->nick, nick->host, find_user_level(nick->nick, nick->host, tmp->channel), ltime))
			put_it("%s", convert_output_format("$[20]0 Idle%W: %K[%n$1- %K]", "%s %s", nick->nick, convert_time(ltime)));
		count++;
	}
}

BUILT_IN_COMMAND(kickidle)
{
char *channel = NULL;
ChannelList *tmp;
int kick_count = 0;
int server = from_server;

	context;
	if (args && *args)
		channel = next_arg(args, &args);

	if ((tmp = prepare_command(&server, channel, NEED_OP)) && tmp->max_idle)
	{
		NickList *nick;
		for (nick = next_nicklist(tmp, NULL); nick; nick = next_nicklist(tmp, nick))
		{
			if (!my_stricmp(nick->nick, get_server_nickname(from_server)))
				continue;
			if (nick->userlist && check_channel_match(nick->userlist->channels, tmp->channel))
				continue;			
			if (nick->botlist && check_channel_match(nick->botlist->channels, tmp->channel))
				continue;
			if (time(NULL) - nick->idle_time >= tmp->max_idle)
			{
				if (kick_count <= get_int_var(MAX_IDLEKICKS_VAR))
					my_send_to_server(server, "KICK %s %s :\002%s\002: (Idle Channel User)", tmp->channel, nick->nick, version);
				else
					bitchsay(" found idle user %-12s channel %s", nick->nick, tmp->channel);
				kick_count++;
			}
		}
	} else
		userage(command, helparg);
}

void save_idle(FILE *output)
{
ChannelList *chan;
int count = 0;

	context;
	if (!output)
		return;
	if (idlechan_list)
	{
		fprintf(output, "# %s Idle Channel list\n", version);
		for (chan = idlechan_list; chan; chan = chan->next)
		{
			if (chan->max_idle)
			{
				fprintf(output, "ADDIDLE %s %d\n", chan->channel, (int)chan->max_idle); 
				count++;
			}
		}
	}
	if (count && do_hook(SAVEFILE_LIST, "Idle %d", count))
		bitchsay("Saved %d Idle channels", count);
}

BUILT_IN_COMMAND(channel_stats)
{
ChannelList *new = NULL;
char *channel = NULL;
WhowasChanList *new1 = NULL;
int numircops = 0;
int usershere = 0;
int usersaway = 0;
int chanops = 0;
int chanunop = 0;
char *ircops = NULL;
int server = -1;

NickList *l; long nick_mem = 0, ban_mem = 0; BanList *b;


	context;

	if (args && *args)
	{
		channel = next_arg(args, &args);
		if (my_strnicmp(channel, "-ALL", strlen(channel)))
		{
			channel = make_channel(channel);
			if (!(new = prepare_command(&server, channel, 3)))
				if ((channel && !(new1 = check_whowas_chan_buffer(channel, 0))))
					return;
		}
		else
		{
	
			int stats_ops= 0, stats_dops = 0, stats_bans = 0, stats_unbans = 0;
			int stats_topics = 0, stats_kicks = 0, stats_pubs = 0, stats_parts = 0;
			int stats_signoffs = 0, stats_joins = 0;
			int total_nicks = 0, max_nicks = 0, total_bans = 0, max_bans = 0;
			int stats_sops = 0, stats_sdops = 0, stats_sbans = 0, stats_sunbans = 0;	
		
			NickList *l; 
			BanList *b;
			long nick_mem = 0, chan_mem = 0, ban_mem = 0;
			channel =  NULL;
				
			if (from_server != -1)
			{
				for (new = server_list[from_server].chan_list; new; new = new->next)
				{		
					if (!channel)
						malloc_strcpy(&channel, new->channel);
					else
					{
						malloc_strcat(&channel, ",");
						malloc_strcat(&channel, new->channel);
					}
					for (l = next_nicklist(new, NULL); l; l = next_nicklist(new, l))
					{
						switch(l->away)
						{
						case 'H':
							usershere++;
							break;
						case 'G':
							usersaway++;
						default:
							break;
						}
						if (l->ircop)
						{
							numircops++;
							malloc_strcat(&ircops, " (");
							malloc_strcat(&ircops, l->nick);
							malloc_strcat(&ircops, ")");
						}
						if (l->chanop)
							chanops++;
						else
							chanunop++;					
						nick_mem += sizeof(NickList);
					}
					for (b = new->bans; b; b = b->next)
						ban_mem += sizeof(BanList);
					chan_mem += sizeof(ChannelList);
					stats_ops += new->stats_ops;
					stats_dops += new->stats_dops;
					stats_bans += new->stats_bans;
					stats_unbans += new->stats_unbans;
					stats_topics += new->stats_topics;
					stats_kicks += new->stats_kicks;
					stats_pubs += new->stats_pubs;
					stats_parts += new->stats_parts;
					stats_signoffs += new->stats_signoffs;
					stats_joins += new->stats_joins;

					total_nicks += new->totalnicks;
					max_nicks += new->maxnicks;
					total_bans += new->totalbans;
					max_bans += new->maxbans;
					stats_sops += new->stats_sops;
					stats_sdops += new->stats_sdops;
					stats_sbans += new->stats_sbans;
					stats_sunbans += new->stats_sunbans;						
				}
			}
			if (!ircops)
				malloc_strcat(&ircops, empty_string);
if (do_hook(CHANNEL_STATS_LIST, "%s %s %s %ld %ld %ld %ld %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %s", 
	channel, empty_string, empty_string,
	nick_mem+chan_mem+ban_mem, nick_mem, 
	sizeof(ChannelList),ban_mem, 
	stats_ops, stats_dops, stats_bans, stats_unbans,
	stats_ops, stats_dops, stats_bans, stats_unbans,
	stats_signoffs, stats_joins, total_bans, max_bans,
	stats_sops, stats_sdops,stats_sbans, stats_sunbans,
	usershere, usersaway, chanops, chanunop,total_nicks,max_nicks,
	numircops, ircops))
{
put_it("%s", convert_output_format("$G %CInformation for channels %K: %W$0", "%s", channel)); 
put_it("%s", convert_output_format("     MEM usage%K:%w Total%K:%w %c$0 bytes   %K[%cNicks $1 b Chan $2 b Bans $3 b%K]", "%d %d %d %d", (int)(nick_mem+chan_mem+ban_mem), (int)nick_mem, (int)sizeof(ChannelList), (int)ban_mem));
put_it("%s", convert_output_format("Ops        %K[%W$[-5]0%K]%w  De-Ops     %K[%W$[-5]1%K]%w  Bans       %K[%W$[-5]2%K]%w  Unbans     %K[%W$[-5]3%K]%w", "%d %d %d %d", stats_ops, stats_dops, stats_bans, stats_unbans));
put_it("%s", convert_output_format("Topics     %K[%W$[-5]0%K]%w  Kicks      %K[%W$[-5]1%K]%w  Publics    %K[%W$[-5]2%K]%w  Parts      %K[%W$[-5]3%K]%w", "%d %d %d %d", stats_topics, stats_kicks, stats_pubs, stats_parts));
put_it("%s", convert_output_format("Signoffs   %K[%W$[-5]0%K]%w  Joins      %K[%W$[-5]1%K]%w  TotalBans  %K[%W$[-5]2%K]%w  MaxBans    %K[%W$[-5]3%K]%w", "%d %d %d %d", stats_signoffs, stats_joins, total_bans, max_bans));
put_it("%s", convert_output_format("ServOps    %K[%W$[-5]0%K]%w  ServDeop   %K[%W$[-5]1%K]%w  ServBans   %K[%W$[-5]2%K]%w  ServUB     %K[%W$[-5]3%K]%w", "%d %d %d %d", stats_sops, stats_sdops,stats_sbans, stats_sunbans));
put_it("%s", convert_output_format("Users Here %K[%W$[-5]0%K]%w  Users Away %K[%W$[-5]1%K]%w  Opped      %K[%W$[-5]2%K]%w  Unopped    %K[%W$[-5]3%K]%w", "%d %d %d %d", usershere, usersaway, chanops, chanunop));
put_it("%s", convert_output_format("TotalNicks %K[%W$[-5]0%K]%w  MaxNicks   %K[%W$[-5]1%K]%w", "%d %d", total_nicks,max_nicks));
put_it("%s", convert_output_format("IRCops     %K[%W$[3]0%K]%w$1-", "%d %s", numircops, ircops));
}
			new_free(&ircops);
			new_free(&channel);
			return;		
		}
	}
	else 
	{
		if (!(new = prepare_command(&server, channel, 3)))
			if ((channel && !(new1 = check_whowas_chan_buffer(channel, 0))))
				return;
	}

	if (!new && new1)
		new = new1->channellist;
	if (!new)
	{
		bitchsay("Try joining a channel first");
		return;
	}		
	if (new)
	{
		for (l = next_nicklist(new, NULL); l; l = next_nicklist(new, l))
		{
			nick_mem += sizeof(NickList);
			switch(l->away)
			{
				case 'H':
					usershere++;
					break;
				case 'G':
					usersaway++;
				default:
					break;
			}
			if (l->ircop)
			{
				numircops++;
				malloc_strcat(&ircops, " (");
				malloc_strcat(&ircops, l->nick);
				malloc_strcat(&ircops, ")");
			}
			if (l->chanop)
				chanops++;
			else
				chanunop++;					
		}
		for (b = new->bans; b; b = b->next)
			ban_mem += sizeof(BanList);
	}
	if (!ircops)
		malloc_strcat(&ircops, empty_string);
	if (do_hook(CHANNEL_STATS_LIST, "%s %s %s %ld %ld %ld %ld %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %s", 
		new->channel, my_ctime(new->channel_create.tv_sec), convert_time(time(NULL)-new->join_time.tv_sec),
		nick_mem+sizeof(ChannelList)+ban_mem, nick_mem, 
		sizeof(ChannelList),ban_mem, 
		new->stats_ops, new->stats_dops, new->stats_bans, new->stats_unbans,
		new->stats_ops, new->stats_dops, new->stats_bans, new->stats_unbans,
		new->stats_signoffs, new->stats_joins, new->totalbans, new->maxbans,
		new->stats_sops, new->stats_sdops,new->stats_sbans, new->stats_sunbans,
		usershere, usersaway, chanops, chanunop,new->totalnicks,new->maxnicks,
		numircops, ircops))
	{
put_it("%s", convert_output_format("$G %CInformation for channel %K: %W$0", "%s", new->channel)); 
put_it("%s", convert_output_format("$G %CChannel created %K: %W$0 $1 $2 $3%n in memory %W$4-", "%s %s", convert_time(time(NULL)-new->channel_create.tv_sec), my_ctime(new->join_time.tv_sec)));

put_it("%s", convert_output_format("     MEM usage%K:%w Total%K:%w %c$0 bytes   %K[%cNicks $1 b Chan $2 b Bans $3 b%K]", "%d %d %d %d", (int)(nick_mem+sizeof(ChannelList)+ban_mem), (int)nick_mem, (int)sizeof(ChannelList), (int)ban_mem));
put_it("%s", convert_output_format("Ops        %K[%W$[-5]0%K]%w  De-Ops     %K[%W$[-5]1%K]%w  Bans       %K[%W$[-5]2%K]%w  Unbans     %K[%W$[-5]3%K]%w", "%d %d %d %d", new->stats_ops, new->stats_dops, new->stats_bans, new->stats_unbans));
put_it("%s", convert_output_format("Topics     %K[%W$[-5]0%K]%w  Kicks      %K[%W$[-5]1%K]%w  Publics    %K[%W$[-5]2%K]%w  Parts      %K[%W$[-5]3%K]%w", "%d %d %d %d", new->stats_topics, new->stats_kicks, new->stats_pubs, new->stats_parts));
put_it("%s", convert_output_format("Signoffs   %K[%W$[-5]0%K]%w  Joins      %K[%W$[-5]1%K]%w  TotalBans  %K[%W$[-5]2%K]%w  MaxBans    %K[%W$[-5]3%K]%w", "%d %d %d %d", new->stats_signoffs, new->stats_joins, new->totalbans, new->maxbans));
put_it("%s", convert_output_format("ServOps    %K[%W$[-5]0%K]%w  ServDeop   %K[%W$[-5]1%K]%w  ServBans   %K[%W$[-5]2%K]%w  ServUB     %K[%W$[-5]3%K]%w", "%d %d %d %d", new->stats_sops, new->stats_sdops,new->stats_sbans, new->stats_sunbans));
put_it("%s", convert_output_format("Users Here %K[%W$[-5]0%K]%w  Users Away %K[%W$[-5]1%K]%w  Opped      %K[%W$[-5]2%K]%w  Unopped    %K[%W$[-5]3%K]%w", "%d %d %d %d", usershere, usersaway, chanops, chanunop));
put_it("%s", convert_output_format("TotalNicks %K[%W$[-5]0%K]%w  MaxNicks   %K[%W$[-5]1%K]%w", "%d %d", new->totalnicks,new->maxnicks));
put_it("%s", convert_output_format("IRCops     %K[%W$[3]0%K]%w$1-", "%d %s", numircops, ircops));

put_it("%s", convert_output_format("  %CThere is %R$0%C limit and limit checking is %R$1-", "%s %s", new->limit ? ltoa(new->limit): "no", new->tog_limit?"Enabled":"Disabled"));
put_it("%s", convert_output_format("  %CIdle user check is %K[%R$0-%K]", "%s", new->check_idle?"Enabled":"Disabled"));
/*put_it("%s", convert_output_format("$G End of channel stats for $0", "%s", new->channel));*/
			/* wtf is do_scan in the channel struct */
	}
	new_free(&ircops);

}

void update_stats(int what, char *channel, NickList *nick, ChannelList *chan, int splitter)
{
time_t this_time = time(NULL);
int t = 0;
	context;

	if (!chan || !chan->channel)
		return;

	switch (what)
	{
		case KICKLIST:
		{
			chan->stats_kicks++;
			chan->totalnicks--;
			if (nick) nick->stat_kicks++;
			break;
		}

		case LEAVELIST:
		{
			chan->stats_parts++;
			chan->totalnicks--;
			break;
		}
		case JOINLIST:
		{
			chan->stats_joins++;
			chan->totalnicks++;
			if (chan->totalnicks > chan->maxnicks)
			{
				chan->maxnicks = chan->totalnicks;
				chan->maxnickstime = this_time;
			}
			if (!splitter)
			{
				if (chan->chop && is_other_flood(chan, nick, JOIN_FLOOD, &t))
				{
					if (get_cset_int_var(chan->csets, JOINFLOOD_CSET) && get_cset_int_var(chan->csets, KICK_ON_JOINFLOOD_CSET) && !nick->kickcount++)
					{
						send_to_server("MODE %s -o+b %s *!*%s", chan->channel, nick->nick, nick->host); 
						send_to_server("KICK %s %s :\002Join flood\002 (%d joins in %dsecs of %dsecs)", chan->channel, nick->nick, get_cset_int_var(chan->csets, KICK_ON_JOINFLOOD_CSET)/*chan->set_kick_on_joinflood*/, t, get_cset_int_var(chan->csets, JOINFLOOD_TIME_CSET) /*chan->set_joinflood_time*/);
						if (get_int_var(AUTO_UNBAN_VAR))
							add_timer("", get_int_var(AUTO_UNBAN_VAR), 1, timer_unban, m_sprintf("%d %s *!*%s", from_server, chan->channel, nick->host), NULL);
					}
				} 
			}
			break;
		}
		case CHANNELSIGNOFFLIST:
		{
			chan->stats_signoffs++;
			chan->totalnicks--;
			break;
		}
		case PUBLICLIST:
		case PUBLICOTHERLIST:
		case PUBLICNOTICELIST:
		case NOTICELIST:
		{
			chan->stats_pubs++;
			if (nick)
			{	
				nick->stat_pub++;
				nick->idle_time = this_time;
			}
			break;
		}
		case TOPICLIST:
		{
			chan->stats_topics++;
			break; 
		}
		case MODEOPLIST:
			if (splitter)
				chan->stats_sops++;
			else
			{
				if (nick) nick->stat_ops++;		
				chan->stats_ops++;
			}
			break;
		case MODEDEOPLIST:
			if (splitter)
				chan->stats_sdops++;
			else
			{
				chan->stats_dops++;		
				if (nick) nick->stat_dops++;
			}

			if (chan->chop && is_other_flood(chan, nick, DEOP_FLOOD, &t))
			{
				if (get_cset_int_var(chan->csets, DEOP_ON_DEOPFLOOD_CSET) < get_cset_int_var(chan->csets, KICK_ON_DEOPFLOOD_CSET))
					send_to_server("MODE %s -o %s", chan->channel, nick->nick);
				else if (!nick->kickcount++)
					send_to_server("KICK %s %s :\002De-op flood\002 (%d de-ops in %dsecs of %dsecs)", chan->channel, nick->nick, get_cset_int_var(chan->csets, KICK_ON_DEOPFLOOD_CSET)/*chan->set_kick_on_deopflood*/, t, get_cset_int_var(chan->csets, DEOPFLOOD_TIME_CSET)); 
			} 
			break;
		case MODEBANLIST:
			if (splitter)
				chan->stats_sbans++;
			else
			{
				if (nick) nick->stat_bans++;		
				chan->stats_bans++;
			}
			chan->totalbans++;
			if (chan->stats_bans > chan->maxbans)
			{
				chan->maxbans = chan->stats_bans;
				chan->maxbanstime = this_time;
			}
			break;
		case MODEUNBANLIST:
			if (splitter)
				chan->stats_sunbans++;
			else
			{
				if (nick) nick->stat_unbans++;
				chan->stats_unbans++;
			}
			if (chan->totalbans) chan->totalbans--;
			break;
		default:
			bitchsay("Illegal what %d passed to update_stats", what);
			break;
	}
}

BUILT_IN_COMMAND(usage)
{
#if defined(HAVE_GETRUSAGE)
struct rusage r_usage;

	context;
	if ((0 == getrusage(RUSAGE_SELF, &r_usage)))
	{
		/* struct timeval ru_utime; user time used 
                 * struct timeval ru_stime;  system time used 
		 */                
		int secs = r_usage.ru_utime.tv_sec + r_usage.ru_stime.tv_sec;
		if (secs == 0)
			secs =1;
		
#ifdef ONLY_STD_CHARS
		put_it("%s", convert_output_format("%G--%WBitchX%G-%WUsage%G-%WStatistics%G------------------------------------", NULL));
		put_it("%s",convert_output_format("%G| %CCPU %cUsage:  Secs %W$[-2]0%n:%W$[-2]1%n     %K[%CU%cser %W$[-2]2%n:%W$[-2]3   %CS%cystem %W$[-2]4%n:%W$[-2]5%K]","%d %d %d %d %d %d", secs/60,secs%60,r_usage.ru_utime.tv_sec/60, r_usage.ru_utime.tv_sec%60,r_usage.ru_stime.tv_sec/60, r_usage.ru_stime.tv_sec%60));
		put_it("%s",convert_output_format("%G| %CMEM %cUsage:  MaXRSS %W$0   %cShMem %W$1  %cData %W$2  %cStack %W$3","%l %l %l %l", r_usage.ru_maxrss, r_usage.ru_ixrss, r_usage.ru_idrss,r_usage.ru_isrss));
		put_it("%s",convert_output_format("%G| %CSwaps %W$[-8]0   %CReclaims %W$[-8]1   %CFaults %W$[-8]2","%l %l %l", r_usage.ru_nswap, r_usage.ru_minflt, r_usage.ru_majflt));
		put_it("%s",convert_output_format("%G| %CBlock %K[%cin  %W$[-8]0  %cout %W$[-8]1%K]","%l %l", r_usage.ru_inblock, r_usage.ru_oublock));
		put_it("%s",convert_output_format("%G| %CMsg   %K[%cRcv %W$[-8]0 %cSend %W$[-8]1%K]","%l %l", r_usage.ru_msgrcv, r_usage.ru_msgsnd));
		put_it("%s",convert_output_format("%G| %CSignals %W$[-8]0   %CContext %cVol. %W$[-8]1   %cInvol %W$[-8]2","%l %l %l", r_usage.ru_nsignals, r_usage.ru_nvcsw, r_usage.ru_nivcsw));

#else
		put_it("%s", convert_output_format("%GÚÄ%WBitchX%GÄ%WUsage%GÄ%WStatistics%GÄÄÄÄ%g---%GÄ%g--%GÄÄ%g-%GÄÄÄÄÄÄ%g---%KÄ%g--%KÄÄ%g-%KÄÄÄÄÄÄÄÄ", NULL));
		put_it("%s",convert_output_format("%G| %CCPU %cUsage:  Secs %W$[-2]0%n:%W$[-2]1%n     %K[%CU%cser %W$[-2]2%n:%W$[-2]3   %CS%cystem %W$[-2]4%n:%W$[-2]5%K]","%d %d %d %d %d %d", secs/60,secs%60,r_usage.ru_utime.tv_sec/60, r_usage.ru_utime.tv_sec%60,r_usage.ru_stime.tv_sec/60, r_usage.ru_stime.tv_sec%60));
		put_it("%s",convert_output_format("%g³ %CMEM %cUsage:  MaXRSS %W$0   %cShMem %W$1  %cData %W$2  %cStack %W$3","%l %l %l %l", r_usage.ru_maxrss, r_usage.ru_ixrss, r_usage.ru_idrss,r_usage.ru_isrss));
		put_it("%s",convert_output_format("%g| %CSwaps %W$[-8]0   %CReclaims %W$[-8]1   %CFaults %W$[-8]2","%l %l %l", r_usage.ru_nswap, r_usage.ru_minflt, r_usage.ru_majflt));
		put_it("%s",convert_output_format("%K³ %CBlock %K[%cin  %W$[-8]0  %cout %W$[-8]1%K]","%l %l", r_usage.ru_inblock, r_usage.ru_oublock));
		put_it("%s",convert_output_format("%K: %CMsg   %K[%cRcv %W$[-8]0 %cSend %W$[-8]1%K]","%l %l", r_usage.ru_msgrcv, r_usage.ru_msgsnd));
		put_it("%s",convert_output_format("%K. %CSignals %W$[-8]0   %CContext %cVol. %W$[-8]1   %cInvol %W$[-8]2","%l %l %l", r_usage.ru_nsignals, r_usage.ru_nvcsw, r_usage.ru_nivcsw));
#endif
	}
#else
	bitchsay("Lack of getrusage(). This function needed to be disabled on your client");
#endif
}

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

#if 0
static int is_reason(char *string)
{
	return (*string == '');
}

#include "dich_conf.h"

aConfItem *host_match = NULL;

char *user_hostserv = NULL;
int stats_k_found = 0;

aConfList       KList1 = { 0, NULL };   /* ordered */
aConfList       KList2 = { 0, NULL };   /* ordered, reversed */
aConfList       KList3 = { 0, NULL };   /* what we can't sort */

void stat_k_userhost(WhoisStuff *stuff, char *nick, char *args)
{
	context;
	if (!stuff || !stuff->nick || !nick || !strcmp(stuff->user, "<UNKNOWN>") || my_stricmp(stuff->nick, nick))
	{
		say("No match for user %s", nick);
		new_free(&user_hostserv);
		new_free(&host_match->host);
		new_free(&host_match->nick);
		new_free(&host_match->passwd);
		new_free(&host_match->name);
		new_free((char **)&host_match);
		return;
	}
	host_match->host = m_strdup(cluster(stuff->host));
	host_match->nick = m_strdup(stuff->nick);
	host_match->name = m_strdup(clear_server_flags(stuff->user));
	send_to_server("STATS K%s%s", user_hostserv?" ":"", user_hostserv?user_hostserv : "");
	new_free(&user_hostserv);
}

BUILT_IN_COMMAND(statkgrep)
{
char *person;

	context;
	if (args && *args)
	{
		person = next_arg(args, &args);
		if (person && *person)
		{
			stats_k_found = 0;
			host_match = make_conf();
			
			if (strchr(person, '!') && strchr(person, '@'))
			{
				char *user, *host, *nick;
				nick = person;
				user = strchr(person, '!');
				*user++ = 0;
				host = strchr(user, '@');
				*host++ = 0;
				host_match->nick = m_strdup(nick);
				host_match->name = m_strdup(user);
				host_match->host = m_strdup(host);
			}
			else if (strchr(person, '@'))
			{
				char *user, *host;
				user = person;
				host = strchr(person, '@');
				*host++ = 0;
				host_match->nick = m_strdup("*");
				host_match->name = m_strdup(user);
				host_match->host = m_strdup(host);
			}
			else if (strchr(person, '!'))
			{
				char *user, *nick;
				nick = person;
				user = strchr(person, '!');
				*user++ = 0;
				host_match->nick = m_strdup(nick);
				host_match->name = m_strdup(user);
				host_match->host = m_strdup("*");
			}
			else if (strchr(person, '.'))
			{
				host_match->nick = m_strdup("*");
				host_match->name = m_strdup("*");
				host_match->host = m_strdup(person);
			}
			else
			{
				if (args && *args)
					malloc_strcpy(&user_hostserv, args);
				add_to_userhost_queue(person, stat_k_userhost, person);
				return;
			}
			send_to_server("STATS K %s", args && *args ? args : "");
		}		
	}
	else
		userage(command, helparg);
}

int stats_k_grep(char **args)
{
/*
 * arg[1] has the banned host
 * arg[3] has the username.
 * arg[4+] has the reason.
 */
aConfItem *aconf;
char *host;
	/* if host_match is null just return, as we are not doing a grep */
	context;

	if (!args || !args[1] || !args[3] || !host_match)
		return 0;

	aconf = make_conf();
	aconf->host = m_strdup(args[1]);
	aconf->nick = m_strdup(args[2]);
	aconf->name = m_strdup(args[3]);
	PasteArgs(args, 4);
	aconf->passwd = m_strdup(args[4]);
	host = host_field(aconf);
	switch(sortable(host))
	{
		case 0:
			l_addto_conf_list(&KList3, aconf, host_field);
			break;
		case 1:
			addto_conf_list(&KList1, aconf, host_field);
			break;
		case -1:
			addto_conf_list(&KList2, aconf, rev_host_field);
			break;
	}
	new_free(&host);
	stats_k_found++;
	return 1;
}

void stats_k_grep_end(void)
{
int match_k = 0;
char *rev;
aConfList *list;
aConfItem *tmp;
	context;
	if (stats_k_found && host_match)
	{
		rev = (char *) new_malloc(strlen(host_match->host)+1);
		reverse(rev, host_match->host);

		/* Start with hostnames of the form "*word" (most frequent) -Sol */
		list = &KList2;
		while ((tmp = find_matching_conf(list, rev)) != NULL)
		{
			if (tmp->name && (!host_match->name || match(tmp->name, host_match->name)))
			{
				match_k++;
				bitchsay("Found matching %s@%s kline [%s]", tmp->name, tmp->host, tmp->passwd?is_reason(tmp->passwd)?tmp->passwd:null:null);
			}
			list = NULL;
		}

		/* Try hostnames of the form "word*" -Sol */
		list = &KList1;
		while ((tmp = find_matching_conf(list, host_match->host)) != NULL)
		{
			if (tmp->name && (!host_match->name || match(tmp->name, host_match->name)))
			{
				match_k++;
				bitchsay("Found matching %s@%s kline [%s]", tmp->name, tmp->host, tmp->passwd?is_reason(tmp->passwd)?tmp->passwd:null:null);
			}
			list = NULL;
		}

		/* If none of the above worked, try non-sorted entries -Sol */
		list = &KList3;
		while ((tmp = l_find_matching_conf(list, host_match->host)) != NULL)
		{
			if (tmp->host && tmp->name && (!host_match->name || match(tmp->name, host_match->name)))
			{
				match_k++;
				bitchsay("Found matching %s@%s kline [%s]", tmp->name, tmp->host, tmp->passwd?is_reason(tmp->passwd)?tmp->passwd:null:null);
			}
			list = NULL;
		}

		new_free(&rev);
		if (!match_k)
			bitchsay("Found %d klines. No K-Line matching [%s!%s@%s].", stats_k_found, host_match->nick, host_match->name, host_match->host);
		else
			bitchsay("Found %d klines and %d matching %s@%s kline%s", stats_k_found, match_k, host_match->name, host_match->host, plural(stats_k_found));
		new_free(&host_match->host);
		new_free(&host_match->nick);
		new_free(&host_match->passwd);
		new_free(&host_match->name);
		new_free((char **)&host_match);
	}
	new_free(&user_hostserv);
	clear_conf_list(&KList1);
	clear_conf_list(&KList2);
	clear_conf_list(&KList3);
	return;
}
#endif

/*  
 * (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 					*/
struct {
	char *fg, *bg;
} codes[16] = {

	{ "[1;37m",   "[47m"        },      /* white                */
	{ "[0;30m",   "[40m"        },      /* black (grey for us)  */
	{ "[0;34m",   "[44m"        },      /* blue                 */
	{ "[0;32m",   "[42m"        },      /* green                */
	{ "[0;31m",   "[41m"        },      /* red                  */
	{ "[0;33m",   "[43m"        },      /* brown                */

	{ "[0;35m",   "[45m"        },      /* magenta              */
	{ "[1;31m",   "[41m"        },      /* bright red           */
	{ "[1;33m",   "[43m"        },      /* yellow               */

	{ "[1;32m",   "[42m"            },      /* bright green         */
	{ "[0;36m",   "[46m"            },      /* cyan                 */
	{ "[1;36m",   "[46m"            },      /* bright cyan          */
	{ "[1;34m",   "[44m"            },      /* bright blue          */
	{ "[1;35m",   "[45m"            },      /* bright magenta       */
	{ "[1;30m",   "[40m"            },      /* dark grey            */
	{ "[0;37m",   "[47m"            }       /* grey                 */
};
	register char *sptr = line, *dptr = newline1;
	short code;
	
	if (!*line)
		return empty_string;
	*newline1 = 0;
	while (*sptr) {
		if (*sptr == '' && 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 == '')
		{
			strcpy(dptr, "[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;
	context;
	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;
	context;
	for (t = *tmp; t; )
	{
		q = t->next;
		new_free(&t->nick);
		new_free(&t->type);
		new_free((char **)&t);
		t = q;
	}
	*tmp = NULL;
}

BUILT_IN_COMMAND(clear_tab)
{
NickTab **tmp = &tabkey_array;
	if (command && *command && !my_stricmp(command, "CLEARAUTO"))
		tmp = &autoreply_array;		
	clear_array(tmp);
}

void userage(char *command, char *use)
{

	context;
	if (do_hook(USAGE_LIST, "%s %s", command, use ? use : "No Help Available for this command"))
		put_it("%s", convert_output_format(fget_string_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];


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

void do_clones (fd_set *rd, fd_set *wr)
{
CloneList *new = NULL;

	context;
	for (new = clones; new; new = new->next)
	{
		if (!new->warn && FD_ISSET(new->socket_num, rd))
		{
			int old_timeout = dgets_timeout(1);
			char buffer[IRCD_BUFFER_SIZE + 1];
			char *str = buffer;
			switch(dgets(str, IRCD_BUFFER_SIZE-2, new->socket_num, NULL))
			{
				case -1:
					break;
				case 0:
					break;
				default:
				if ((buffer[strlen(buffer)-1] == '\r') || (buffer[strlen(buffer)-1] == '\n'))
					buffer[strlen(buffer)-1] = 0;
				if ((buffer[strlen(buffer)-1] == '\r') || (buffer[strlen(buffer)-1] == '\n'))
					buffer[strlen(buffer)-1] = 0;
				do_hook(CLONE_READ_LIST, "%d %d %s %s", new->socket_num, new->port, new->server, buffer);
			}
			dgets_timeout(old_timeout);
		}
	}
}

void auto_away(unsigned long value)
{
	context;
	if (get_int_var(AUTO_AWAY_VAR) && !away_set)
	{
		char *msg = NULL;
		if (awaymsg)
			malloc_sprintf(&msg, "%s: [%d mins]", convert_output_format(awaymsg, NULL), get_int_var(AUTO_AWAY_TIME_VAR)/60);
		else
			malloc_sprintf(&msg, "Auto-Away after %d mins", get_int_var(AUTO_AWAY_TIME_VAR)/60);
		away(NULL, msg, NULL, NULL);
		new_free(&msg);		
	}
}

char *logfile[] = { "tcl.log", "msg.log", NULL };

/* putlog(level,channel_name,format,...);  */
void putlog(int type, ...)
{
va_list va; 
time_t	t;
char	*format,
	*chname,
	*logfilen = NULL, 
	s[BIG_BUFFER_SIZE+1],
	s1[40],
	s2[BIG_BUFFER_SIZE+1];
FILE	*f; 
	if (!get_int_var(BOT_LOG_VAR))
		return;
	if (!(logfilen = get_string_var(BOT_LOGFILE_VAR)))
		return;
			
	va_start(va, type); 
	t = time(NULL);
	strftime(s1, 30, "%I:%M%p", localtime(&t));
	chname=va_arg(va,char *);
	format=va_arg(va,char *);
	vsprintf(s,format,va);
	
	if (!*s) 
		strcpy(s2,empty_string);
	else 
		sprintf(s2,"[%s] %s",s1,s); 

	if (chname && *chname =='*')
	{
		if ((f=fopen(logfilen, "a+")) != NULL)
		{
			fprintf(f,"%s\n",s2); 
			fclose(f);
		}
	}
}

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

	context;
	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((char **) &temp);
		temp = hold;
	}
	*serv1 = NULL;
}

irc_server *add_server(irc_server **serv1, char *channel, char *arg, int hops, char *time)
{
irc_server *serv2;
	serv2 = (irc_server *) 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((List **)&split_link, server)))
	{
		new_free(&temp->name);
		new_free(&temp->link);
		new_free(&temp->time);
		new_free((char **) &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(fget_string_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(fget_string_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(fget_string_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);
}

/*
 * find split servers we hope 
 */
BUILT_IN_COMMAND(linklook)
{
struct server_split *serv = server_last;
int count;

	if (!serv)
	{
		bitchsay("No active splits");
		return;
	}
	
	count = 0;
	while (serv)
	{
		if (serv->status & SPLIT)
		{
			if (!count)
				put_it("%s", convert_output_format(fget_string_var(FORMAT_NETSPLIT_HEADER_FSET), "%s %s %s %s", "time","server","uplink","hops"));
			if (do_hook(LLOOK_SPLIT_LIST, "%s %s %d", serv->name, serv->link, serv->hopcount))
				put_it("%s", convert_output_format(fget_string_var(FORMAT_NETSPLIT_FSET), "%s %s %s %d", serv->time, serv->name, serv->link, serv->hopcount));
			count++;
		}
		serv = serv->next;
	}
	if (count)
		bitchsay("There %s %d split servers", (count == 1) ? "is": "are", count);
	else
		bitchsay("No split servers found");
}

enum REDIR_TYPES { PRIVMSG = 0, KICK, TOPIC, WALL, WALLOP, NOTICE, KBOOT, KILL, DCC, LIST};
void userhost_ban(WhoisStuff *stuff, char *nick1, char *args);

int redirect_msg(char *to, enum REDIR_TYPES what, char *str, int showansi)
{
char *new_str;
	if (showansi)
		new_str = str;
	else
		new_str = stripansicodes(str);
	switch(what)
	{
		case PRIVMSG:
			if (is_channel(to))
				put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_PUBLIC_FSET), "%s %s %s %s", update_clock(GET_TIME), "*", to, str));
			else if ((*to == '=') && dcc_active(to+1))
				;
			else
				put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_MSG_FSET), "%s %s %s %s", update_clock(GET_TIME), to, "*", str));
			if ((*to == '=') && dcc_active(to+1))
				dcc_chat_transmit(to+1, new_str, str, "PRIVMSG");
			else
				send_to_server("PRIVMSG %s :%s", to, new_str);
			break;
		case KILL:
			send_to_server("KILL %s :%s", to, new_str);
			break;
		case KBOOT:
			add_to_userhost_queue(to, userhost_ban, "%s %s %s", get_current_channel_by_refnum(0), to, "");
		case KICK:
			send_to_server("KICK %s %s :%s", get_current_channel_by_refnum(0), to, new_str);
			break;
		case TOPIC:
			send_to_server("TOPIC %s :%s", to, new_str);
			break;
		case WALL:
		{
			ChanWallOp(NULL, new_str, NULL, NULL);
			break;
		}
		case WALLOP:
			put_it("!! %s", str); 
			send_to_server("WALLOPS :%s", new_str);
			break;
		case NOTICE:
			put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_NOTICE_FSET), "%s %s %s %s", update_clock(GET_TIME), to, "*", str));
			send_to_server("NOTICE %s :%s", to, new_str);
			break;
		case LIST:
		default:
			break;
	}
	return 1;
}

BUILT_IN_COMMAND(do_dirlasttype)
{
	char *channel = NULL; int count = -1;
	LastMsg *t = NULL;
	char *form = NULL;
	char *sform;
	int numargs = 5;
	int size = 1;
	int len = strlen(command);
	int showansi = 0;
	enum REDIR_TYPES what = PRIVMSG;

	if (!my_strnicmp(command, "RELCR", 5))
	{
		t = &last_ctcp[0];
		form = fget_string_var(FORMAT_CTCP_REPLY_FSET);
		sform = "%s %s %s %s %s";
		if (len == 6 && command[len-1] == 'T')
			what = TOPIC;
	}
	else if (!my_strnicmp(command, "RELC", 4))
	{
		t = &last_sent_ctcp[0];
		form = fget_string_var(FORMAT_SEND_CTCP_FSET);
		sform = "%s %s %s %s %s";
		if (len > 4 && command[len-1] == 'T')
			what = TOPIC;
	}
	else if (!my_strnicmp(command, "RELD", 4))
	{
		t = &last_dcc[0];
		form = fget_string_var(FORMAT_DCC_CHAT_FSET);
		sform = "%s %s %s %s";
		if (len > 4 && command[len-1] == 'T')
			what = TOPIC;
		numargs = 4;
	}
	else if (!my_strnicmp(command, "RELI", 4))
	{
		t = &last_invite_channel[0];
		form = fget_string_var(FORMAT_INVITE_FSET);
		numargs = 4;
		sform = "%s %s %s";
		if (len > 4 && command[len-1] == 'T')
			what = TOPIC;
	}
	else if (!my_strnicmp(command, "RELM", 4))
	{
		/* ??? */
		t = &last_msg[0]; size = MAX_LAST_MSG;
		form = fget_string_var(FORMAT_RELM_FSET);
		sform = "%s %s %s %s %s";
		if (len > 4 && command[len-1] == 'T')
			what = TOPIC;
	}
	else if (!my_strnicmp(command, "RELN", 4))
	{
		/* ??? */
		t = &last_notice[0]; size = MAX_LAST_MSG;
		form = fget_string_var(FORMAT_RELN_FSET);
		sform = "%s %s %s %s %s";
		if (len > 4 && command[len-1] == 'T')
			what = TOPIC;
	}
	else if (!my_strnicmp(command, "RELSM", 5))
	{
		/* ??? */
		t = &last_sent_msg[0]; size = MAX_LAST_MSG;
		form = fget_string_var(FORMAT_RELSM_FSET);
		sform = "%s %s %s %s %s";
		if (len > 5 && command[len-1] == 'T')
			what = TOPIC;
	}
	else if (!my_strnicmp(command, "RELSN", 5))
	{
		/* ??? */
		t = &last_sent_notice[0]; size = MAX_LAST_MSG;
		form = fget_string_var(FORMAT_SEND_NOTICE_FSET);
		sform = "%s %s %s %s";
		numargs = 4;
		if (len > 5 && command[len-1] == 'T')
			what = TOPIC;
	}
	else if (!my_strnicmp(command, "RELST", 5))
	{
		/* ??? */
		t = &last_sent_topic[0];
		form = fget_string_var(FORMAT_TOPIC_FSET);
		sform = "%s %s %s";
		numargs = 2;
		if (len > 5 && command[len-1] == 'T')
			what = TOPIC;
	}
	else if (!my_strnicmp(command, "RELSW", 5))
	{
		/* ??? */
		t = &last_sent_wall[0];
		form = fget_string_var(FORMAT_WALLOP_FSET);
		sform = "%s %s %s %s %s";
		if (len > 5 && command[len-1] == 'T')
			what = TOPIC;
	}
	else if (!my_strnicmp(command, "RELS", 4))
	{
		t = &last_servermsg[0]; size = MAX_LAST_MSG;
		form = fget_string_var(FORMAT_RELS_FSET);
		sform = "%s %s %s %s";
		numargs = 4;
		if (len > 4 && command[len-1] == 'T')
			what = TOPIC;
	}
	else if (!my_strnicmp(command, "RELT", 4))
	{
		/* ??? */
		t = &last_topic[0];
		form = fget_string_var(FORMAT_TOPIC_FSET);
		sform = "%s %s %s";
		numargs = 2;
		if (len > 4 && command[len-1] == 'T')
			what = TOPIC;
	}
	else if (!my_strnicmp(command, "RELW", 4))
	{
		/* ??? */
		t = &last_wall[0]; size = MAX_LAST_MSG;
		form = fget_string_var(FORMAT_WALLOP_FSET);
		sform = "%s %s %s %s";
		numargs = 4;
		if (len > 4 && command[len-1] == 'T')
			what = TOPIC;
	} 
	else
	{
		what = LIST; size = MAX_LAST_MSG;
		t = &last_msg[0];
		for (count = 0; count < size; count++)
		{
			if (!t[count].last_msg)
			{
				bitchsay("No such msg #%d for /%s received", count, command);
				break;
			} else
				put_it("%2d %s", count, convert_output_format(fget_string_var(FORMAT_RELN_FSET), "%s %s %s %s %s", t[count].time, t[count].from, t[count].uh, t[count].to, t[count].last_msg));
		}
		return;
	}

	while (args && *args)
	{
		char *comm;
		if (!(comm = next_arg(args, &args)))
			break;
		if (!my_strnicmp(comm, "-list", strlen(comm)))
		{
			for (count = 0; count < size; count++)
			{
				if (!count && !t[count].last_msg)
				{
					bitchsay("No such msg #%d for /%s received", count, command);
					break;
				} else if (!t[count].last_msg)
					break;
				switch(numargs)
				{
					case 2:
						put_it("%2d %s", count, convert_output_format(form, sform, t[count].time, t[count].to, t[count].last_msg));
						break;
					case 3:
						put_it("%2d %s", count, convert_output_format(form, sform, t[count].time, t[count].from, t[count].last_msg));
						break;
					case 4:
						put_it("%2d %s", count, convert_output_format(form, sform, t[count].time, t[count].from, t[count].to, t[count].last_msg));
						break;
					case 5:
						put_it("%2d %s", count, convert_output_format(form, sform, t[count].time, t[count].from, t[count].uh, t[count].to, t[count].last_msg));
				}
			}
			return;
		}
		else if (!my_strnicmp(comm, "-kick", strlen(comm)))
			what = KICK;
		else if (!my_strnicmp(comm, "-wall", strlen(comm)))
			what = WALL;
		else if (!my_strnicmp(comm, "-wallop", strlen(comm)))
			what = WALLOP;
		else if (!my_strnicmp(comm, "-msg", strlen(comm)))
			what = PRIVMSG;
		else if (!my_strnicmp(comm, "-notice", strlen(comm)))
			what = NOTICE;
		else if (!my_strnicmp(comm, "-topic", strlen(comm)))
			what = TOPIC;
		else if (!my_strnicmp(comm, "-kboot", strlen(comm)))
			what = KBOOT;
		else if (!my_strnicmp(comm, "-kill", strlen(comm)))
			what = KILL;
		else if (!my_strnicmp(comm, "-ansi", strlen(comm)))
			showansi++;
		else if (!my_strnicmp(comm, "-help", strlen(comm)))
		{
			userage(command, helparg/*"<-list|-kick|-wall|-wallop|-msg|-notice|-topic|-kboot|-ansi|-kill|-help> <#|channel|nick> <channel|nick>"*/);
			return;
		}
		else 
		{
			if (is_number(comm))
			{
				count = my_atol(comm);
				if (count < 0)
					count *= -1;
			}
			else
				channel = comm;
		}
		if (count > size)
			count = size;
	}
	if (count == -1)
		count = 0;
	if (!channel)
		channel = get_current_channel_by_refnum(0);
	if (channel || what == WALLOP)
	{
		char *p = NULL;
		if (!t[count].last_msg)
		{
			bitchsay("No such msg #%d for /%s received", count, command);
			return;
		}
		switch(numargs)
		{
			case 2:
				malloc_strcpy(&p, convert_output_format(form, sform, t[count].time, t[count].to, t[count].last_msg));
				break;
			case 3:
				malloc_strcpy(&p, convert_output_format(form, sform, t[count].time, t[count].from, t[count].last_msg));
				break;
			case 4:
				malloc_strcpy(&p, convert_output_format(form, sform, t[count].time, t[count].from, t[count].to, t[count].last_msg));
				break;
			case 5:
				malloc_strcpy(&p, convert_output_format(form, sform, t[count].time, t[count].from, t[count].uh, t[count].to, t[count].last_msg));
		}
		redirect_msg(channel, what, p, showansi);
		new_free(&p);
	}
	else
		bitchsay("Can not %s what you requested", command);
}

/*
 * Wallop   Sends NOTICE to all ops of Current Channel!       
 */
BUILT_IN_COMMAND(ChanWallOp)
{
	char *channel = NULL;
	char *chops = NULL;
	char *include = NULL;
	char *exclude = NULL;
	ChannelList *chan;
	NickList *tmp;
	char	buffer[BIG_BUFFER_SIZE + 1];
	
	if (!args || (args && !*args))
	{
		userage("WALLMSG", helparg);
		return;
	}
	if (get_current_channel_by_refnum(0))
	{
		int count = 0;
		int i = 0;
		char *nick = NULL;
		malloc_strcpy(&channel, get_current_channel_by_refnum(0));
		chan = lookup_channel(channel, curr_scr_win->server, 0);
		while (args && (*args == '-' || *args == '+'))
		{
			nick = next_arg(args, &args);
			if (*nick == '-')
			{
				malloc_strcat(&exclude, nick+1);
				malloc_strcat(&exclude, " ");
			} 
			else 
			{
				malloc_strcat(&include, nick+1);
				malloc_strcat(&include, " ");
			}
		}
		if (!args || !*args)
		{
			bitchsay("NO Wallmsg included");
			new_free(&exclude);
			new_free(&include);
			new_free(&channel);
		}
		message_from(channel, LOG_NOTICE);
		sprintf(buffer, "[\002BX-Wall\002/\002%s\002] %s", channel, args);
		for (tmp = next_nicklist(chan, NULL); tmp; tmp = next_nicklist(chan, tmp))
		{
			if (!my_stricmp(tmp->nick, get_server_nickname(from_server)))
				continue;
			if (exclude && stristr(exclude, tmp->nick))
				continue;
			if (tmp->chanop == 1 || (include && stristr(include, tmp->nick)))
			{
				if (chops)
					malloc_strcat(&chops, ",");
				malloc_strcat(&chops, tmp->nick);
				count++;
			}
			if (count >= 8 && chops)
			{
				send_to_server("%s %s :%s", "NOTICE", chops, buffer);
				i+=count;
				count = 0;
				new_free(&chops);
			}
		}
		i+=count;
		if (chops)
			send_to_server("%s %s :%s", "NOTICE", chops, buffer);
		if (i) 
		{
			put_it("%s", convert_output_format(fget_string_var(FORMAT_BWALL_FSET), "%s %s %s %s %s", update_clock(GET_TIME), channel, "*", "*", args));
			add_last_type(&last_sent_wall[0], 1, NULL, NULL, channel, buffer);
			if (exclude)
				bitchsay("Excluded <%s> from wallmsg", exclude);
			if (include)
				bitchsay("Included <%s> in wallmsg", include);
		}
		else
			put_it("%s", convert_output_format("$G No ops on $0", "%s", channel));
		message_from(NULL, LOG_CRAP);
	}
	else
		say("No Current Channel for this Window.");
	new_free(&include);
	new_free(&channel);
	new_free(&chops);
	new_free(&exclude);
}

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

	if (((logfile = get_string_var(MSGLOGFILE_VAR)) == NULL) || !get_string_var(CTOOLZ_DIR_VAR))
	{
		bitchsay("You must set the MSGLOGFILE and CTOOLZ_DIR variables 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;
}

char *randreason(char *filename)
{
	int count, min, i;
	FILE *bleah;
	static char buffer[BIG_BUFFER_SIZE/2 + 1];

	min = 1;
	count = 0;

	buffer[0] = '\0';

	if (!filename || !(bleah = fopen(filename, "r")))
		return NULL;

	while (!feof(bleah))
		if (freadln(bleah, buffer))
			count++;
	fseek(bleah, 0, 0);
	i = getrandom(1, count);
	count = 0;

	while (!feof(bleah) && (count < i))
		if (freadln(bleah, buffer))
			count++;
	fclose(bleah);

	if (*buffer)
		return buffer;
	return NULL;
}

char *get_reason(char *nick, char *file)
{
	char *temp, *p;
	char *filename = NULL;
static char reason[BIG_BUFFER_SIZE/2 + 1];
	*reason = '\0';	
	if (file && *file)
		malloc_sprintf(&filename, "%s", file);
	else
#ifdef WINNT
		malloc_sprintf(&filename, "%s/%s.%s", get_string_var(CTOOLZ_DIR_VAR), version, "kck");
#else
		malloc_sprintf(&filename, "%s/%s.%s", get_string_var(CTOOLZ_DIR_VAR), version, "reasons");
#endif
	p = expand_twiddle(filename);
	temp = randreason(p);
	new_free(&filename);
	if ((!temp || !*temp) && get_string_var(DEFAULT_REASON_VAR))
		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);
	return reason;
}

char *get_signoffreason(char *nick)
{
	char *temp, *p;
	char *filename = NULL;
static char reason[BIG_BUFFER_SIZE/2 + 1];

	*reason = '\0';	

#ifdef WINNT
	malloc_sprintf(&filename, "%s/%s.%s", get_string_var(CTOOLZ_DIR_VAR), version, "qt");
#else
	malloc_sprintf(&filename, "%s/%s.%s", get_string_var(CTOOLZ_DIR_VAR), version, "quit");
#endif
	p = expand_twiddle(filename);
	temp = randreason(p);
	new_free(&filename);

	if (!temp || !*temp)
		temp = "$0 has no reason";

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

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));
}

char *rights(string, num)
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;
}

void set_socket_read (fd_set *rd, fd_set *wr)
{
register int i;
	for (i = 0; i < FD_SETSIZE; i++)
	{
		if (sockets[i].is_read)
			FD_SET(i, rd);
		if (sockets[i].is_write)
			FD_SET(i, wr);
	}
}

void scan_sockets(fd_set *rd, fd_set *wr)
{
register int i;
	for (i = 0; i < FD_SETSIZE; i++)
	{
		if (sockets[i].is_read && FD_ISSET(i, rd))
			(sockets[i].func) (i);
		if (sockets[i].is_write && FD_ISSET(i, wr))
			(sockets[i].func) (i);
	}
}

extern int dgets_errno;

void read_netfinger(int s)
{
char tmpstr[BIG_BUFFER_SIZE+1];
register unsigned char *p = tmpstr;
	*tmpstr = 0;
	dgets_timeout(1);
	switch(dgets(tmpstr, BIG_BUFFER_SIZE, s, NULL))
	{
		case -1:
			break;
		case 0:
			FD_CLR(s, &readables);
			sockets[s].func = NULL;
			sockets[s].is_read = 0;
			close(s);
			if (dgets_errno == -1)
				bitchsay("Remote closed connection");
			else
				bitchsay("%s", strerror(dgets_errno));
			break;
		default:
		{
			chop(tmpstr, 1);
			while (*p)
			{
				switch(*p)
				{
					case 0210:
					case 0211:
					case 0212:
					case 0214:
						*p -= 0200;
						break;
					case '\n':
					case '\r':
						*p = '\0';
						break;
					default:
						if (!isprint(*p))
							*p = (*p & 0x7f) | 0x40;
						break;
				}
				p++;
			}
			put_it("%s", tmpstr);
		}
	}
	dgets_timeout(0);
}

void netfinger _((char *name))
{
	char *host = NULL;
	unsigned short port = 79;
	int s;
	
	if (name)
	{
		if ((host = strrchr(name, '@')))
			*host++ = 0;
		else
			host = name;
	}
	if (!host || !*host)
	{
		say("Invalid @host or user@host.");
		return;
	}
	
	if ((s = connect_by_number(host, &port, SERVICE_CLIENT, PROTOCOL_TCP, 0)) < 0)
	{
		bitchsay("Finger connect error on %s@%s", name?name:empty_string, host);
		return;
	}
	if (*name)
		write(s, name, strlen(name));
	write(s, "\r\n", 2);

	sockets[s].func = read_netfinger;
	sockets[s].is_read = s;
	return;
}

void userhost_nsl(WhoisStuff *stuff, char *nick, char *args)
{
	char *nsl;

	if (!stuff || !stuff->nick || !nick || !strcmp(stuff->user, "<UNKNOWN>") || my_stricmp(stuff->nick, nick))
	{
		say("No information for %s", nick);
		return;
	}
	nsl = do_nslookup(stuff->host);
}

void userhost_ignore _((WhoisStuff *stuff, char *nick1, char *args))
{
	char *p, *arg;
	char *nick = NULL, *user = NULL, *host = NULL;
	int old_window_display;
	char ignorebuf[BIG_BUFFER_SIZE+1];
	Ignore *igptr, *igtmp;
	WhowasList *whowas;

	arg = next_arg(args, &args);
	if (!stuff || !stuff->nick || !nick1 || !strcmp(stuff->user, "<UNKNOWN>") || my_stricmp(stuff->nick, nick1))
	{
                if ((whowas = check_whowas_nick_buffer(nick1, arg, 0)))
		{
			bitchsay("Using WhoWas info for %s of %s ", arg, nick1);
			user = host; host = strchr(host, '@'); *host++ = 0;
			nick = whowas->nicklist->nick;
		}
		else
		{                                                                                                                
			say("No match for user %s", nick1);
			return;
		}
	}
	else
	{
		user = clear_server_flags(stuff->user);
		host = stuff->host; nick = stuff->nick;
	}
	if (!my_stricmp(arg, "+HOST"))
		sprintf(ignorebuf, "*!*@%s ALL -CRAP -PUBLIC", cluster(host));
	else if (!my_stricmp(arg, "+USER"))
		sprintf(ignorebuf, "*%s@%s ALL -CRAP -PUBLIC", user, cluster(host));
	else if (!my_stricmp(arg, "-USER") || !my_stricmp(arg, "-HOST"))
	{
		int found = 0;
		if (!my_stricmp(arg, "-HOST"))
			sprintf(ignorebuf, "*!*@%s", cluster(host));
		else
			sprintf(ignorebuf, "%s!%s@%s", nick, user, host);
		igptr = ignored_nicks;
		while (igptr != NULL)
		{
			igtmp = igptr->next;
			if (match(igptr->nick, ignorebuf) ||
			    match(nick, igptr->nick))
			{
				sprintf(ignorebuf, "%s NONE", igptr->nick);
				old_window_display = window_display;
				window_display = 0;
				ignore(NULL, ignorebuf, ignorebuf, NULL);
				window_display = old_window_display;
				bitchsay("Unignored %s!%s@%s", nick, user, host);
				found++;
			}
			igptr = igtmp;
		}
		if (!found)
			bitchsay("No Match for ignorance of %s", nick);
		return;
	}
	old_window_display = window_display;
	window_display = 0;
	ignore(NULL, ignorebuf, ignorebuf, NULL);
	if ((arg = next_arg(args, &args)))
	{
		char tmp[BIG_BUFFER_SIZE+1];
		sprintf(tmp, "%s ^IGNORE %s NONE", arg, ignorebuf);
		timercmd("TIMER", tmp, NULL, NULL);
	}
	window_display = old_window_display;
	if ((p = strchr(ignorebuf, ' ')))
		*p = 0;
	say("Now ignoring ALL except CRAP and PUBLIC from %s", ignorebuf);
	return;
}

BUILT_IN_COMMAND(reset)
{
	refresh_screen(0, NULL);
}


extern char *channel_key _((char *));
BUILT_IN_COMMAND(cycle)
{
	char *to = NULL;
	int server = from_server;
	ChannelList *chan;
	
	if (args && args)
		to = next_arg(args, &args);
		
	if (!(chan = prepare_command(&server, to, NO_OP)))
		return;		
	my_send_to_server(server, "PART %s", chan->channel);
	my_send_to_server(server, "JOIN %s%s%s", chan->channel, chan->key?" ":"", chan->key?chan->key:"");
}


BUILT_IN_COMMAND(nslookup)
{
	char *host, *hostname;

	if ((host = next_arg(args, &args)))
	{
		bitchsay("Checking tables...");
		if (!strchr(host, '.'))
			add_to_userhost_queue(host, userhost_nsl, "%s", host);
		else
			hostname = do_nslookup(host);
	}
	else
		userage(command, helparg);
}

int do_newuser(char *command, char *args, char *subargs)
{
char *newusername = NULL;
	if ((newusername = next_arg(args, &args)))
	{
#ifdef IDENT_FAKE
		FILE *outfile;
		char *p = NULL, *q = NULL;
		malloc_sprintf(&p, "~/%s", get_string_var(IDENT_HACK_VAR));
		q = expand_twiddle(p);
		outfile = fopen(q,"w");
#ifdef CIDENTD
		fprintf(outfile,"hideme\nmynameis %s\n", newusername);
#else
		fprintf(outfile,"%s", newusername);
#endif
		fclose(outfile);
#endif
		strmcpy(username, newusername, NAME_LEN);
		if (subargs && *subargs)
                        strmcpy(realname, subargs, REALNAME_LEN);
#ifdef IDENT_FAKE
		new_free(&p); new_free(&q);
#endif
		reconnect_cmd(NULL, newusername, NULL, NULL);
	}
	else
		return 0;
	return 1;
}

BUILT_IN_COMMAND(newnick)
{
	char *newnick, *newusername;

	if ((newnick = next_arg(args, &args)) &&
	    (newusername = next_arg(args, &args)))
		do_newuser(newnick, newusername, args);
	else
		say("You must specify a nick and username");
}


BUILT_IN_COMMAND(newuser)
{
	char *newusername;

	if ((newusername = next_arg(args, &args)))
	{
		if ((do_newuser(NULL, newusername, args)))
			say("You must specify a username.");
	} else
		userage(command, helparg);
}

void start_finger _((WhoisStuff *stuff, char *nick, char *args))
{
	char *finger_userhost = NULL;

	if (!stuff || !stuff->nick || !nick || !strcmp(stuff->user, "<UNKNOWN>") || my_stricmp(stuff->nick, nick))
	{
		say("No information for %s", nick);
		return;
	}

	malloc_sprintf(&finger_userhost, "%s@%s", stuff->user, stuff->host);
	if (*finger_userhost == '~' || *finger_userhost == '#')
	{
		say("Launching finger for %s (%s)", nick, finger_userhost+1);
		netfinger(finger_userhost+1);
		new_free(&finger_userhost);
		return;
	}
	say("Launching finger for %s (%s)", nick, finger_userhost);
	netfinger(finger_userhost);
	new_free(&finger_userhost);
}

BUILT_IN_COMMAND(finger)
{
	char *userhost;

	if ((userhost = next_arg(args, &args)))
	{
		if (!strchr(userhost, '@'))
		{
			add_to_userhost_queue(userhost, start_finger, userhost);
			return;
		}
		netfinger(userhost);
	}
	else
		userage(command, helparg);
}

BUILT_IN_COMMAND(do_ig)
{
	char *nick;
	static char ignore_type[6];
	int got_ignore_type = 0;
	int need_time = 0;
	if (!args || !*args)
		goto bad_ignore;

	while ((nick = next_arg(args, &args)))
	{
		if (!nick || !*nick)
			goto bad_ignore;
		if (*nick == '-' || *nick == '+')
		{
			if (!my_stricmp(nick, "-USER") || !my_stricmp(nick, "+HOST") || !my_stricmp(nick, "+USER") || !my_stricmp(nick, "-HOST"))
				strcpy(ignore_type, nick);
			if (!args || !*args)
				goto bad_ignore;
			got_ignore_type ++;
			continue;
		}
		else if (!got_ignore_type)
		{
			if (command && !my_strnicmp(command, "IGH",3))
				strcpy(ignore_type, "+HOST");
			else if (command && !my_strnicmp(command, "IG",2))
				strcpy(ignore_type, "+USER");
			if (command && !my_strnicmp(command, "UNIGH", 5))
				strcpy(ignore_type, "-HOST");
			else if (command && !my_strnicmp(command, "UNIG", 4))
				strcpy(ignore_type, "-USER");
			if (command && toupper(command[strlen(command)-1]) == 'T')
				need_time ++;
		}
		if (need_time)
			add_to_userhost_queue(nick, userhost_ignore, "%s %d", ignore_type, get_int_var(IGNORE_TIME_VAR) * 60);
		else
			add_to_userhost_queue(nick, userhost_ignore, "%s", ignore_type);
	}
	return;
bad_ignore:
	userage(command, helparg);
}



BUILT_IN_COMMAND(users)
{
	ChannelList *chan;
	NickList *nicks;
	char *to, *spec, *rest, *temp1;
	char modebuf[BIG_BUFFER_SIZE + 1];
	char msgbuf[BIG_BUFFER_SIZE +1];
	int count, ops, msg;
	int hook = 0;
	int server = from_server; 
	
	rest = NULL;
	spec = NULL;
	temp1 = NULL;
	*msgbuf = 0;
	*modebuf = 0;
		
	if (!(to = next_arg(args, &args)))
		to = NULL;
	if (to && !is_channel(to))
	{
		spec = to;
		to = NULL;
	}

	if (!(chan = prepare_command(&server, to, NO_OP)))
		return;

	message_from(chan->channel, LOG_CRAP);
	if (!spec && !(spec = next_arg(args, &args)))
		spec = "*!*@*";
	if (*spec == '-')
	{
		temp1 = spec;
		spec = "*!*@*";
	}
	else
		temp1 = next_arg(args, &args);
	ops = 0;
	msg = 0;

	if ( ((temp1 && (!my_strnicmp(temp1, "-ops", strlen(temp1)))) || (command && !my_stricmp(command, "CHOPS")) ))
		ops = 1;
	if ( ((temp1 && (!my_strnicmp(temp1, "-nonops", strlen(temp1)))) || (command && !my_stricmp(command, "NOPS")) ))
		ops = 2;
	if (ops)
		temp1 = next_arg(args, &args);

	if (temp1)
	{
		if (!my_strnicmp(temp1, "-msg", strlen(temp1)))
			msg = 1;
		else if (!my_strnicmp(temp1, "-notice", strlen(temp1)))
			msg = 2;
		else if (!my_strnicmp(temp1, "-nkill", strlen(temp1)))
			msg = 3;
		else if (!my_strnicmp(temp1, "-kill", strlen(temp1)))
			msg = 4;
		else if (!my_strnicmp(temp1, "-kick", strlen(temp1)))
			msg = 5;
		else if (!my_strnicmp(temp1, "-stats", strlen(temp1)))
			msg = 6;
	}
	if ((msg == 1 || msg == 2) && (!args || !*args))
	{
		say("No message given");
		message_from(NULL, LOG_CRAP);
		return;
	}

	count = 0;
	switch (msg)
	{
		case 6:
			if (do_hook(STAT_HEADER_LIST, "%s %s %s %s %s", "Nick", "dops", "kicks","nicks","publics"))
				put_it("Nick        dops  kicks  nicks  publics");
			break;
		default:
			break;
	}
	for (nicks = next_nicklist(chan, NULL); nicks; nicks = next_nicklist(chan, nicks))
	{
		sprintf(modebuf, "%s!%s", nicks->nick,
		      nicks->host ? nicks->host : "<UNKNOWN@UNKOWN>");
		if (match(spec, modebuf) && (!ops ||
					 ((ops == 1) && nicks->chanop) ||
					 ((ops == 2) && !nicks->chanop)))
		{
			if (msg == 3)
				count--;
			else if (msg == 4)
				if (!isme(nicks->nick))
					my_send_to_server(server, "KILL %s :%s (%i", nicks->nick,
						       args && *args ? args : get_reason(nicks->nick, NULL),
						       count + 1);
				else
					count--;
			else if (msg == 5)
				if (!isme(nicks->nick))
					my_send_to_server(server, "KICK %s %s :%s", chan->channel,
						       nicks->nick, (args && *args) ? args :
						       get_reason(nicks->nick, NULL));
				else
					count--;
			else if (msg == 6)
			{
				if (my_stricmp(nicks->nick, get_server_nickname(from_server)))
				{
					if (do_hook(STAT_LIST, "%s %d %d %d %d", nicks->nick, nicks->dopcount, nicks->kickcount, 
						nicks->nickcount, nicks->floodcount))
						put_it("%-10s  %4d   %4d   %4d     %4d", 
							nicks->nick, nicks->dopcount, nicks->kickcount, 
							nicks->nickcount, nicks->floodcount);
				}
			}	
			else if (msg == 1 || msg == 2)
			{
				if (count)
					strcat(msgbuf, ",");
				strcat(msgbuf, nicks->nick);
			}
			else
			{
				if (!count && do_hook(USERS_HEADER_LIST, "%s %s %s %s %s %s %s", "Level", "aop", "prot", "Channel", "Nick", "+o", "UserHost"))
					put_it("%s", convert_output_format(fget_string_var(FORMAT_USERS_HEADER_FSET), "%s", chan->channel));
				
				if ((hook = do_hook(USERS_LIST, "%d %d %d %s %s %s %c",
					nicks->userlist? nicks->userlist->level:0, 
					nicks->userlist?nicks->userlist->aop:0,
					nicks->userlist?nicks->userlist->prot:0, 
					chan->channel, nicks->nick, 
					nicks->host, 
					nicks->chanop ? '@' : ' ')))
				{
					put_it("%s", convert_output_format(fget_string_var(nicks->userlist?FORMAT_USERS_USER_FSET:nicks->botlist?FORMAT_USERS_BOT_FSET:nicks->shitlist?FORMAT_USERS_SHIT_FSET:FORMAT_USERS_FSET), "%d %d %d %s %s %s %s", 
						nicks->userlist ? nicks->userlist->level:nicks->botlist?0:nicks->shitlist?nicks->shitlist->level:0,
						nicks->userlist ? nicks->userlist->aop:nicks->botlist?nicks->botlist->aop:0,
						nicks->userlist ? nicks->userlist->prot:nicks->botlist?nicks->botlist->prot:0, 
						chan->channel, nicks->nick,
						nicks->host,
						nicks->chanop ? "@" : (nicks->voice? "v" : "ÿ")));
				}
			}
			count++;
		}
	}
	if (!count)
	{
		if (!command)
			say("No match of %s on %s", spec, chan->channel);
		else
			say("There are no [\002%s\002] on %s", command, chan->channel);
	}
	else if (!msg && !hook)
		bitchsay("End of UserList on %s %d counted", chan->channel, count);

	if (msg && (msg != 3) && (msg != 4) && (msg != 5) && (msg != 6) /*&& (msg != 7)*/ && count)
	{
		put_it("%s", convert_output_format(fget_string_var((msg == 1)?FORMAT_SEND_MSG_FSET:FORMAT_SEND_NOTICE_FSET), "%s %s %s", update_clock(GET_TIME),msgbuf, args));
		my_send_to_server(server, "%s %s :%s", (msg == 1) ? "PRIVMSG" : "NOTICE", msgbuf, args);
	}
	message_from(NULL, LOG_CRAP);

}

int caps_fucknut _((register char *crap))
{
	int total = 0, allcaps = 0;
/* removed from ComStud client */
	while (*crap)
	{
		if ((*crap >= 'a' && *crap <= 'z') || (*crap >= 'A' && *crap <= 'Z'))
		{
			total++;
			if (toupper(*crap) == *crap)
				allcaps++;
		}
		crap++;
	}
	if (total)
		if ((float) allcaps / (float) total >= .75 && total > 3)
			return (1);
	return (0);
}

int char_fucknut _((register char *crap, char looking, int max))
{
	int total = strlen(crap), allchar = 0;

	while (*crap)
	{
		if ((*crap == looking))
		{
			crap++;
			while(*crap && *crap != looking)
			{
				allchar++;
				crap++;
			}
		}
		if (*crap)
			crap++;
	}
	if (total > 12)
		if ((float) allchar / (float) total >= .75 && total > 3)
			return (1);
	return (0);
}

static int cparse_recurse = -1;

char *convert_output_format(const char *format, const char *str, ...)
{
static unsigned char buffer[10*BIG_BUFFER_SIZE+1];
char buffer2[2*BIG_BUFFER_SIZE+1];
enum color_attributes this_color = BLACK;
register unsigned char *t;
register unsigned char *s;
char *copy = NULL;
char *tmpc = NULL;
char *p;
int old_who_level = who_level;
int bold = 0;
extern int in_chelp;
va_list args;
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/* && !in_cparse*/)
	{

		p = (char *)str;
		va_start(args, str);
		while(p && *p)
		{
			if (*p == '%')
			{
				switch(*++p)
				{
				case 's':
				{
					char *s = (char *)va_arg(args, char *);
					if (s)
						strcat(buffer2, s);
					break;
				}
				case 'd':
				{
					int d = (int) va_arg(args, int);
					strcat(buffer2, ltoa((long)d));
					break;
				}
				case 'c':
				{
					char c = (char )va_arg(args, int);
					buffer2[strlen(buffer2)] = c;
					break;
				}
				case 'u':
				{
					unsigned int d = (unsigned int) va_arg(args, unsigned int);
					strcat(buffer2, ltoa(d));
					break;
				}
				case 'l':
				{
					unsigned long d = (unsigned long) va_arg(args, unsigned long);
					strcat(buffer2, ltoa(d));
					break;
				}
				case '%':
				{
					buffer2[strlen(buffer2)] = '%';
					p++;
					break;
				}
				default:
					strcat(buffer2, "%");
					buffer2[strlen(buffer2)] = *p;
				}
				p++;
			} else 
			{
				buffer2[strlen(buffer2)] = *p;
				p++;
			}
		}
		va_end(args);
	} 
	else if (/*in_cparse && */str)
		strcpy(buffer2, str);

	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 == '$' && !in_chelp)
		{
			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 == 255) *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;
}

void add_last_type (LastMsg *array, int size, char *from, char *uh, char *to, char *str)
{
int i;
	for (i = size - 1; i > 0; i--)
	{
		
		malloc_strcpy(&(array[i].last_msg), array[i - 1].last_msg);
		malloc_strcpy(&(array[i].from), array[i - 1].from);
		malloc_strcpy(&(array[i].uh), array[i - 1].uh);
		malloc_strcpy(&(array[i].to), array[i - 1].to);
		malloc_strcpy(&(array[i].time), array[i - 1].time);
	}
	malloc_strcpy(&array->last_msg, str);
	malloc_strcpy(&array->from, from);
	malloc_strcpy(&array->to, to);
	malloc_strcpy(&array->uh, uh);
	malloc_strcpy(&array->time, update_clock(GET_TIME));
}

int check_last_type(LastMsg *array, int size, char *from, char *uh)
{
int i;
	for (i = 0; i < size-1; i++)
	{
		if (array[i].from && array[i].uh && !my_stricmp(from, array[i].from) && !my_stricmp(uh, array[i].uh))
			return 1;
	}
	return 0;
}

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);
}

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

	if (!channel && !get_current_channel_by_refnum(0))
	{
		context;
		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 : get_current_channel_by_refnum(0), server, 0)))
	{
		context;
		if (need_op != 3) 
			not_on_a_channel(curr_scr_win);
		return NULL;
	}
	if (need_op == NEED_OP && chan && !chan->chop) 
	{
		context;
		error_not_opped(chan->channel);
		return NULL;
	}
	return chan;
}

char *make_channel (char *chan)
{
static char buffer[BIG_BUFFER_SIZE+1];
	*buffer = 0;
        if (*chan != '#' && *chan != '&' && *chan != '+')
		snprintf(buffer, IRCD_BUFFER_SIZE-2, "#%s", chan);
	else
		strncpy(buffer, chan, BIG_BUFFER_SIZE);
	return buffer;
}

BUILT_IN_COMMAND(do_map)
{
	if (server_list[from_server].link_look == 0)
	{
		bitchsay("Generating irc server map");
		send_to_server("LINKS");
		server_list[from_server].link_look = 2;
	} else
		bitchsay("Wait until previous %s is done", server_list[from_server].link_look == 2? "MAP":"LLOOK");	
}

void add_to_irc_map(char *server1, char *distance)
{
irc_server *tmp, *insert, *prev;
int dist = 0;
	if (distance)
		dist = atoi(distance);
	tmp = (irc_server *) 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];
#ifdef ONLY_STD_CHARS
	char *ascii="-> ";
#else
	char *ascii = "ÀÄ> ";
#endif			    
	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_string));
		prevdist = tmp->hopcount;
		new_free(&tmp->name);
		new_free((char **)&tmp);
	}	
}

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

char *country(char *hostname)
{
typedef struct _domain {
char *code;
char *country;
} Domain;
static 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 (pHEAR)" },
	{"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 (pHEAR)" },
	{"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 (pHEAR)" },
	{"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";
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;
}

