dslinux/user/bitchx/source Makefile.in X.c alias.c alist.c alloca.c array.c art.c banlist.c bot_link.c cdcc.c cdns.c cdrom.c chelp.c commands.c commands2.c compat.c cset.c ctcp.c dcc.c debug.c digraph.inc encrypt.c exec.c expr.c expr2.c files.c flood.c fset.c functions.c funny.c glob.c gtkbitchx.c hash.c hebrew.c help.c history.c hook.c if.c ignore.c input.c irc.c ircaux.c ircsig.c keys.c lastlog.c list.c log.c mail.c misc.c modules.c names.c network.c newio.c notice.c notify.c numbers.c opendir.c output.c parse.c pmbitchx.RES pmbitchx.c pmbitchx.def pmbitchx.ico pmbitchx.rc queue.c readlog.c reg.c scr-bx.c screen.c server.c stack.c status.c struct.c tcl_public.c term.c timer.c translat.c user.c userlist.c vars.c who.c whowas.c winbitchx.c window.c words.c wserv.c

stsp stsp at user.in-berlin.de
Sun Jul 2 15:18:39 CEST 2006


Update of /cvsroot/dslinux/dslinux/user/bitchx/source
In directory antilope:/tmp/cvs-serv9280/source

Added Files:
	Makefile.in X.c alias.c alist.c alloca.c array.c art.c 
	banlist.c bot_link.c cdcc.c cdns.c cdrom.c chelp.c commands.c 
	commands2.c compat.c cset.c ctcp.c dcc.c debug.c digraph.inc 
	encrypt.c exec.c expr.c expr2.c files.c flood.c fset.c 
	functions.c funny.c glob.c gtkbitchx.c hash.c hebrew.c help.c 
	history.c hook.c if.c ignore.c input.c irc.c ircaux.c ircsig.c 
	keys.c lastlog.c list.c log.c mail.c misc.c modules.c names.c 
	network.c newio.c notice.c notify.c numbers.c opendir.c 
	output.c parse.c pmbitchx.RES pmbitchx.c pmbitchx.def 
	pmbitchx.ico pmbitchx.rc queue.c readlog.c reg.c scr-bx.c 
	screen.c server.c stack.c status.c struct.c tcl_public.c 
	term.c timer.c translat.c user.c userlist.c vars.c who.c 
	whowas.c winbitchx.c window.c words.c wserv.c 
Log Message:
Adding pristine copy of BitchX so I can branch from it.


--- NEW FILE: gtkbitchx.c ---
/* gtkbitchx.c: Written by Brian Smith (NuKe) for BitchX */

/* So X11/X.h won't get included */
#define X_H

#include <zvt/zvtterm.h>
#include <gdk/gdkkeysyms.h>
#include <pthread.h>
#ifdef SOUND
#include <esd.h>
#endif
#include "ircaux.h"
#include "misc.h"
#include "window.h"
#include "server.h"
#include "hook.h"
#include "input.h"
#include "commands.h"
#include "list.h"
[...3333 lines suppressed...]
{
	if(get_int_var(MDI_VAR))
	{
		gtkcommand.command = GTKACTIVITY;
		gtkcommand.data[0] = (gpointer) color;
		gtkcommand.data[1] = (gpointer) target_window;
		gtk_sendcommand(TRUE);
	}
}

void gui_setfileinfo(char *filename, char *nick, int server)
{
}

void gui_setfd(fd_set *rd)
{
	/* Set the GUI IPC pipe readable for select() */
	FD_SET(guiipc[0], rd);
	FD_SET(gtkipcin[0], rd);
}

--- NEW FILE: whowas.c ---
/*
 * whowas.c   a linked list buffer of people who have left your channel 
 * mainly used for ban prot and stats stuff.
 * Should even speed stuff up a bit too.
 *
 * Written by Scott H Kilau
 *
 * Copyright(c) 1995
 * Modified Colten Edwards 1996
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
 */

#include "irc.h"
static char cvsrevision[] = "$Id: whowas.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(whowas_c)
#include "struct.h"

#include "vars.h"
#include "ircaux.h"
#include "window.h"
#include "who.h"
#include "hook.h"
#include "input.h"
#include "names.h"
#include "alias.h"
#include "output.h"
#include "numbers.h"
#include "status.h"
#include "screen.h"
#include "commands.h"
#include "config.h"
#include "list.h"
#include "userlist.h"
#include "misc.h"
#include "hash.h"
#include "hash2.h"
#define MAIN_SOURCE
#include "modval.h"

/*
#include "ctimers.h"
#include "edit2.h"
#include "edit3.h"
#include "edit4.h"
*/

#include "userlist.h"
#include "whowas.h"

WhowasWrapList whowas_userlist_list = { 0 };
WhowasWrapList whowas_reg_list = { 0 };
WhowasWrapList whowas_splitin_list = { 0 };

WhowasChanList *whowas_chan_list = NULL;

static int whowas_userlist_count = 0;
static int whowas_reg_count = 0;
static int whowas_chan_count = 0;

extern	WhowasList *check_whowas_buffer(char *nick, char *userhost, char *channel, int unlink)
{
	WhowasList *tmp = NULL;
	if (!(tmp = find_userhost_channel(userhost, channel, unlink, &whowas_userlist_list)))
		tmp = find_userhost_channel(userhost, channel, unlink, &whowas_reg_list);
	return tmp;
}


extern	WhowasList * check_whowas_nick_buffer(char *nick, char *channel, int unlink)
{
	WhowasList *tmp = NULL, *last = NULL;
	for (tmp = next_userhost(&whowas_userlist_list, NULL); tmp; tmp = next_userhost(&whowas_userlist_list, tmp))
	{
		if (!my_stricmp(tmp->nicklist->nick, nick) && !my_stricmp(tmp->channel, channel))
		{
			if (unlink)
			{
				last = find_userhost_channel(tmp->nicklist->host, tmp->channel, 1, &whowas_userlist_list);
				tmp = NULL;
			}
			return last?last:tmp;
		}
	}
	for (tmp = next_userhost(&whowas_reg_list, NULL); tmp; tmp = next_userhost(&whowas_reg_list, tmp))
	{
		if (!my_stricmp(tmp->nicklist->nick, nick) && !my_stricmp(tmp->channel, channel))
		{
			if (unlink)
			{
				last = find_userhost_channel(tmp->nicklist->host, tmp->channel, 1, &whowas_reg_list);
				tmp = NULL;
			}
			return last?last:tmp;
		}
	}
	return( NULL );
}

extern	WhowasList * check_whosplitin_buffer(char *nick, char *userhost, char *channel, int unlink)
{
	WhowasList *tmp = NULL;
	tmp = find_userhost_channel(userhost, channel, unlink, &whowas_splitin_list);
	return tmp;
}

void add_to_whowas_buffer(NickList *nicklist, char *channel, char *server1, char *server2)
{
	WhowasList *new;
	if (!nicklist || !nicklist->nick)
		return;

	if (nicklist->userlist) 
	{
		if (whowas_userlist_count >= whowas_userlist_max) 
		{
			whowas_userlist_count -=
			   remove_oldest_whowas(&whowas_userlist_list, 0,
			   (whowas_userlist_max + 1) - whowas_userlist_count); 
		}
		new = (WhowasList *) new_malloc(sizeof(WhowasList));
		new->has_ops = nick_isop(nicklist);
		new->nicklist = nicklist;
		malloc_strcpy(&(new->channel), channel);
		malloc_strcpy(&(new->server1), server1);
		if (server2)
			malloc_strcpy(&(new->server2), server2);
		new->time = now;
		add_whowas_userhost_channel(new, &whowas_userlist_list);
		whowas_userlist_count++;
	}
	else 
	{
		if (whowas_reg_count >= whowas_reg_max) 
		{
			whowas_reg_count -=
			   remove_oldest_whowas(&whowas_reg_list, 0,
			   (whowas_reg_max + 1) - whowas_reg_count); 
		}
		new = (WhowasList *) new_malloc(sizeof(WhowasList));
		new->has_ops = nick_isop(nicklist);
		new->nicklist = (NickList *) nicklist;
		malloc_strcpy(&(new->channel), channel);
		malloc_strcpy(&(new->server1), server1);
		malloc_strcpy(&(new->server2), server2);
		new->time = now;
		add_whowas_userhost_channel(new, &whowas_reg_list);
		whowas_reg_count++;
	}
}

void add_to_whosplitin_buffer(NickList *nicklist, char *channel, char *server1, char *server2)
{
	WhowasList *new;

	new = (WhowasList *) new_malloc(sizeof(WhowasList));
	new->has_ops = nick_isop(nicklist);

	new->nicklist = (NickList *)new_malloc(sizeof(NickList)); /*nicklist;*/
	new->nicklist->nick = m_strdup(nicklist->nick);
	new->nicklist->host = m_strdup(nicklist->host);

	malloc_strcpy(&(new->channel), channel);
	malloc_strcpy(&(new->server1), server1);
	malloc_strcpy(&(new->server2), server2);
	new->time = now;
	add_whowas_userhost_channel(new, &whowas_splitin_list);

}

int remove_oldest_whowas(WhowasWrapList *list, time_t timet, int count)
{
	int total = 0;
	/* if no ..count.. then remove ..time.. links */
	total = remove_oldest_whowas_hashlist(list, timet, count);
	return total;
}

void clean_whowas_list(void)
{
	if (whowas_userlist_count)
		whowas_userlist_count -= remove_oldest_whowas_hashlist(&whowas_userlist_list, 20 * 60, 0);
	if (whowas_reg_count)
		whowas_reg_count -= remove_oldest_whowas_hashlist(&whowas_reg_list, 10 * 60, 0);
	remove_oldest_whowas_hashlist(&whowas_splitin_list, 25 * 60 , 0);
}

/* Used to rehash whowas listings for new users */
void sync_whowas_adduser(UserList *added)
{
WhowasList *tmp;
	for (tmp = next_userhost(&whowas_userlist_list, NULL); tmp; tmp = next_userhost(&whowas_userlist_list, tmp))
	{
		if (check_channel_match(added->channels, tmp->channel))
			if (wild_match(added->host, tmp->nicklist->host) && wild_match(added->nick, tmp->nicklist->nick))
				tmp->nicklist->userlist = added;
	}
	for (tmp = next_userhost(&whowas_reg_list, NULL); tmp; tmp = next_userhost(&whowas_reg_list, tmp))
	{
		if (check_channel_match(added->channels, tmp->channel))
			if (wild_match(added->host, tmp->nicklist->host) && wild_match(added->nick, tmp->nicklist->nick))
				tmp->nicklist->userlist = added;
	}
}

/* Used to rehash whowas listings for removed userlist entries */
void sync_whowas_unuser(UserList *entry)
{
WhowasList *tmp;
	for (tmp = next_userhost(&whowas_userlist_list, NULL); tmp; tmp = next_userhost(&whowas_userlist_list, tmp))
	{
		if (!tmp->nicklist->userlist)  continue;
		if (!my_stricmp(tmp->nicklist->userlist->host, entry->host) && !my_stricmp(tmp->nicklist->userlist->nick, entry->nick) && 
			check_channel_match(tmp->nicklist->userlist->channels, entry->channels))
			tmp->nicklist->userlist = NULL;
	}
	for (tmp = next_userhost(&whowas_reg_list, NULL); tmp; tmp = next_userhost(&whowas_reg_list, tmp))
	{
		if (!tmp->nicklist->userlist)  continue;
		if (!my_stricmp(tmp->nicklist->userlist->host, entry->host) && !my_stricmp(tmp->nicklist->userlist->nick, entry->nick) && 
			check_channel_match(tmp->nicklist->userlist->channels, entry->channels))
			tmp->nicklist->userlist = NULL;
	}
}

/* Used to rehash whowas listings for new shitlist entries */
void sync_whowas_addshit(ShitList *added)
{
WhowasList *tmp;
char user[BIG_BUFFER_SIZE];
	for (tmp = next_userhost(&whowas_userlist_list, NULL); tmp; tmp = next_userhost(&whowas_userlist_list, tmp))
	{
		if (check_channel_match(added->channels, tmp->channel)) 
		{
			sprintf(user, "%s!%s", tmp->nicklist->nick, tmp->nicklist->host);
			if (wild_match(added->filter, user))
				tmp->nicklist->shitlist = added;
		}
	}
	for (tmp = next_userhost(&whowas_reg_list, NULL); tmp; tmp = next_userhost(&whowas_reg_list, tmp))
	{
		if (check_channel_match(added->channels, tmp->channel))
		{
			sprintf(user, "%s!%s", tmp->nicklist->nick, tmp->nicklist->host);
			if (wild_match(added->filter, user))
				tmp->nicklist->shitlist = added;
		}
	}
}

/* Used to rehash whowas listings for removed shitlist entries */
void sync_whowas_unshit(ShitList *entry)
{
WhowasList *tmp;
	for (tmp = next_userhost(&whowas_userlist_list, NULL); tmp; tmp = next_userhost(&whowas_userlist_list, tmp))
	{
		if (!tmp->nicklist->shitlist)  continue;
		if (!my_stricmp(tmp->nicklist->shitlist->filter, entry->filter) &&
			check_channel_match(tmp->nicklist->shitlist->channels, entry->channels))
			tmp->nicklist->shitlist = NULL;
	}
	for (tmp = next_userhost(&whowas_reg_list, NULL); tmp; tmp = next_userhost(&whowas_reg_list, tmp))
	{
		if (!tmp->nicklist->shitlist)  continue;
		if (!my_stricmp(tmp->nicklist->shitlist->filter, entry->filter) &&
			check_channel_match(tmp->nicklist->shitlist->channels, entry->channels))
			tmp->nicklist->shitlist = NULL;
	}
}


/* BELOW THIS MARK IS THE CHANNEL WHOWAS STUFF */

extern	WhowasChanList *check_whowas_chan_buffer(char *channel, int refnum, int unlink)
{
	WhowasChanList *tmp, *last = NULL;

	for (tmp = whowas_chan_list; tmp; tmp = tmp->next) {
		if (!my_stricmp(tmp->channellist->channel, channel))
		{
			if ((refnum == -1) || (refnum == tmp->channellist->refnum)) 
			{
				if (unlink) {
					if (last)
						last->next = tmp->next;
					else
						whowas_chan_list = tmp->next;
					whowas_chan_count--;
				}
				return(tmp);
			}
		}
		last = tmp;
	}
	return( NULL );
}

void add_to_whowas_chan_buffer(ChannelList *channel)
{
	WhowasChanList *new;
	WhowasChanList **slot;

	if (whowas_chan_count >= whowas_chan_max) 
	{
		whowas_chan_count -=
		   remove_oldest_chan_whowas(&whowas_chan_list, 0,
		   (whowas_chan_max + 1) - whowas_chan_count); 
	}
	new = (WhowasChanList *) new_malloc(sizeof(WhowasChanList));

	new->channellist = channel;
	new->time = now;
	clear_nicklist_hashtable(channel);
	/* we've created it, now put it in order */
	for (slot = &whowas_chan_list; *slot; slot = &(*slot)->next)
	{
		if ((*slot)->time > new->time)
			break;
	}
	new->next = *slot;
	*slot = new;
	whowas_chan_count++;
}

int remove_oldest_chan_whowas(WhowasChanList **list, time_t timet, int count)
{
	WhowasChanList *tmp = NULL;
	time_t t;
	int total = 0;

	/* if no ..count.. then remove ..time.. links */
	if (!count) 
	{
		t = now;
		while (*list && ((*list)->time + timet) <= t) 
		{
			tmp = *list;
			new_free(&(tmp->channellist->channel));
			new_free(&(tmp->channellist->topic));
			new_free(&(tmp->channellist->modelock_key));
			clear_bans(tmp->channellist);
			if (tmp->channellist->msglog_fp)
				fclose(tmp->channellist->msglog_fp);
			remove_csets_for_channel(tmp->channellist->csets);
			new_free((char **)&(tmp->channellist));
			*list = tmp->next;
			new_free((char **)&tmp);
			total++;
		}
	}
	else 
	{
		while (*list && count) 
		{
			tmp = *list;
			new_free(&(tmp->channellist->channel));
			new_free(&(tmp->channellist->topic));
			new_free(&(tmp->channellist->modelock_key));
			clear_bans(tmp->channellist);
			if (tmp->channellist->msglog_fp)
				fclose(tmp->channellist->msglog_fp);
			remove_csets_for_channel(tmp->channellist->csets);
			new_free((char **)&(tmp->channellist));
			*list = tmp->next;
			new_free((char **)&tmp);
			total++;
			count--;
		}
	}
	return total;
}

void clean_whowas_chan_list(void)
{
   whowas_chan_count -= remove_oldest_chan_whowas(&whowas_chan_list, 24 * 60 * 60, 0);
}

void clear_whowas(void)
{
	whowas_chan_count -= remove_oldest_chan_whowas(&whowas_chan_list, 0, 0);
	if (whowas_userlist_count)
		whowas_userlist_count -= remove_oldest_whowas_hashlist(&whowas_userlist_list, 0, 0);
	if (whowas_reg_count)
		whowas_reg_count -= remove_oldest_whowas_hashlist(&whowas_reg_list, 0, 0);
	remove_oldest_whowas_hashlist(&whowas_splitin_list, 0 , 0);
}

void show_whowas(void)
{
	reset_display_target();
	show_whowas_hashtable(&whowas_userlist_list, "Userlist");
	show_whowas_hashtable(&whowas_reg_list, "Reglist");
}

void show_wholeft(char *channel)
{
int count = 0;
int hook = 0;
time_t ltime = now;
	reset_display_target();
#if 0
	hook = show_wholeft_hashtable(&whowas_splitin_list, ltime, &count, &hook, "SplitList");
#endif
	hook = show_wholeft_hashtable(&whowas_userlist_list, ltime, &count, &hook, "Splitin");
	hook = show_wholeft_hashtable(&whowas_reg_list, ltime, &count, &hook, "Splitin");
	if (count && hook && fget_string_var(FORMAT_WHOLEFT_FOOTER_FSET))
		put_it("%s", convert_output_format(fget_string_var(FORMAT_WHOLEFT_FOOTER_FSET), NULL));
}

--- NEW FILE: digraph.inc ---
/*
 * diagraph.inc: included several times by translat.c
 */

DiLo(' ')	DiHi(0xa8)	DiDi(0x22)	/* Digital VT300 / VT400 */
DiLo(' ')	DiHi(0xb4)	DiDi(0x27)	/* Digital VT300 / VT400 */

/*
 * We don't really need to be *that* pedantic, so let's skip the useless
 * ones:  /Tomten
 */
/* DiLo(' ')	DiHi(' ')	DiDi(0xa0) */	/* Digital VT300 / VT400 */
/* DiLo(' ')	DiHi('^')	DiDi('^')  */	/* Digital VT300 / VT400 */
/* DiLo(' ')	DiHi('`')	DiDi('`')  */	/* Digital VT300 / VT400 */
/* DiLo(' ')	DiHi('~')	DiDi('~')  */	/* Digital VT300 / VT400 */
/* DiLo(' ')	DiHi(0xa8)	DiDi(0xa8) */	/* Digital VT300 / VT400 */

DiLo('!')	DiHi('!')	DiDi(0xa1)	/* Digital VT300 / VT400 */
DiLo('!')	DiHi('P')	DiDi(0xb6)	/* Digital VT300 / VT400 */
DiLo('!')	DiHi('S')	DiDi(0xa7)	/* Digital VT300 / VT400 */
DiLo('!')	DiHi('^')	DiDi(0xa6)	/* Digital VT300 / VT400 */
DiLo('!')	DiHi('~')	DiDi(0xa1)

DiLo(0x22)	DiHi(0x22)	DiDi(0xa8)	/* Digital VT300 / VT400 */
DiLo(0x22)	DiHi('>')	DiDi(0xab)
DiLo(0x22)	DiHi('<')	DiDi(0xbb)
DiLo(0x22)	DiHi('A')	DiDi(0xc4)
DiLo(0x22)	DiHi('E')	DiDi(0xcb)
DiLo(0x22)	DiHi('I')	DiDi(0xcf)
DiLo(0x22)	DiHi('O')	DiDi(0xd6)
DiLo(0x22)	DiHi('U')	DiDi(0xdc)
DiLo(0x22)	DiHi('a')	DiDi(0xe4)
DiLo(0x22)	DiHi('e')	DiDi(0xeb)
DiLo(0x22)	DiHi('i')	DiDi(0xef)
DiLo(0x22)	DiHi('o')	DiDi(0xf6)
DiLo(0x22)	DiHi('u')	DiDi(0xfc)
DiLo(0x22)	DiHi('y')	DiDi(0xff)

DiLo(0x27)	DiHi(0x27)	DiDi(0xb4)	/* Digital VT300 / VT400 */
DiLo(0x27)	DiHi('A')	DiDi(0xc1)
DiLo(0x27)	DiHi('E')	DiDi(0xc9)
DiLo(0x27)	DiHi('I')	DiDi(0xcd)
DiLo(0x27)	DiHi('O')	DiDi(0xd3)
DiLo(0x27)	DiHi('U')	DiDi(0xda)
DiLo(0x27)	DiHi('Y')	DiDi(0xdd)
DiLo(0x27)	DiHi('a')	DiDi(0xe1)
DiLo(0x27)	DiHi('e')	DiDi(0xe9)
DiLo(0x27)	DiHi('i')	DiDi(0xed)
DiLo(0x27)	DiHi('o')	DiDi(0xf3)
DiLo(0x27)	DiHi('u')	DiDi(0xfa)

DiLo('(')	DiHi('(')	DiDi('[')	/* Digital VT300 / VT400 */
DiLo('(')	DiHi('-')	DiDi('{')	/* Digital VT300 / VT400 */

DiLo(')')	DiHi(')')	DiDi(']')	/* Digital VT300 / VT400 */
DiLo(')')	DiHi('-')	DiDi('}')	/* Digital VT300 / VT400 */

DiLo('*')	DiHi('A')	DiDi(0xc5)	/* Digital VT300 / VT400 */
DiLo('*')	DiHi('a')	DiDi(0xe5)

DiLo('+')	DiHi('+')	DiDi('#')	/* Digital VT300 / VT400 */
DiLo('+')	DiHi('-')	DiDi(0xb1)	/* Digital VT300 / VT400 */

DiLo(',')	DiHi(',')	DiDi(0xb8)	/* Digital VT300 / VT400 */
DiLo(',')	DiHi('-')	DiDi(0xac)	/* Digital VT300 / VT400 */
DiLo(',')	DiHi('C')	DiDi(0xc7)
DiLo(',')	DiHi('c')	DiDi(0xe7)

DiLo('-')	DiHi('-')	DiDi(0xad)	/* Digital VT300 / VT400 */
DiLo('-')	DiHi(':')	DiDi(0xf7)	/* Digital VT300 / VT400 */
DiLo('-')	DiHi('D')	DiDi(0xd0)
DiLo('-')	DiHi('L')	DiDi(0xa3)	/* Digital VT300 / VT400 */
DiLo('-')	DiHi('Y')	DiDi(0xa5)	/* Digital VT300 / VT400 */
DiLo('-')	DiHi('^')	DiDi(0xaf)	/* Digital VT300 / VT400 */
DiLo('-')	DiHi('a')	DiDi(0xaa)
DiLo('-')	DiHi('d')	DiDi(0xf0)
DiLo('-')	DiHi('o')	DiDi(0xba)

DiLo('.')	DiHi('^')	DiDi(0xb7)	/* Digital VT300 / VT400 */

DiLo('/')	DiHi('/')	DiDi('\\')	/* Digital VT300 / VT400 */
DiLo('/')	DiHi('<')	DiDi('\\')	/* Digital VT300 / VT400 */
DiLo('/')	DiHi('C')	DiDi(0xa2)	/* Digital VT300 / VT400 */
DiLo('/')	DiHi('O')	DiDi(0xd8)
DiLo('/')	DiHi('U')	DiDi(0xb5)	/* Digital VT300 / VT400 */
DiLo('/')	DiHi('^')	DiDi('|')	/* Digital VT300 / VT400 */
DiLo('/')	DiHi('o')	DiDi(0xf8)

DiLo('0')	DiHi('C')	DiDi(0xa9)	/* Digital VT300 / VT400 */
DiLo('0')	DiHi('S')	DiDi(0xa7)	/* Digital VT300 / VT400 */
DiLo('0')	DiHi('X')	DiDi(0xa4)	/* Digital VT300 / VT400 */
DiLo('0')	DiHi('^')	DiDi(0xb0)	/* Digital VT300 / VT400 */

DiLo('1')	DiHi('^')	DiDi(0xb9)	/* Digital VT300 / VT400 */
DiLo('1')	DiHi('2')	DiDi(0xbd)	/* Digital VT300 / VT400 */
DiLo('1')	DiHi('4')	DiDi(0xbc)	/* Digital VT300 / VT400 */

DiLo('2')	DiHi('^')	DiDi(0xb2)	/* Digital VT300 / VT400 */

DiLo('3')	DiHi('4')	DiDi(0xbe)	/* Digital VT300 / VT400 */
DiLo('3')	DiHi('^')	DiDi(0xb3)	/* Digital VT300 / VT400 */

DiLo('<')	DiHi('<')	DiDi(0xab)	/* Digital VT300 / VT400 */

DiLo('=')	DiHi('L')	DiDi(0xa3)	/* Digital VT300 / VT400 */
DiLo('=')	DiHi('Y')	DiDi(0xa5)	/* Digital VT300 / VT400 */

DiLo('>')	DiHi('>')	DiDi(0xbb)	/* Digital VT300 / VT400 */

DiLo('?')	DiHi('?')	DiDi(0xbf)	/* Digital VT300 / VT400 */
DiLo('?')	DiHi('~')	DiDi(0xbf)

DiLo('@')	DiHi('A')	DiDi(0xc5)
DiLo('@')	DiHi('a')	DiDi(0xe5)

DiLo('A')	DiHi('A')	DiDi('@')	/* Digital VT300 / VT400 */
DiLo('A')	DiHi('E')	DiDi(0xc6)
DiLo('A')	DiHi('^')	DiDi(0xc2)
DiLo('A')	DiHi('_')	DiDi(0xaa)	/* Digital VT300 / VT400 */
DiLo('A')	DiHi('`')	DiDi(0xc0)
DiLo('A')	DiHi('~')	DiDi(0xc3)
DiLo('A')	DiHi(0xa8)	DiDi(0xc4)	/* Digital VT300 / VT400 */
DiLo('A')	DiHi(0xb0)	DiDi(0xc5)	/* Digital VT300 / VT400 */
DiLo('A')	DiHi(0xb4)	DiDi(0xc1)	/* Digital VT300 / VT400 */

DiLo('C')	DiHi('O')	DiDi(0xa9)	/* Digital VT300 / VT400 */
DiLo('C')	DiHi('|')	DiDi(0xa2)	/* Digital VT300 / VT400 */
DiLo('C')	DiHi(0xb8)	DiDi(0xc7)	/* Digital VT300 / VT400 */

DiLo('E')	DiHi('^')	DiDi(0xca)
DiLo('E')	DiHi('`')	DiDi(0xc8)
DiLo('E')	DiHi(0xa8)	DiDi(0xcb)	/* Digital VT300 / VT400 */
DiLo('E')	DiHi(0xb4)	DiDi(0xc9)	/* Digital VT300 / VT400 */

DiLo('H')	DiHi('T')	DiDi(0xde)	/* Digital VT300 / VT400 */

DiLo('I')	DiHi('^')	DiDi(0xce)
DiLo('I')	DiHi('`')	DiDi(0xcc)
DiLo('I')	DiHi(0xa8)	DiDi(0xcf)	/* Digital VT300 / VT400 */
DiLo('I')	DiHi(0xb4)	DiDi(0xcd)	/* Digital VT300 / VT400 */

DiLo('N')	DiHi('~')	DiDi(0xd1)

DiLo('O')	DiHi('R')	DiDi(0xae)	/* Digital VT300 / VT400 */
DiLo('O')	DiHi('S')	DiDi(0xa7)	/* Digital VT300 / VT400 */
DiLo('O')	DiHi('X')	DiDi(0xa4)	/* Digital VT300 / VT400 */
DiLo('O')	DiHi('^')	DiDi(0xd4)
DiLo('O')	DiHi('_')	DiDi(0xba)	/* Digital VT300 / VT400 */
DiLo('O')	DiHi('`')	DiDi(0xd2)
DiLo('O')	DiHi('~')	DiDi(0xd5)
DiLo('O')	DiHi(0xa8)	DiDi(0xd6)	/* Digital VT300 / VT400 */
DiLo('O')	DiHi(0xb4)	DiDi(0xd3)	/* Digital VT300 / VT400 */

DiLo('U')	DiHi('^')	DiDi(0xdb)
DiLo('U')	DiHi('`')	DiDi(0xd9)
DiLo('U')	DiHi(0xa8)	DiDi(0xdc)	/* Digital VT300 / VT400 */
DiLo('U')	DiHi(0xb4)	DiDi(0xda)	/* Digital VT300 / VT400 */

DiLo('Y')	DiHi(0xb4)	DiDi(0xdd)	/* Digital VT300 / VT400 */

DiLo('^')	DiHi('a')	DiDi(0xe2)
DiLo('^')	DiHi('e')	DiDi(0xea)
DiLo('^')	DiHi('i')	DiDi(0xee)
DiLo('^')	DiHi('o')	DiDi(0xf4)
DiLo('^')	DiHi('u')	DiDi(0xfb)

DiLo('`')	DiHi('a')	DiDi(0xe0)
DiLo('`')	DiHi('e')	DiDi(0xe8)
DiLo('`')	DiHi('i')	DiDi(0xec)
DiLo('`')	DiHi('o')	DiDi(0xf2)
DiLo('`')	DiHi('u')	DiDi(0xf9)

DiLo('a')	DiHi('e')	DiDi(0xe6)
DiLo('a')	DiHi('~')	DiDi(0xe3)
DiLo('a')	DiHi(0xa8)	DiDi(0xe4)	/* Digital VT300 / VT400 */
DiLo('a')	DiHi(0xb0)	DiDi(0xe5)	/* Digital VT300 / VT400 */
DiLo('a')	DiHi(0xb4)	DiDi(0xe1)	/* Digital VT300 / VT400 */

DiLo('c')	DiHi(0xb8)	DiDi(0xe7)	/* Digital VT300 / VT400 */

DiLo('e')	DiHi(0xa8)	DiDi(0xeb)	/* Digital VT300 / VT400 */
DiLo('e')	DiHi(0xb4)	DiDi(0xe9)	/* Digital VT300 / VT400 */

DiLo('h')	DiHi('t')	DiDi(0xfe)	/* Digital VT300 / VT400 */

DiLo('i')	DiHi(0xa8)	DiDi(0xef)	/* Digital VT300 / VT400 */
DiLo('i')	DiHi(0xb4)	DiDi(0xed)	/* Digital VT300 / VT400 */

DiLo('n')	DiHi('~')	DiDi(0xf1)

DiLo('o')	DiHi('~')	DiDi(0xf5)
DiLo('o')	DiHi(0xa8)	DiDi(0xf6)	/* Digital VT300 / VT400 */
DiLo('o')	DiHi(0xb4)	DiDi(0xf3)	/* Digital VT300 / VT400 */

DiLo('s')	DiHi('s')	DiDi(0xdf)	/* Digital VT300 / VT400 */

DiLo('u')	DiHi(0xa8)	DiDi(0xfc)	/* Digital VT300 / VT400 */
DiLo('u')	DiHi(0xb4)	DiDi(0xfa)	/* Digital VT300 / VT400 */

DiLo('x')	DiHi('x')	DiDi(0xd7)	/* Digital VT300 / VT400 */

DiLo('y')	DiHi(0xa8)	DiDi(0xff)	/* Digital VT300 / VT400 */

DiLo('|')	DiHi('|')	DiDi(0xa6)	/* Digital VT300 / VT400 */

--- NEW FILE: ignore.c ---
/*
 * ignore.c: handles the ingore command for irc 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: ignore.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(ignore_c)
#include "struct.h"

#include "ignore.h"
#include "ircaux.h"
#include "list.h"
[...1049 lines suppressed...]
		 */
		if ((tmp->dont & dont_mask) != dont_mask)
			continue;

		/*
		 * Any "positive ignore" bits, if any, must be present,
		 * but there must not be a corresponding "negative ignore"
		 * bit for the levels as well.  That is to say, the
		 * negative ignore bits "turn off" any corresponding bits
		 * in the positive ignore set.
		 */
		if (((tmp->type & ~tmp->dont) & do_mask) != do_mask)
			continue;

		/* Add it to the fray */
		m_s3cat(&result, space, tmp->nick);
	}

	return result;
}

--- NEW FILE: pmbitchx.ico ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: fset.c ---
#include "irc.h"
static char cvsrevision[] = "$Id: fset.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(fset_c)
#include "struct.h"

#include "alist.h"
#include "ircaux.h"
#include "screen.h"
#include "hook.h"
#include "output.h"
#include "misc.h"
#include "list.h"
#include "vars.h"
#define MAIN_SOURCE
#include "modval.h"

extern void reinit_status (Window *, char *, int);

typedef struct _fset_number_ {
[...1048 lines suppressed...]
					bitchsay("Numeric %d is %s", num, tmp->format);
				return;
			}
		}
		last = tmp;
	}		
	if (!args || !*args)
	{
		if (verbose)
			bitchsay("No such Numeric Fset %d", num);
		return;
	}
	tmp = (FsetNumber *) new_malloc(sizeof(FsetNumber));
	tmp->numeric = num;
	tmp->format = m_strdup(args);
	add_to_list_ext((List **)&numeric_fset, (List *)tmp, compare_number);
	if (verbose)
		bitchsay("Added Numeric %d as %s", num, tmp->format);
}


--- NEW FILE: chelp.c ---
/*
 * Copyright Colten Edwards (c) 1996
 * BitchX help file system. 
 * When Chelp is called the help file is loaded from 
 * BitchX.help and saved. This file is never loaded from disk after this.
 * Information from the help file is loaded into an array as 0-Topic.
 * $help() also calls the same routines except this information is loaded 
 * differantly as 1-Topic. this allows us to distingush between them 
 * internally. 
 */
 
#include "irc.h"
static char cvsrevision[] = "$Id: chelp.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(chelp_c)
#include "struct.h"
#include "ircaux.h"
#include "chelp.h"
#include "output.h"
#include "hook.h"
#include "misc.h"
#include "vars.h"
#include "window.h"
#define MAIN_SOURCE
#include "modval.h"

#ifdef WANT_CHELP
int read_file (FILE *help_file, int helpfunc);
extern int in_cparse;
int in_chelp = 0;

typedef struct _chelp_struct {
	char *title;
	char **contents;
	char *relates;
} Chelp;

Chelp **help_index = NULL;
Chelp **script_help = NULL;

char *get_help_topic(char *args, int helpfunc)
{
char *new_comm = NULL;
int found = 0, i;
char *others = NULL;

	new_comm = LOCAL_COPY(args);

	for (i = 0; helpfunc ? script_help[i] : help_index[i]; i++)
	{
		if (!my_strnicmp(helpfunc?script_help[i]->title:help_index[i]->title, new_comm, strlen(new_comm)))
		{
			int j;
			char *text = NULL;
			if (found++)
			{
				m_s3cat(&others, " , ", helpfunc?script_help[i]->title:help_index[i]->title);
				continue;
			}
			if (args && *args && do_hook(HELPTOPIC_LIST, "%s", args))
				put_it("%s",convert_output_format("$G \002$0\002: Help on Topic: \002$1\002", version, args));
			for (j = 0; ; j++)
			{
				if (helpfunc && (script_help[i] && script_help[i]->contents[j]))
					text = script_help[i]->contents[j];
				else if (!helpfunc && (help_index[i] && help_index[i]->contents[j]))
					text = help_index[i]->contents[j];
				else 
					break;

				if (text && do_hook(HELPSUBJECT_LIST, "%s %s", new_comm, text))
				{
					in_chelp++;
					put_it("%s", convert_output_format(text, NULL));
					in_chelp--;
				}
			}		
			text = helpfunc ?script_help[i]->relates:help_index[i]->relates;
			if (text && do_hook(HELPTOPIC_LIST, "%s", text))
				put_it("%s", convert_output_format(text, NULL));
		}
		else if (found)
			break;
	}
	if (!found)
	{
		if (do_hook(HELPTOPIC_LIST, "%s", args))
			bitchsay("No help on %s", args);
	}

	if (others && found)
	{
		if (do_hook(HELPTOPIC_LIST, "%d %s", found, others))
			put_it("Other %d subjects: %s", found - 1, others);
	}
	new_free(&others);
	if (helpfunc)
		return m_strdup(empty_string);
	return NULL;
}

BUILT_IN_COMMAND(chelp)
{
static int first_time = 1;
	reset_display_target();
	if (args && *args == '-' && !my_strnicmp(args, "-dump", 4))
	{
		int i, j;
		next_arg(args, &args);
		first_time = 1;
		if (help_index)
		{
			for (i = 0; help_index[i]; i++)
			{
				if (help_index[i]->contents)
				{
					for (j =0; help_index[i]->contents[j]; j++)
						new_free(&help_index[i]->contents[j]);
				}
				new_free(&help_index[i]->contents);
				new_free(&help_index[i]->title);
				new_free(&help_index[i]->relates);
				new_free(&help_index[i]);
			}
			new_free(&help_index);
		}
	}
	if (first_time)
	{
		char *help_dir = NULL;
		FILE *help_file;
#ifdef PUBLIC_SYSTEM
		malloc_sprintf(&help_dir, "%s", DEFAULT_BITCHX_HELP_FILE);
#else
		malloc_sprintf(&help_dir, "%s", get_string_var(BITCHX_HELP_VAR));
#endif
		if (!(help_file = uzfopen(&help_dir, get_string_var(LOAD_PATH_VAR), 1)))
		{
			new_free(&help_dir);
			return;
		}
		new_free(&help_dir);
		first_time = 0;
		read_file(help_file, 0);
		fclose(help_file);
	}	
	if (!args || !*args)
	{
		userage(command, helparg);
		return;
	}
	get_help_topic(args, 0);
}

int read_file(FILE *help_file, int helpfunc)
{
char line[BIG_BUFFER_SIZE + 1];
char *topic = NULL;
char *subject = NULL;
int item_number = 0;
int topics = 0;
	fgets(line, sizeof(line)-1, help_file);
	if (line)
		line[strlen(line)-1] = '\0';
	while (!feof(help_file))
	{
		if (!line || !*line || *line == '#')
		{
			fgets(line, sizeof(line)-1, help_file);
			continue;
		}
		else if (*line && (*line != ' ')) /* we got a topic copy to topic */
		{
			topics++;
			item_number = 0;
			if (!my_strnicmp(line, "-RELATED", 7))
			{
				if (topic)
				{
					if (helpfunc)
						script_help[topics-1]->relates = m_strdup(line+8);
					else
						help_index[topics-1]->relates = m_strdup(line+8);
				}
			}
			else
			{	
				new_free(&topic); new_free(&subject);
				malloc_strcpy(&topic, line);
				if (helpfunc)
				{
					RESIZE(script_help, Chelp, topics+1);
					script_help[topics-1] = new_malloc(sizeof(Chelp));
					script_help[topics-1]->title = m_strdup(line);
				}
				else
				{
					RESIZE(help_index, Chelp, topics+1);
					help_index[topics-1] = new_malloc(sizeof(Chelp));
					help_index[topics-1]->title = m_strdup(line);
				}
			}
			fgets(line, sizeof(line)-1, help_file);
			if (line)
				line[strlen(line)-1] = '\0';
		}
		else if (topic && *topic)
		{ /* we found the subject material */
			do {
				if (!line || (line && *line != ' '))
					break;
				if (helpfunc)
				{
					RESIZE(script_help[topics-1]->contents, char **, ++item_number);
					script_help[topics-1]->contents[item_number-1] = m_strdup(line);
				}
				else
				{
					RESIZE(help_index[topics-1]->contents, char **, ++item_number);
					help_index[topics-1]->contents[item_number-1] = m_strdup(line);
				}
				fgets(line, sizeof(line)-1, help_file);
				if (line)
					line[strlen(line)-1] = '\0';
			} while (!feof(help_file));
		}
	}

	return 0;
}
#endif

--- NEW FILE: stack.c ---
/*
 * stack.c - does the handling of stack functions
 *
 * written by matthew green
 * finished by Jeremy Nelson (ESL)
 * modified Colten Edwards 1996 for BitchX
 * copyright (C) 1993.
 */

#include "irc.h"
static char cvsrevision[] = "$Id: stack.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(stack_c)
#include "struct.h"

#include "vars.h"
#include "stack.h"
#include "window.h"
#include "hook.h"
#include "ircaux.h"
#include "output.h"
#include "list.h"
#include "misc.h"
#define MAIN_SOURCE
#include "modval.h"

	AliasStack1	*set_stack = NULL;
extern void do_stack_set (int, char *);

#ifdef OLD_HOOK
static	OnStack		*on_stack = NULL;

extern void do_stack_on (int, char *);

extern int Add_Remove_Check (Hook *, char *);

static	void	do_stack_on (int type, char *args)
{
	int	len, cnt, i, which = 0;
	Hook	*list = NULL;
	NumberMsg *nhook = NULL;
	char foo[10];

	if (!on_stack && (type == STACK_POP || type == STACK_LIST))
	{
		say("ON stack is empty!");
		return;
	}
	if (!args || !*args)
	{
		say("Missing event type for STACK ON");
		return;
	}
	len = strlen(args);
	for (cnt = 0, i = 0; i < NUMBER_OF_LISTS; i++)
	{
		if (!my_strnicmp(args, hook_functions[i].name, len))
		{
			if (strlen(hook_functions[i].name) == len)
			{
				cnt = 1;
				which = i;
				break;
			}
			else
			{
				cnt++;
				which = i;
			}
		}
		else if (cnt)
			break;
	}
	if (!cnt)
	{
		if (is_number(args))
		{
			which = my_atol(args);
			if (which < 1 || which > 999)
			{
				say("Numerics must be between 001 and 999");
				return;
			}
			which = -which;
		}
		else
		{
			say("No such ON function: %s", args);
			return;
		}
	}
	if (which < 0)
	{
		sprintf(foo, "%3.3u", -which);
		if ((nhook = find_name_in_hooklist(foo, numeric_list, 0, HOOKTABLE_SIZE)))
			list = nhook->list;
	}
	else
		list = hook_functions[which].list;

	if (type == STACK_PUSH)
	{
		OnStack	*new;

		new = (OnStack *) new_malloc(sizeof(OnStack));
		new->next = on_stack;
		on_stack = new;
		new->which = which;
		new->list = list;
		if (which < 0)
		{
			
			if (!nhook)
				return;
			nhook = find_name_in_hooklist(foo, numeric_list, 1, HOOKTABLE_SIZE);
			new_free(&nhook->name);
			new_free(&nhook);
		}
		else
			hook_functions[which].list = NULL;
		return;
	}
	else if (type == STACK_POP)
	{
		OnStack	*p, *tmp = NULL;

		for (p = on_stack; p; tmp = p, p = p->next)
		{
			if (p->which == which)
			{
				if (p == on_stack)
					on_stack = p->next;
				else
					tmp->next = p->next;
				break;
			}
		}
		if (!p)
		{
			say("No %s on the stack", args);
			return;
		}
		if (which >= 0 && hook_functions[which].list)
			remove_hook(which, NULL, 0, 0, 1);	/* free hooks */
		else if (which < 0 && nhook)
		{
			Hook *tmp, *next;
			if ((nhook = find_name_in_hooklist(foo, numeric_list, 1, HOOKTABLE_SIZE)))
			{
				new_free(&nhook->name);
				for (tmp = nhook->list; tmp; tmp = next)
				{
					next = tmp->next;
					tmp->not = 1;
					new_free(&(tmp->nick));
					new_free(&(tmp->stuff));
					new_free((char **)&tmp);
				}
				new_free(&nhook);
			}
		}
		if (which < 0)
		{
			/* look -- do we have any hooks already for this numeric? */
			if (p->list)
			{
				sprintf(foo, "%3.3u", -which);
				nhook = add_name_to_hooklist(foo, NULL, numeric_list, HOOKTABLE_SIZE);
				add_to_list_ext((List **)&nhook->list, (List *)p->list, (int (*)(List *, List *))Add_Remove_Check);
			}
		}
		else
			hook_functions[which].list = p->list;
		new_free((char **)&p);
		return;
	}
	else if (type == STACK_LIST)
	{
		int	slevel = 0;
		OnStack	*osptr;

		for (osptr = on_stack; osptr; osptr = osptr->next)
		{
			if (osptr->which == which)
			{
				Hook	*hptr;

				slevel++;
				say("Level %d stack", slevel);
				for (hptr = osptr->list; hptr; hptr = hptr->next)
					show_hook(hptr, args);
			}
		}
		
		if (!slevel)
			say("The STACK ON %s list is empty", args);
		return;
	}
	say("Unknown STACK ON type ??");
}
#endif

BUILT_IN_COMMAND(stackcmd)
{
	char	*arg;
	int	len, type;

	if ((arg = next_arg(args, &args)) != NULL)
	{
		len = strlen(arg);
		if (!my_strnicmp(arg, "PUSH", len))
			type = STACK_PUSH;
		else if (!my_strnicmp(arg, "POP", len))
			type = STACK_POP;
		else if (!my_strnicmp(arg, "LIST", len))
			type = STACK_LIST;
		else
		{
			say("%s is unknown stack verb", arg);
			return;
		}
	}
	else
		return;
	if ((arg = next_arg(args, &args)) != NULL)
	{
		len = strlen(arg);
		if (!my_strnicmp(arg, "ON", len))
			do_stack_on(type, args);
		else if (!my_strnicmp(arg, "ALIAS", len))
			do_stack_alias(type, args, STACK_DO_ALIAS);
		else if (!my_strnicmp(arg, "ASSIGN", len))
			do_stack_alias(type, args, STACK_DO_ASSIGN);
		else if (!my_strnicmp(arg, "SET", len))
			do_stack_set(type, args);
		else
		{
			say("%s is not a valid STACK type", arg);
			return;
		}
	}
}

--- NEW FILE: glob.c ---
#include "defs.h"
#include "config.h"

#if defined(NEED_GLOB)
/*
 * Copyright (c) 1989, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Guido van Rossum.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* from: static char sccsid[] = "@(#)glob.c	8.3 (Berkeley) 10/13/93"; */
/* $Id: glob.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $ */

/*
 * glob(3) -- a superset of the one defined in POSIX 1003.2.
 *
 * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
 *
 * Optional extra services, controlled by flags not defined by POSIX:
 *
 * GLOB_QUOTE:
 *	Escaping convention: \ inhibits any special meaning the following
 *	character might have (except \ at end of string is retained).
 * GLOB_MAGCHAR:
 *	Set in gl_flags if pattern contained a globbing character.
 * GLOB_NOMAGIC:
 *	Same as GLOB_NOCHECK, but it will only append pattern if it did
 *	not contain any magic characters.  [Used in csh style globbing]
 * GLOB_ALTDIRFUNC:
 *	Use alternately specified directory access functions.
 * GLOB_TILDE:
 *	expand ~user/foo to the /home/dir/of/user/foo
 * GLOB_BRACE:
 *	expand {1,2}{a,b} to 1a 1b 2a 2b 
 * gl_matchc:
 *	Number of matches in the current invocation of glob.
 */

#include <sys/param.h>
#ifdef __EMX__
#include <sys/types.h>
#endif
#include <sys/stat.h>

#include <ctype.h>
#include <dirent.h>
#include <errno.h>

#include "bsdglob.h"
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "ircaux.h"
#define MAIN_SOURCE
#include "modval.h"

#ifndef MAXPATHLEN
#define MAXPATHLEN PATHLEN
#endif

#undef EOS

#define	DOLLAR		'$'
#define	DOT		'.'
#define	EOS		'\0'
#define	LBRACKET	'['
#define	NOT		'!'
#define	QUESTION	'?'
#define	QUOTE		'\\'
#define	RANGE		'-'
#define	RBRACKET	']'
#define	SEP		'/'
#define	STAR		'*'
#define	TILDE		'~'
#define	UNDERSCORE	'_'
#define	LBRACE		'{'
#define	RBRACE		'}'
#define	SLASH		'/'
#define	COMMA		','

#define	M_QUOTE		0x8000
#define	M_PROTECT	0x4000
#define M_ANYCASE       0x2000
#define	M_MASK		0xffff
#define	M_ASCII		0x00ff

#if 0
#ifdef ULTRIX
#define S_IFLNK         0120000        /* symbolic link */
#define S_ISLNK( mode )         (((mode) & _S_IFMT) == S_IFLNK)
#endif
#endif
                    
typedef u_short Char;

#define	CHAR(c)		((Char)((c)&M_ASCII))
#define	META(c)		((Char)((c)|M_QUOTE))
#define	M_ALL		META('*')
#define	M_END		META(']')
#define	M_NOT		META('!')
#define	M_ONE		META('?')
#define	M_RNG		META('-')
#define	M_SET		META('[')
#define	ismeta(c)	(((c)&M_QUOTE) != 0)


static int	 	compare (const void *, const void *);
static void	 	g_Ctoc (const Char *, char *);
static int	 	g_lstat (Char *, struct stat *, glob_t *);
static DIR	*	g_opendir (Char *, glob_t *);
static Char	*	g_strchr (Char *, int);
#ifdef S_ISLNK
static int	 	g_stat (Char *, struct stat *, glob_t *);
#endif
static int	 	glob0 (const Char *, glob_t *);
static int	 	glob1 (Char *, glob_t *);
static int	 	glob2 (Char *, Char *, Char *, glob_t *);
static int	 	glob3 (Char *, Char *, Char *, Char *, glob_t *);
static int	 	globextend (const Char *, glob_t *);
static const Char *	globtilde (const Char *, Char *, glob_t *);
static int	 	globexp1 (const Char *, glob_t *);
static int	 	globexp2 (const Char *, const Char *, glob_t *, int *);
static int	 	match (Char *, Char *, Char *, int);

int BX_bsd_glob		(	const char *pattern,
				int flags, 
				int (*errfunc) (const char *, int),
				glob_t *pglob				)
{
	const u_char *patnext;
	int c;
	Char *bufnext, *bufend, patbuf[MAXPATHLEN+1];

#if defined(__EMX__) || defined(WINNT)
	strlwr((char *)pattern);
#endif
	patnext = (u_char *) pattern;
	if (!(flags & GLOB_APPEND)) {
		pglob->gl_pathc = 0;
		pglob->gl_pathv = NULL;
		if (!(flags & GLOB_DOOFFS))
			pglob->gl_offs = 0;
	}
	pglob->gl_flags = flags & ~GLOB_MAGCHAR;
	pglob->gl_errfunc = errfunc;
	pglob->gl_matchc = 0;

	bufnext = patbuf;
	bufend = bufnext + MAXPATHLEN;
	if (flags & GLOB_QUOTE) {
		/* Protect the quoted characters. */
		while (bufnext < bufend && (c = *patnext++) != EOS) 
			if (c == QUOTE) {
				if ((c = *patnext++) == EOS) {
					c = QUOTE;
					--patnext;
				}
				*bufnext++ = c | M_PROTECT;
			}
			else
				*bufnext++ = c;
	}
	else 
	    while (bufnext < bufend && (c = *patnext++) != EOS) 
		    *bufnext++ = c;
	*bufnext = EOS;

	if (flags & GLOB_BRACE)
	    return globexp1(patbuf, pglob);
	else
	    return glob0(patbuf, pglob);
}

/*
 * Expand recursively a glob {} pattern. When there is no more expansion
 * invoke the standard globbing routine to glob the rest of the magic
 * characters
 */
static int globexp1	(	const Char *pattern,
				glob_t *pglob			)
{
	const Char* ptr = pattern;
	int rv;

	/* Protect a single {}, for find(1), like csh */
	if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
		return glob0(pattern, pglob);

	while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL)
		if (!globexp2(ptr, pattern, pglob, &rv))
			return rv;

	return glob0(pattern, pglob);
}


/*
 * Recursive brace globbing helper. Tries to expand a single brace.
 * If it succeeds then it invokes globexp1 with the new pattern.
 * If it fails then it tries to glob the rest of the pattern and returns.
 */
static int globexp2	(	const Char *ptr,
				const Char *pattern,
				glob_t *pglob,
				int *rv				)
{
	int     i;
	Char   *lm, *ls;
	const Char *pe, *pm, *pl;
	Char    patbuf[MAXPATHLEN + 1];

	/* copy part up to the brace */
	for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
		continue;
	ls = lm;

	/* Find the balanced brace */
	for (i = 0, pe = ++ptr; *pe; pe++)
		if (*pe == LBRACKET) 
		{
			/* Ignore everything between [] */
			for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
				continue;
			if (*pe == EOS) 
			{
				/* 
				 * We could not find a matching RBRACKET.
				 * Ignore and just look for RBRACE
				 */
				pe = pm;
			}
		}
		else if (*pe == LBRACE)
			i++;
		else if (*pe == RBRACE) 
		{
			if (i == 0)
				break;
			i--;
		}

	/* Non matching braces; just glob the pattern */
	if (i != 0 || *pe == EOS) 
	{
		*rv = glob0(patbuf, pglob);
		return 0;
	}

	for (i = 0, pl = pm = ptr; pm <= pe; pm++)
		switch (*pm) 
		{
		case LBRACKET:
			/* Ignore everything between [] */
			for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++)
				continue;
			if (*pm == EOS) 
			{
				/* 
				 * We could not find a matching RBRACKET.
				 * Ignore and just look for RBRACE
				 */
				pm = pl;
			}
			break;

		case LBRACE:
			i++;
			break;

		case RBRACE:
			if (i) 
			{
			    i--;
			    break;
			}
			/* FALLTHROUGH */
		case COMMA:
			if (i && *pm == COMMA)
				break;
			else 
			{
				/* Append the current string */
				for (lm = ls; (pl < pm); *lm++ = *pl++)
					continue;
				/* 
				 * Append the rest of the pattern after the
				 * closing brace
				 */
				for (pl = pe + 1; (*lm++ = *pl++) != EOS;)
					continue;

				/* Expand the current pattern */
				*rv = globexp1(patbuf, pglob);

				/* move after the comma, to the next string */
				pl = pm + 1;
			}
			break;

		default:
			break;
		}
	*rv = 0;
	return 0;
}



/*
 * expand tilde from the passwd file.
 */
static const Char *globtilde	(	const Char *pattern,
					Char *patbuf,
					glob_t *pglob		)
{
	struct passwd *pwd;
	char *h;
	const Char *p;
	Char *b;

	if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE))
		return pattern;

	/* Copy up to the end of the string or / */
	for (p = pattern + 1, h = (char *) patbuf; *p && *p != SLASH; 
	     *h++ = *p++)
		continue;

	*h = EOS;

	if (((char *) patbuf)[0] == EOS) {
		/* 
		 * handle a plain ~ or ~/ by expanding $HOME 
		 * first and then trying the password file
		 */
		if ((h = getenv("HOME")) == NULL) {
			if ((pwd = getpwuid(getuid())) == NULL)
				return pattern;
			else
				h = pwd->pw_dir;
		}
	}
	else {
		/*
		 * Expand a ~user
		 */
		if ((pwd = getpwnam((char*) patbuf)) == NULL)
			return pattern;
		else
			h = pwd->pw_dir;
	}

	/* Copy the home directory */
	for (b = patbuf; *h; *b++ = *h++)
		continue;
	
	/* Append the rest of the pattern */
	while ((*b++ = *p++) != EOS)
		continue;
#if defined(__EMX__) || defined(WINNT)
	convert_unix((char *)patbuf);
#endif
	return patbuf;
}
	

/*
 * The main glob() routine: compiles the pattern (optionally processing
 * quotes), calls glob1() to do the real pattern matching, and finally
 * sorts the list (unless unsorted operation is requested).  Returns 0
 * if things went well, nonzero if errors occurred.  It is not an error
 * to find no matches.
 */
static int glob0		(	const Char *pattern,
					glob_t *pglob		)
{
	const Char *qpatnext;
	int c, err, oldpathc;
	Char *bufnext, patbuf[MAXPATHLEN+1];

	qpatnext = globtilde(pattern, patbuf, pglob);
	oldpathc = pglob->gl_pathc;
	bufnext = patbuf;

	/* We don't need to check for buffer overflow any more. */
	while ((c = *qpatnext++) != EOS) 
	{
		switch (c) 
		{
		case LBRACKET:
			c = *qpatnext;
			if (c == NOT)
				++qpatnext;
			if (*qpatnext == EOS || g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) 
			{
				*bufnext++ = LBRACKET;
				if (c == NOT)
					--qpatnext;
				break;
			}
			*bufnext++ = M_SET;
			if (c == NOT)
				*bufnext++ = M_NOT;
			c = *qpatnext++;
			do 
			{
				*bufnext++ = CHAR(c);
				if (*qpatnext == RANGE && (c = qpatnext[1]) != RBRACKET) 
				{
					*bufnext++ = M_RNG;
					*bufnext++ = CHAR(c);
					qpatnext += 2;
				}
			} 
			while ((c = *qpatnext++) != RBRACKET);

			pglob->gl_flags |= GLOB_MAGCHAR;
			*bufnext++ = M_END;
			break;
		case QUESTION:
			pglob->gl_flags |= GLOB_MAGCHAR;
			*bufnext++ = M_ONE;
			break;
		case STAR:
			pglob->gl_flags |= GLOB_MAGCHAR;
			/* collapse adjacent stars to one, 
			 * to avoid exponential behavior
			 */
			if (bufnext == patbuf || bufnext[-1] != M_ALL)
			    *bufnext++ = M_ALL;
			break;
		default:
			*bufnext++ = CHAR(c);
			break;
		}
	}
	*bufnext = EOS;

	if ((err = glob1(patbuf, pglob)) != 0)
		return(err);

	/*
	 * If there was no match we are going to append the pattern 
	 * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
	 * and the pattern did not contain any magic characters
	 * GLOB_NOMAGIC is there just for compatibility with csh.
	 */
	if (pglob->gl_pathc == oldpathc && 
	    ((pglob->gl_flags & GLOB_NOCHECK) || 
	      ((pglob->gl_flags & GLOB_NOMAGIC) &&
	       !(pglob->gl_flags & GLOB_MAGCHAR))))
		return(globextend(pattern, pglob));
	else if (!(pglob->gl_flags & GLOB_NOSORT)) 
	{
		if (pglob->gl_pathv)
			qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
			    pglob->gl_pathc - oldpathc, sizeof(char *), compare);
	}
	return(0);
}

static int compare		(	const void *p,
					const void *q		)
{
	return(strcmp(*(char **)p, *(char **)q));
}

static int glob1		(	Char *pattern,
					glob_t *pglob		)
{
	Char pathbuf[MAXPATHLEN+1];

	/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
	if (*pattern == EOS)
		return(0);
	return(glob2(pathbuf, pathbuf, pattern, pglob));
}

/*
 * The functions glob2 and glob3 are mutually recursive; there is one level
 * of recursion for each segment in the pattern that contains one or more
 * meta characters.
 */
static int glob2		(	Char *pathbuf,
					Char *pathend,
					Char *pattern,
					glob_t *pglob		)
{
	struct stat sb;
	Char *p, *q;
	int anymeta;

	/*
	 * Loop over pattern segments until end of pattern or until
	 * segment with meta character found.
	 */
	for (anymeta = 0;;) 
	{
		if (*pattern == EOS)		/* End of pattern? */
		{
			*pathend = EOS;
			if (g_lstat(pathbuf, &sb, pglob))
				return(0);
		
			if (((pglob->gl_flags & GLOB_MARK) &&
			    pathend[-1] != SEP) && (S_ISDIR(sb.st_mode)
#ifdef S_ISLNK
			    || (S_ISLNK(sb.st_mode) &&
			    (g_stat(pathbuf, &sb, pglob) == 0) &&
			    S_ISDIR(sb.st_mode))
#endif
			    )) 
			{
				*pathend++ = SEP;
				*pathend = EOS;
			}
			++pglob->gl_matchc;
			return(globextend(pathbuf, pglob));
		}

		/* Find end of next segment, copy tentatively to pathend. */
		q = pathend;
		p = pattern;
		while (*p != EOS && *p != SEP) 
		{
			if (ismeta(*p))
				anymeta = 1;
			*q++ = *p++;
		}

		if (!anymeta)		/* No expansion, do next segment. */
		{
			pathend = q;
			pattern = p;
			while (*pattern == SEP)
				*pathend++ = *pattern++;
		} else			/* Need expansion, recurse. */
			return(glob3(pathbuf, pathend, pattern, p, pglob));
	}
	/* NOTREACHED */
}

static int glob3		(	Char *pathbuf,
					Char *pathend,
					Char *pattern,
					Char *restpattern,
					glob_t *pglob			)
{
	register struct dirent *dp;
	DIR *dirp;
	int err;
	char buf[MAXPATHLEN];
	int nocase = 0;
	
	/*
	 * The readdirfunc declaration can't be prototyped, because it is
	 * assigned, below, to two functions which are prototyped in glob.h
	 * and dirent.h as taking pointers to differently typed opaque
	 * structures.
	 */
	struct dirent *(*readdirfunc)();

	*pathend = EOS;
	errno = 0;
	    
	if ((dirp = g_opendir(pathbuf, pglob)) == NULL) 
	{
		/* TODO: don't call for ENOENT or ENOTDIR? */
		if (pglob->gl_errfunc) 
		{
			g_Ctoc(pathbuf, buf);
			if (pglob->gl_errfunc(buf, errno) ||
			    pglob->gl_flags & GLOB_ERR)
				return (GLOB_ABEND);
		}
		return(0);
	}

	err = 0;

	/* Search directory for matching names. */
	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
		readdirfunc = pglob->gl_readdir;
	else
		readdirfunc = readdir;


	if (pglob->gl_flags & GLOB_INSENSITIVE)
		nocase = 1;

	while ((dp = (*readdirfunc)(dirp))) 
	{
		register u_char *sc;
		register Char *dc;

#if defined(__EMX__) || defined(WINNT)
		if(dp->d_name && *(dp->d_name))
			strlwr(dp->d_name);
#endif

		/* Initial DOT must be matched literally. */
		if (dp->d_name[0] == DOT && *pattern != DOT)
			continue;
		for (sc = (u_char *) dp->d_name, dc = pathend; 
		     (*dc++ = *sc++) != EOS;)
			continue;
		if (!match(pathend, pattern, restpattern, nocase)) 
		{
			*pathend = EOS;
			continue;
		}
		err = glob2(pathbuf, --dc, restpattern, pglob);
		if (err)
			break;
	}

	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
		(*pglob->gl_closedir)(dirp);
	else
		closedir(dirp);
	return(err);
}


/*
 * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
 * add the new item, and update gl_pathc.
 *
 * This assumes the BSD realloc, which only copies the block when its size
 * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
 * behavior.
 *
 * Return 0 if new item added, error code if memory couldn't be allocated.
 *
 * Invariant of the glob_t structure:
 *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
 *	gl_pathv points to (gl_offs + gl_pathc + 1) items.
 */
static int globextend		(	const Char *path,
					glob_t *pglob			)
{
	register char **pathv;
	register int i;
	u_int newsize;
	char *copy;
	const Char *p;

	newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
	pathv = pglob->gl_pathv ? 
		    (char **)realloc((char *)pglob->gl_pathv, newsize) :
		    (char **)malloc(newsize);
	if (pathv == NULL)
		return(GLOB_NOSPACE);

	if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) 
	{
		/* first time around -- clear initial gl_offs items */
		pathv += pglob->gl_offs;
		for (i = pglob->gl_offs; --i >= 0; )
			*--pathv = NULL;
	}
	pglob->gl_pathv = pathv;

	for (p = path; *p++;)
		continue;
	if ((copy = malloc(p - path)) != NULL) 
	{
		g_Ctoc(path, copy);
		pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
	}
	pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
	return(copy == NULL ? GLOB_NOSPACE : 0);
}


/*
 * pattern matching function for filenames.  Each occurrence of the *
 * pattern causes a recursion level.
 */
static int match		(	register Char *name,
					register Char *pat,
					register Char *patend,
					int nocase	)
{
	int ok, negate_range;
	Char c, k;

	while (pat < patend) 
	{
		c = *pat++;
		switch (c & M_MASK) 
		{
		case M_ALL:
			if (pat == patend)
				return(1);
			do 
			    if (match(name, pat, patend, nocase))
				    return(1);
			while (*name++ != EOS);
			return(0);
		case M_ONE:
			if (*name++ == EOS)
				return(0);
			break;
		case M_SET:
			ok = 0;
			if ((k = *name++) == EOS)
				return(0);
			if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
				++pat;
			while (((c = *pat++) & M_MASK) != M_END)
			{
				if ((*pat & M_MASK) == M_RNG) 
				{
					if (c <= k && k <= pat[1])
						ok = 1;
					pat += 2;
				} else if (c == k)
					ok = 1;
			}
			if (ok == negate_range)
				return(0);
			break;
		default:
			if (nocase)
			{
				if (toupper(CHAR(*name++)) != toupper(CHAR(c)))
					return 0;
			}
			else
			{
				if (*name++ != c)
					return 0;
			}
			break;
		}
	}
	return(*name == EOS);
}

/* Free allocated data belonging to a glob_t structure. */
void BX_bsd_globfree 		(	glob_t *pglob			)
{
	register int i;
	register char **pp;

	if (pglob->gl_pathv != NULL) {
		pp = pglob->gl_pathv + pglob->gl_offs;
		for (i = pglob->gl_pathc; i--; ++pp)
			if (*pp)
				free(*pp);
		free(pglob->gl_pathv);
	}
}

static DIR *g_opendir		(	register Char *str,
					glob_t *pglob			)
{
	char buf[MAXPATHLEN];

	if (!*str)
		strcpy(buf, ".");
	else
		g_Ctoc(str, buf);

	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
		return((*pglob->gl_opendir)(buf));

	return(opendir(buf));
}

static int g_lstat		(	register Char *fn,
					struct stat *sb,
					glob_t *pglob			)
{
	char buf[MAXPATHLEN];

	g_Ctoc(fn, buf);
	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
		return((*pglob->gl_lstat)(buf, sb));
#if defined(__EMX__) || defined(__OPENNT)
	return(stat(buf, sb));
#else
	return(lstat(buf, sb));
#endif
}

#ifdef S_ISLNK
static int g_stat		(	register Char *fn,
					struct stat *sb,
					glob_t *pglob			)
{
	char buf[MAXPATHLEN];

	g_Ctoc(fn, buf);
	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
		return((*pglob->gl_stat)(buf, sb));
	return(stat(buf, sb));
}
#endif

static Char *g_strchr		(	Char *str, 
					int ch				)
{
	do {
		if (*str == ch)
			return (str);
	} while (*str++);
	return (NULL);
}

static void g_Ctoc		(	register const Char *str,
					char *buf			)
{
	register char *dc;

	for (dc = buf; (*dc++ = *str++) != EOS;)
		continue;
}
#endif

--- NEW FILE: pmbitchx.rc ---
#define INCL_WINSYS
#define INCL_WINSTDDLGS
#define INCL_WINSTDSPIN
#define INCL_NLS
#define INCL_SW

#include <os2.h>
#include "..\include\pmbitchx.h"

ICON    IDM_MAINMENU    "pmbitchx.ico"
BITMAP  IDM_LOGO        "..\\doc\\bitch52.bmp"

DLGTEMPLATE FONTDIALOG 
BEGIN
    DIALOG "Change font", FONTDIALOG, 160, 170, 124, 143, FS_NOBYTEALIGN |
                FS_DLGBORDER | WS_CLIPSIBLINGS | WS_SAVEBITS | WS_VISIBLE,
                FCF_TITLEBAR | FCF_SYSMENU
	BEGIN
        CONTROL "OK",                  DID_OK, 9, 6, 51, 14, WC_BUTTON,
                                            BS_PUSHBUTTON | BS_DEFAULT |
                                            WS_TABSTOP | WS_VISIBLE
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "Cancel",              DID_CANCEL, 63, 6, 51, 14, WC_BUTTON,
                                            BS_PUSHBUTTON | WS_TABSTOP |
                                            WS_VISIBLE
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "Sample",              LB_FONTLIST, 5, 53, 113, 85, WC_LISTBOX,
                                            WS_GROUP | WS_TABSTOP | WS_VISIBLE
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "Save as default font", CB_DEFAULT, 9, 38, 108, 10, WC_BUTTON,
                                            BS_AUTOCHECKBOX | WS_GROUP |
                                            WS_TABSTOP | WS_VISIBLE
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "Change for all windows", CB_CHANGEFORALL, 9, 25, 107, 10,
                                            WC_BUTTON, WS_TABSTOP | WS_TABSTOP |
                                            BS_AUTOCHECKBOX | WS_VISIBLE
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
	END
END

DLGTEMPLATE PROPNBK 
BEGIN
    DIALOG "PMBitchX Properties", PROPNBK, 54, 121, 306, 149, FS_NOBYTEALIGN |
                FS_DLGBORDER | WS_CLIPSIBLINGS | WS_SAVEBITS | WS_VISIBLE,
                FCF_TITLEBAR | FCF_SYSMENU
    PRESPARAMS PP_USER, "9.WarpSans Bold"
    BEGIN
        CONTROL "",                    ID_PROP, 4, 24, 298, 120, WC_NOTEBOOK,
                                            BKS_BACKPAGESTR | BKS_MAJORTABTOP |
                                            BKS_ROUNDEDTABS |  0x800 |
                                            BKS_STATUSTEXTLEFT |
                                            BKS_TABTEXTCENTER | BKS_SOLIDBIND |
                                            WS_TABSTOP | WS_VISIBLE
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans Bold"
        DEFPUSHBUTTON "Dismiss",               DID_OK, 130, 5, 38, 12
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"

    END
END

DLGTEMPLATE NBKP_SETS1
BEGIN
    DIALOG "", NBKP_SETS1, 0, 0, 275, 74, FS_NOBYTEALIGN | NOT FS_DLGBORDER
    PRESPARAMS PP_USER, "9.WarpSans Bold"
    BEGIN
        CONTROL "",                    SETS1, 10, 12, 113, 58, WC_LISTBOX,
                                            LS_NOADJUSTPOS | WS_VISIBLE
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "Entry",               EF_ENTRY1, 140, 51, 103, 10,
                                            WC_ENTRYFIELD, ES_MARGIN | ES_AUTOSCROLL |
                                            WS_TABSTOP | WS_VISIBLE | WS_DISABLED
                CTLDATA 8, 1023, 0, 0
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "On/Off",              CB_ONOFF1, 140, 21, 38, 10, WC_BUTTON,
                                            BS_AUTOCHECKBOX | WS_TABSTOP |
                                            WS_VISIBLE | WS_DISABLED
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
    END
END

DLGTEMPLATE NBKP_SETS2
BEGIN
    DIALOG "", NBKP_SETS2, 0, 0, 275, 74, FS_NOBYTEALIGN | NOT FS_DLGBORDER
    PRESPARAMS PP_USER, "9.WarpSans Bold"
    BEGIN
        CONTROL "",                    SETS2, 10, 12, 113, 58, WC_LISTBOX,
                                            LS_NOADJUSTPOS | WS_VISIBLE
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "Entry",               EF_ENTRY2, 140, 51, 103, 10,
                                            WC_ENTRYFIELD, ES_MARGIN | ES_AUTOSCROLL |
                                            WS_TABSTOP | WS_VISIBLE | WS_DISABLED
                CTLDATA 8, 1023, 0, 0
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "On/Off",              CB_ONOFF2, 140, 21, 38, 10, WC_BUTTON,
                                            BS_AUTOCHECKBOX | WS_TABSTOP |
                                            WS_VISIBLE | WS_DISABLED
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
    END
END

DLGTEMPLATE NBKP_SETS3
BEGIN
    DIALOG "", NBKP_SETS3, 0, 0, 275, 74, FS_NOBYTEALIGN | NOT FS_DLGBORDER
    PRESPARAMS PP_USER, "9.WarpSans Bold"
    BEGIN
        CONTROL "",                    SETS3, 10, 12, 113, 58, WC_LISTBOX,
                                            LS_NOADJUSTPOS | WS_VISIBLE
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "Entry",               EF_ENTRY3, 140, 51, 103, 10,
                                            WC_ENTRYFIELD, ES_MARGIN | ES_AUTOSCROLL |
                                            WS_TABSTOP | WS_VISIBLE | WS_DISABLED
                CTLDATA 8, 1023, 0, 0
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "On/Off",              CB_ONOFF3, 140, 21, 38, 10, WC_BUTTON,
                                            BS_AUTOCHECKBOX | WS_TABSTOP |
                                            WS_VISIBLE | WS_DISABLED
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
    END
END

DLGTEMPLATE NBKP_SETS4
BEGIN
    DIALOG "", NBKP_SETS4, 0, 0, 275, 74, FS_NOBYTEALIGN | NOT FS_DLGBORDER
    PRESPARAMS PP_USER, "9.WarpSans Bold"
    BEGIN
        CONTROL "",                    SETS4, 10, 12, 113, 58, WC_LISTBOX,
                                            LS_NOADJUSTPOS | WS_VISIBLE
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "Entry",               EF_ENTRY4, 140, 51, 103, 10,
                                            WC_ENTRYFIELD, ES_MARGIN | ES_AUTOSCROLL |
                                            WS_TABSTOP | WS_VISIBLE | WS_DISABLED
                CTLDATA 8, 1023, 0, 0
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "On/Off",              CB_ONOFF4, 140, 21, 38, 10, WC_BUTTON,
                                            BS_AUTOCHECKBOX | WS_TABSTOP |
                                            WS_VISIBLE | WS_DISABLED
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
    END
END

DLGTEMPLATE NBKP_OSSETTINGS
BEGIN
    DIALOG "", NBKP_OSSETTINGS, 0, 0, 275, 74, FS_NOBYTEALIGN |
                NOT FS_DLGBORDER
    PRESPARAMS PP_USER, "9.WarpSans Bold"
    BEGIN
        CONTROL "Long Filenames",   CB_LFN, 20, 58, 97, 10, WC_BUTTON,
                                            BS_CHECKBOX | WS_TABSTOP |
                                            WS_VISIBLE | WS_DISABLED
                PRESPARAMS PP_FONTNAMESIZE, "9.WarpSans"
        CONTROL "Sound",          CB_SOUND, 20, 45, 54, 10, WC_BUTTON,
                                            BS_CHECKBOX | WS_TABSTOP |
                                            WS_VISIBLE | WS_DISABLED
    END
END

--- NEW FILE: numbers.c ---
/*
 * numbers.c:handles all those strange numeric response dished out by that
 * wacky, nutty program we call ircd 
 *
 *
 * written by michael sandrof
 *
 * copyright(c) 1990 
 *
 * see the copyright file, or do a help ircii copyright 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: numbers.c,v 1.1.1.3 2003/06/11 07:00:43 root Exp $";
CVS_REVISION(numbers_c)
#include "struct.h"

#include "dcc.h"
[...1595 lines suppressed...]
			}
		case 369:		/* #define RPL_ENDOFWHOWAS      369 */
		case 374:		/* #define RPL_ENDOFINFO        374 */
		case 394:		/* #define RPL_ENDOFUSERS       394 */
		{
			int hook;
			PasteArgs(ArgList, 0);
			hook = do_hook(current_numeric, "%s %s", from, *ArgList);
			if (hook && get_int_var(SHOW_END_OF_MSGS_VAR))
				display_msg(from, ArgList);
			break;
		}
		default:
			display_msg(from, ArgList);
		}
	}
	current_numeric = old_current_numeric;
	reset_display_target();
}


--- NEW FILE: newio.c ---
/*
 * newio.c: This is some handy stuff to deal with file descriptors in a way
 * much like stdio's FILE pointers 
 *
 * IMPORTANT NOTE:  If you use the routines here-in, you shouldn't switch to
 * using normal reads() on the descriptors cause that will cause bad things
 * to happen.  If using any of these routines, use them all 
 *
 * Copyright 1990 Michael Sandrof
 * Copyright 1995 Matthew Green
 * Copyright 1997 EPIC Software Labs
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#include "irc.h"
static char cvsrevision[] = "$Id: newio.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(newio_c)
#include "ircaux.h"
#include "output.h"

#include <sys/ioctl.h>

#ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif
#ifdef WIN32
#include "winbitchx.h"
#endif

#define MAIN_SOURCE
#include "modval.h"

#if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX) && !defined(__EMX__)
# define IO_ARRAYLEN sysconf(_SC_OPEN_MAX)
#else
# ifdef FD_SETSIZE
#  define IO_ARRAYLEN FD_SETSIZE
# else
#  define IO_ARRAYLEN NFDBITS
# endif
#endif

#ifdef __GNU__
#undef IO_ARRAYLEN
#define IO_ARRAYLEN 65535
#endif

#define MAX_SEGMENTS 16

typedef	struct	myio_struct
{
	char		*buffer;
	size_t		buffer_size;
	unsigned 	read_pos,
			write_pos;
	int		segments;
	int		error;
}           MyIO;

static	MyIO	**io_rec = NULL;
	int	dgets_errno = 0;

/*
 * Get_pending_bytes: What do you think it does?
 */
size_t get_pending_bytes (int fd)
{
	if (fd >= 0 && io_rec[fd] && io_rec[fd]->buffer)
		return strlen(io_rec[fd]->buffer);

	return 0;
}

static	void	init_io (void)
{
	static	int	first = 1;

	if (first)
	{
		int	c, max_fd = IO_ARRAYLEN;

		io_rec = (MyIO **)new_malloc(sizeof(MyIO *) * max_fd);
		for (c = 0; c < max_fd; c++)
			io_rec[c] = (MyIO *) 0;
		first = 0;
	}
}

#ifdef HAVE_SSL
void SSL_show_errors(void)
{
	char buf[1000];
	int sslerr;

	while((sslerr = ERR_get_error()))
	{
		ERR_error_string(sslerr, buf);
		say("%s", buf);
	}
}
#endif

/*
 * All new dgets -- no more trap doors!
 *
 * There are at least four ways to look at this function.
 * The most important variable is 'buffer', which determines if
 * we force line buffering.  If it is on, then we will sit on any
 * incomplete lines until they get a newline.  This is the default
 * behavior for server connections, because they *must* be line
 * delineated.  However, when are getting input from an untrusted
 * source (eg, dcc chat, /exec'd process), we cannot assume that every
 * line will be newline delinated.  So in those cases, 'buffer' is 0,
 * and we force a flush on whatever we can slurp, without waiting for
 * a newline.
 *
 * Return values:
 *
 *	-1 -- something really died.  Either a read error occured, the
 *	      fildesc wasnt really ready for reading, or the input buffer
 *	      for the filedesc filled up (8192 bytes)
 *	 0 -- If the data read in from the file descriptor did not form a 
 *	      complete line, then zero is always returned.  This should be
 *	      considered a stopping condition.  Do not call dgets() again
 *	      after it returns 0, because unless more data is avaiable on
 *	      the fd, it will return -1, which you would misinterpret as an
 *	      error condition.
 *	      If "buffer" is 0, then whatever we have available will be 
 *	      returned in "str".
 *	      If "buffer" is not 0, then we will retain whatever we have
 *	      available, waiting for the newline to occur perhaps next time.
 *	>0 -- If a full, newline terminated line was available, the length
 *	      of the line is returned.
 */
int BX_dgets (char *str, int des, int buffer, int buffersize, void *ssl_fd)
{
	int	cnt = 0, c;
	MyIO	*ioe;
	int	nbytes;
	
	if (!io_rec)
		init_io();

	ioe = io_rec[des];

	if (ioe == NULL)
	{
		ioe = io_rec[des] = (MyIO *)new_malloc(sizeof(MyIO));
		ioe->buffer_size = IO_BUFFER_SIZE;
		ioe->buffer = (char *)new_malloc(ioe->buffer_size + 2);
		ioe->read_pos = ioe->write_pos = 0;
	}

	if (ioe->read_pos == ioe->write_pos)
	{
		ioe->read_pos = ioe->write_pos = 0;
		ioe->buffer[0] = 0;
		ioe->segments = 0;
	}

	if (!strchr(ioe->buffer + ioe->read_pos, '\n'))
	{
		if (ioe->read_pos)
		{
			ov_strcpy(ioe->buffer, ioe->buffer + ioe->read_pos);
			ioe->read_pos = 0;
			ioe->write_pos = strlen(ioe->buffer);
			ioe->segments = 1;
		}

		/*
		 * Dont try to read into a full buffer.
		 */
		if (ioe->write_pos >= ioe->buffer_size)
		{
			yell("***XXX*** Buffer for des [%d] is filled!", des);
			dgets_errno = ENOMEM; /* Cough */
			return -1;
		}
		/*
		 * Check to see if any bytes are ready.  If this fails,
		 * then its almost always due to the filedesc being 
		 * bogus.  Thats a fatal error.
		 */
		if (ioctl(des, FIONREAD, &nbytes) == -1)
		{
			*str = 0;
			dgets_errno = errno;
			return -1;
		} 

		/*
		 * Check for a quasi-EOF condition.  If we get to this
		 * point, then new_select() indicated that this fd is ready.
		 * The fd is ready if either:
		 *	1) A newline is in the buffer
		 *	2) select(2) returned ready for the fd.
		 *
		 * If 1) is true, then write_pos will not be zero.  So we can
		 * use that as a cheap way to check for #1.  If #1 is false,
		 * then #2 must have been true, and if nbytes is 0, then 
		 * that indicates an EOF condition.
		 */
		else if (!nbytes && ioe->write_pos == 0)
		{
			*str = 0;
			dgets_errno = errno;
			return -1;
		}

		else if (nbytes)
		{
#ifdef HAVE_SSL
			int rc = 0;
#endif

			if (nbytes >= IO_BUFFER_SIZE)
				nbytes = IO_BUFFER_SIZE-1;
#ifdef HAVE_SSL
			if(ssl_fd)
			{
				c = SSL_read((SSL *)ssl_fd, ioe->buffer + ioe->write_pos,
							 ioe->buffer_size - ioe->write_pos);

				if(c == -1 && (rc = SSL_get_error((SSL *)ssl_fd, c)) == SSL_ERROR_WANT_READ)
				{
					/* If SSL needs more data, then we need to call SSL_read again,
					 * so we'll return with 0 bytes read, and hope we get called
					 * again. :)
					 */
					return 0;
				}
			}
			else
#endif
				c = read(des, ioe->buffer + ioe->write_pos,
						 ioe->buffer_size - ioe->write_pos);

			if (x_debug & DEBUG_INBOUND) 
				yell("FD [%d], should [%d] did [%d]",
					 des, nbytes, c);

			if (c <= 0)
			{
#ifdef HAVE_SSL
				if(ssl_fd)
				{
					say("SSL_read() failed, SSL error %d", rc);
					SSL_show_errors();
				}
#endif
				*str = 0;
				dgets_errno = (c == 0) ? -1 : errno;
				return -1;
			}
			ioe->buffer[ioe->write_pos + c] = 0;
			ioe->write_pos += c;
			ioe->segments++;
		}
		else
		{
			/*
			 * At this point nbytes is 0, and it doesnt
			 * appear the socket is at EOF or ready to read.
			 * Very little to do at this point but force the
			 * issue and figure out what the heck went wrong.
			 */
			struct timeval t = { 0, 0 };
			fd_set testing;

			FD_ZERO(&testing);
			FD_SET(des, &testing);
			switch (select(des + 1, &testing, NULL, NULL, &t))
			{
				case -1:
				{
					yell("Aberrant condition for des [%d], closing down the connection out of desperation.", des);
					*str = 0;
					dgets_errno = errno;
					return -1;
				}
				case 0:
				{
					yell("des [%d] passed to dgets(), but it isnt ready.", des);
					if (ioe->write_pos == 0)
					{
						yell("X*X*X*X*X*X*X*X*X ABANDON SHIP! X*X*X*X*X*X*X*X*X*X");
						ircpanic("write_pos is zero when it cant be.");
					}
					else
					{
						yell("But something is buffered.  Flushing it to see if that helps.");
						ioe->buffer[ioe->write_pos++] = '\n';
						break;
					}
				}
				case 1:
				{
					errno = ECONNABORTED;
					*str = 0;
					dgets_errno = errno;
					return -1;
				}
			}
		}
	}

	dgets_errno = 0;

	/*
	 * If the caller wants us to force line buffering, and if there
	 * is no complete line, just stop right here.
	 */
	if (buffer && !strchr(ioe->buffer + ioe->read_pos, '\n'))
	{
		if (ioe->segments > MAX_SEGMENTS)
		{
			yell("*** Too many read()s on des [%d] without a newline!", des);
			*str = 0;
			dgets_errno = ECONNABORTED;
			return -1;
		}
		return 0;
	}
	/*
	 * Slurp up the data that is available into 'str'. 
	 */
	while (ioe->read_pos < ioe->write_pos)
	{
		if (((str[cnt] = ioe->buffer[ioe->read_pos++])) == '\n')
			break;
		cnt++;
		if (cnt >= buffersize-1)
			break;
	}

	/*
	 * Terminate it
	 */
	str[cnt + 1] = 0;

	/*
	 * If we end in a newline, then all is well.
	 * Otherwise, we're unbuffered, tell the caller.
	 * The caller then would need to do a strlen() to get
 	 * the amount of data.
	 */
	if (str[cnt] == '\n')
		return cnt;
	else
		return 0;
}


static int global_max_fd = -1;

/*
 * new_select: works just like select(), execpt I trimmed out the excess
 * parameters I didn't need.  
 */
int new_select (fd_set *rd, fd_set *wd, struct timeval *timeout)
{
		int	i,
			set = 0;
		fd_set 	new;
	struct timeval	thetimeout;
	struct timeval *newtimeout = &thetimeout;

	if (timeout)
		thetimeout = *timeout;
	else
		newtimeout = NULL;

	if (!io_rec)
		ircpanic("new select called before io_rec init");
	if (newtimeout && newtimeout->tv_usec < 0)
		ircpanic("new select with < -1");
		
	FD_ZERO(&new);
	for (i = 0; i <= global_max_fd; i++)
	{
		if (io_rec[i])
		{
			if ((io_rec[i]->read_pos < io_rec[i]->write_pos) &&
				strchr(io_rec[i]->buffer + io_rec[i]->read_pos, '\n'))
			{
				FD_SET(i, &new);
				set++;
			}
		}
	}

	if (set)
	{
		*rd = new;
		return set;
	}
#ifdef WIN32
	memset(&thetimeout, 0, sizeof(struct timeval));
	thetimeout.tv_usec = 500;
	i = select(global_max_fd + 1, rd, wd, NULL, &thetimeout);
	{
		MSG msg;

		while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return i;
#else
	return (select(global_max_fd + 1, rd, wd, NULL, newtimeout));
#endif
}

/*
 * Register a filedesc for readable events
 * Set up its input buffer
 */
int 	BX_new_open (int des)
{
	if (des < 0)
		return des;		/* Invalid */

	if (!io_rec)
		init_io();

	if (!FD_ISSET(des, &readables))
		FD_SET(des, &readables);
	if (des > global_max_fd)
		global_max_fd = des;
		
	return des;
}

int 	new_open_write (int des)
{
	if (des < 0)
		return des;		/* Invalid */

	if (!io_rec)
		init_io();

	if (!FD_ISSET(des, &writables))
		FD_SET(des, &writables);
#if 0
	if (des > global_max_fd)
		global_max_fd = des;
#endif		
	return des;
}


/*
 * Unregister a filedesc for readable events 
 * and close it down and free its input buffer
 */
int	BX_new_close (int des)
{
	if (des < 0)
		return -1;

	if (FD_ISSET(des, &readables))
		FD_CLR(des, &readables);

	if (io_rec && io_rec[des])
	{
	        new_free(&(io_rec[des]->buffer));
        	new_free((char **)&(io_rec[des]));
	}
	close(des);

	/*
	 * If we're closing the highest fd in use, then we
	 * want to adjust global_max_fd downward to the next highest fd.
	 */
	if (des == global_max_fd)
	{
		do
			des--;
		while (des >= 0 && !FD_ISSET(des, &readables));

		global_max_fd = des;
	}
	return -1;
}

int	new_close_write (int des)
{
	if (des < 0)
		return -1;

	if (FD_ISSET(des, &writables))
		FD_CLR(des, &writables);

	if (io_rec && io_rec[des])
	{
	        new_free(&(io_rec[des]->buffer));
        	new_free((char **)&(io_rec[des]));
	}
	close(des);

#if 0
	/*
	 * If we're closing the highest fd in use, then we
	 * want to adjust global_max_fd downward to the next highest fd.
	 */
	if (des == global_max_fd)
	{
		do
			des--;
		while (!FD_ISSET(des, &writables));

		global_max_fd = des;
	}
#endif
	return -1;
}

/* set's socket options */
void set_socket_options (int s)
{
	int	opt = 1;
#ifndef NO_STRUCT_LINGER
	struct linger	lin;

	lin.l_onoff = lin.l_linger = 0;
	setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&lin, sizeof(struct linger));
#endif

	setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
	opt = 1;
	setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, sizeof(opt));

#if notyet
	/* This is waiting for nonblock-aware code */
	info = fcntl(fd, F_GETFL, 0);
	info |= O_NONBLOCK;
	fcntl(fd, F_SETFL, info);
#endif
}


--- NEW FILE: userlist.c ---
/*
 *   CDE created userlist functions.
 *   Copyright Colten Edwards 04/10/96
 *
 */
 
#include "irc.h"
static char cvsrevision[] = "$Id: userlist.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(userlist_c)
#include "struct.h"

#include "server.h"
#include "commands.h"
#include "encrypt.h"
#include "vars.h"
#include "ircaux.h"
#include "lastlog.h"
#include "window.h"
#include "screen.h"
[...1678 lines suppressed...]
				window_display = 0;
				malloc_strcpy(&tmp->password, cryptit(new_pass));
				savelists(NULL, NULL, NULL, NULL);
				window_display = old_display_win;
				return 1;
			}
		}
		/* user doesn't have a pass, so set it up */
		if (old_pass)
		{
			int old_display_win = window_display;
			window_display = 0;
			tmp->password = m_strdup(cryptit(old_pass));
			savelists(NULL, NULL, NULL, NULL);
			window_display = old_display_win;
			return 1;
		}
	}
	return 0;
}

--- NEW FILE: wserv.c ---
/*
 * wserv.c - little program to be a pipe between a screen or
 * xterm window to the calling ircII process.
 *
 * Written by Troy Rollo, Copyright 1992 Troy Rollo
 * Finished by Matthew Green, Copyright 1993 Matthew Green
 * Support for 4.4BSD by Jeremy Nelson, Copyright 1997 EPIC Software Labs
 *
 * Works by opening up the unix domain socket that ircII bind's
 * before calling wserv, and which ircII also deleted after the
 * connection has been made.
 */

#if 0
static	char	rcsid[] = "@(#)$Id: wserv.c,v 1.1.1.1 2003/04/11 01:09:08 dan Exp $";
#endif

#include "defs.h"
#include "config.h"
#include "irc.h"
#include "struct.h"

#include "ircterm.h"
#include "ircaux.h"
#define WTERM_C
#include "modval.h"
#include <errno.h>
#include <sys/uio.h>

static 	int 	s;
static	char	buffer[256];

void 	my_exit(int);
void 	ignore (int value);

#ifdef CLOAKED
extern char proctitlestr[140];
extern char **Argv;
extern char *LastArgv;
#endif

int main (int argc, char **argv)
{
	fd_set		reads;
	int		nread;
	unsigned short 	port;
	char 		*host;
	char		*tmp;
	int		t;
	char		stuff[100];
	        
#ifndef WINNT
	my_signal(SIGWINCH, SIG_IGN, 0);
#endif
	my_signal(SIGHUP, SIG_IGN, 0);
	my_signal(SIGQUIT, SIG_IGN, 0);
	my_signal(SIGINT, ignore, 0);

	if (argc != 3)    /* no socket is passed */
		my_exit(1);

	host = argv[1];
	port = (unsigned short)atoi(argv[2]);
	if (!port)
		my_exit(2);		/* what the hey */

	s = connect_by_number(host, &port, SERVICE_CLIENT, PROTOCOL_TCP, 0);
	if (s < 0)
		my_exit(23);

	/*
	 * first line from a wserv program is the tty.  this is so ircii
	 * can grab the size of the tty, and have it changed.
	 */
	tmp = ttyname(0);
	sprintf(stuff, "%s\n", tmp);
	t = write(s, stuff, strlen(stuff));
	term_init(NULL);
	printf("t is %d", t);

	/*
	 * The select call..  reads from the socket, and from the window..
	 * and pipes the output from out to the other..  nice and simple
	 */
	for (;;)
	{
		FD_ZERO(&reads);
		FD_SET(0, &reads);
		FD_SET(s, &reads);
		if (select(s + 1, &reads, NULL, NULL, NULL) <= 0)
			if (errno == EINTR)
				continue;

		if (FD_ISSET(0, &reads))
		{
			if ((nread = read(0, buffer, sizeof(buffer))))
				write(s, buffer, nread);
			else
				my_exit(3);
		}
		if (FD_ISSET(s, &reads))
		{
			if ((nread = read(s, buffer, sizeof(buffer))))
				write(1, buffer, nread);
			else
				my_exit(4);
		}
	}

	my_exit(8);
}

void ignore (int value)
{
	/* send a ^C */
	char foo = 3;
	write(s, &foo, 1);
}

void my_exit(int value)
{
	printf("exiting with %d!\n", value);
	printf("errno is %d (%s)\n", errno, strerror(errno));
	exit(value);
}

/* These are here so we can link with network.o */
char *LocalHostName = NULL;
struct sockaddr_foobar LocalHostAddr;
char empty_string[] = "";
int get_int_var (enum VAR_TYPES unused) { return 5; }
void set_socket_options (int s) { }

/* End of file */

--- NEW FILE: pmbitchx.RES ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: input.c ---
/*
 * input.c: does the actual input line stuff... keeps the appropriate stuff
 * on the input line, handles insert/delete of characters/words... the whole
 * ball o wax 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#include "irc.h"
static char cvsrevision[] = "$Id: input.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(input_c)
#include <pwd.h>
#include "struct.h"

#include "alias.h"
[...2852 lines suppressed...]
					say("Invalid key sequence: ^%c", c);
					return;
				}
				key = c - 64;
				break;
			    }
			}
		}
		else if (*args == '\\')
		{
			key = *++args;
			args++;
		}
		else
			key = *(args++);

		edit_char(key);
	}
}


--- NEW FILE: notify.c ---
/*
 * notify.c: a few handy routines to notify you when people enter and leave irc 
 *
 * Written By Michael Sandrof
 * Copyright(c) 1990 
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 *
 * Modified Colten Edwards 96
 */


#include "irc.h"
static char cvsrevision[] = "$Id: notify.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(notify_c)
#include "struct.h"

#include "list.h"
#include "notify.h"
#include "ircaux.h"
#include "who.h"
#include "hook.h"
#include "server.h"
#include "output.h"
#include "vars.h"
#include "timer.h"
#include "misc.h"
#include "status.h"
#include "userlist.h"
#include "hash2.h"
#include "cset.h"
#include "server.h"
#define MAIN_SOURCE
#include "modval.h"

extern Server *server_list;

#define NOTIFY_LIST(s)          (&(server_list[s].notify_list))
#define NOTIFY_MAX(s)           (NOTIFY_LIST(s)->max)
#define NOTIFY_ITEM(s, i)       (NOTIFY_LIST(s)->list[i])

#define WATCH_LIST(s)          (&(server_list[s].watch_list))
#define WATCH_MAX(s)           (WATCH_LIST(s)->max)
#define WATCH_ITEM(s, i)       (WATCH_LIST(s)->list[i])

void batch_notify_userhost (char *);
void dispatch_notify_userhosts (void);
void notify_userhost_dispatch (UserhostItem *, char *, char *);
void notify_userhost_reply (char *, char *);

void	rebuild_notify_ison (int server)
{
	char *stuff;
	int i;
	if (from_server == -1)
		return;
	stuff = NOTIFY_LIST(from_server)->ison;

	if (NOTIFY_LIST(from_server)->ison)
		NOTIFY_LIST(from_server)->ison[0] = 0;

	for (i = 0; i < NOTIFY_MAX(from_server); i++)
	{
		m_s3cat(&(NOTIFY_LIST(from_server)->ison),
			space, NOTIFY_ITEM(from_server, i)->nick);
	}
}

void	rebuild_all_ison (void)
{
	int i;
	int ofs = from_server;
	for (i = 0; i < server_list_size(); i++)
	{
		from_server = i;
		rebuild_notify_ison(i);
	}
	from_server = ofs;
}



void ison_notify(char *AskedFor, char *AreOn)
{
	char	*NextAsked;
	char	*NextGot;

	NextGot = next_arg(AreOn, &AreOn);
	while ((NextAsked = next_arg(AskedFor, &AskedFor)) != NULL)
	{
		if (NextGot && !my_stricmp(NextAsked, NextGot))
		{
			notify_mark(NextAsked, NULL, 1, 1);
			NextGot = next_arg(AreOn, &AreOn);
		}
		else
			notify_mark(NextAsked, NULL, 0, 1);
	}
        dispatch_notify_userhosts();
}


void add_to_notify_queue(char *buffer)
{
	char *lame = LOCAL_COPY(buffer);
	isonbase(lame, ison_notify);
}

void notify_count(int server, int *on, int *off)
{
	NotifyItem *tmp;
	int i;
	if (server <= -1)
		return;	

	if (on) *on = 0;
	if (off) *off = 0;
	for (i = 0; i < NOTIFY_MAX(server); i++)
	{
		tmp = NOTIFY_ITEM(server, i);
		if (tmp->flag)
		{
			if (on) (*on)++;
		}
		else
		{
			if (off) (*off)++;
		}			
	}
}

/* Rewritten, -lynx */
void show_notify_list(int all)
{
	int count = 0;
	int i;
	char lastseen[20];
	char period[20];
	char timeson[20];
	NotifyItem *tmp;
	
	if (from_server == -1)
		return;	

	for (i = 0; i < NOTIFY_MAX(from_server); i++)
	{
		tmp = NOTIFY_ITEM(from_server, i);
		if (tmp->flag)
		{
			if (count == 0)
			{
				if (do_hook(NOTIFY_HEADER_LIST, "%s", "Online users"))
				{
					put_it("%s", convert_output_format("$G Online Users", NULL, NULL));
					put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_ON_FSET), "%s %s %s %s %s", "Nick", "UserHost", "Times", "Period", "Last seen"));
				}
			}
			strcpy(period, ltoa(tmp->period));
			strcpy(timeson, ltoa(tmp->times));
			strcpy(lastseen, ltoa(now - tmp->added));
			if (do_hook(NOTIFY_LIST, "%s %s %d %s %s %s", tmp->nick, tmp->host?tmp->host:"unknown at unknown", tmp->flag, timeson, period, lastseen))
				put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_ON_FSET), "%s %s %s %s %s", tmp->nick, tmp->host?tmp->host:tmp->looking, timeson, lastseen, "now" ));
			count++;
		}
	}
	count = 0;
	for (i = 0; i < NOTIFY_MAX(from_server); i++)
	{
		tmp = NOTIFY_ITEM(from_server, i);
		if ((all && !tmp->flag) || (tmp->times && !tmp->flag))
		{
			if (count == 0)
			{
				if (do_hook(NOTIFY_HEADER_LIST, "%s", "Offline users"))
				{
					put_it("%s", convert_output_format("$G Offline Users", NULL, NULL));
					put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_OFF_FSET), "%s %s %s %s %s", "Nick", "UserHost", "Times", "Period", "Last seen"));
				}
			}
			strcpy(period, ltoa(tmp->period));
			strcpy(timeson, ltoa(tmp->times));
			strcpy(lastseen, ltoa(now - tmp->lastseen));
			if (do_hook(NOTIFY_LIST, "%s %s %d %s %s %s", tmp->nick, tmp->host?tmp->host:"unknown at unknown", tmp->flag, timeson, period, lastseen))
			{
				if (!tmp->times)
					put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_OFF_FSET), "%s %s %s %s %s", tmp->nick, tmp->host?tmp->host:tmp->looking, "never", "none", "n/a"));
				else
					put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_OFF_FSET), "%s %s %s %s %s", tmp->nick, tmp->host?tmp->host:tmp->looking, timeson, period, lastseen));
			}
			count++;
		}
	}
}

/* notify: the NOTIFY command.  Does the whole ball-o-wax */
BUILT_IN_COMMAND(notify)
{
	char	*nick,
		*list = NULL,
		*ptr;
	int	no_nicks = 1;
	int	do_ison = 0;
	int	servnum = from_server;
	int	shown = 0;
	NotifyItem	*new_n;

	malloc_strcpy(&list, empty_string);
	while ((nick = next_arg(args, &args)))
	{
		for (no_nicks = 0; nick; nick = ptr)
		{
			char *host = NULL;
			shown = 0;
			if ((ptr = strchr(nick, ',')) != NULL)
				*ptr++ = 0;
			if ((host = strchr(nick, '!')))
				*host++ = 0;			
			else
				host = "*@*";
				
			if (*nick == '-')
			{
				nick++;
				
				if (*nick)
				{
					for (servnum = 0; servnum < server_list_size(); servnum++)
					{
						if ((new_n = (NotifyItem *)remove_from_array(
							(Array *)NOTIFY_LIST(servnum), nick)))
						{
							new_free(&new_n->nick);
							new_free(&new_n->host);
							new_free(&new_n->looking);
							new_free((char **)&new_n);

							if (!shown)
							{
								bitchsay("%s!%s removed from notification list", nick, host);
								shown = 1;
							}
						}
						else
						{
							if (!shown)
							{
								bitchsay("%s!%s is not on the notification list", nick, host);
								shown = 1;
							}
						}
					}
				}
				else
				{
					for (servnum = 0; servnum < server_list_size(); servnum++)
					{
						while ((new_n = (NotifyItem *)array_pop(
							(Array *)NOTIFY_LIST(servnum), 0)))
						{
							new_free(&new_n->nick);
							new_free(&new_n->host);
							new_free(&new_n->looking);
							new_free((char **)&new_n);
						}
					}
					bitchsay("Notify list cleared");
				}
			}
			else
			{
				/* compatibility */
				if (*nick == '+')
					nick++;

				if (*nick)
				{
					int added = 0;
					if (strchr(nick, '*'))
						bitchsay("Wildcards not allowed in NOTIFY nicknames!");
					else
					{
						for (servnum = 0; servnum < server_list_size(); servnum++)
						{

							if ((new_n = (NotifyItem *)array_lookup(
								(Array *)NOTIFY_LIST(servnum), nick, 0, 0)))
							{
								malloc_strcpy(&new_n->looking, host);
								new_n->flag = 0;
								continue;	/* Already there! */
							}

							new_n = (NotifyItem *)new_malloc(sizeof(NotifyItem));
							new_n->nick = m_strdup(nick);
							new_n->looking = m_strdup(host);
							new_n->host = NULL;
							new_n->flag = 0;
							add_to_array((Array *)NOTIFY_LIST(servnum), 
							     (Array_item *)new_n);
							added = 1;
						}
						if (added)
						{
							m_s3cat(&list, space, new_n->nick);
							do_ison = 1;
						}
						bitchsay("%s!%s added to the notification list", nick, host);
					}
				} else
					show_notify_list(1);
			}
		}
	}

	if (do_ison && get_int_var(NOTIFY_VAR))
	{
		int ofs = from_server;
		for (servnum = 0; servnum < server_list_size(); servnum++)
		{
			from_server = servnum;
			if (is_server_connected(from_server) && list && *list)
				add_to_notify_queue(list);
		}
		from_server = ofs;
	}
	
	new_free(&list);	
	rebuild_all_ison();
	if (no_nicks)
		show_notify_list(0);
}

/*
 * do_notify: This simply goes through the notify list, sending out a WHOIS
 * for each person on it.  This uses the fancy whois stuff in whois.c to
 * figure things out.
 */
void do_notify(void)
{
	int	old_from_server = from_server;
	int	servnum;
	static	time_t		last_notify = 0;
	int		interval = get_int_var(NOTIFY_INTERVAL_VAR);
	time_t current_time = time(NULL);

	if (current_time < last_notify)
		last_notify = current_time;
	else if (!interval || interval > (current_time - last_notify))
		return;		/* Not yet */

	last_notify = current_time;

	if (!server_list_size() || !get_int_var(NOTIFY_VAR))
		return;
	for (servnum = 0; servnum < server_list_size(); servnum++)
	{
		if (is_server_connected(servnum) && !get_server_watch(servnum))
		{
			from_server = servnum;
			if (NOTIFY_LIST(servnum)->ison && *NOTIFY_LIST(servnum)->ison)
			{
				char *lame = LOCAL_COPY(NOTIFY_LIST(servnum)->ison);
				isonbase(lame, ison_notify);
			}
		}
	}
	from_server = old_from_server;
	return;
}

void check_auto_invite(char *nick, char *userhost)
{
#ifdef WANT_USERLIST
ChannelList *chan = NULL;
UserList *tmp = NULL;
	for (chan = get_server_channels(from_server); chan; chan = chan->next)
	{
		if ((tmp = lookup_userlevelc("*", userhost, chan->channel, NULL)))
		{
			NickList *n = NULL;
			n = find_nicklist_in_channellist(nick, chan, 0);
			if (!n && chan->chop && get_cset_int_var(chan->csets, AINV_CSET) && (tmp->flags & ADD_INVITE) && get_cset_int_var(chan->csets, AINV_CSET))
			{
				bitchsay("Auto-inviting %s to %s", nick, chan->channel);
				send_to_server("NOTICE %s :Auto-invite from %s", nick, get_server_nickname(from_server));
				send_to_server("INVITE %s %s%s%s", nick, chan->channel, chan->key?space:empty_string, chan->key?chan->key:empty_string);
			}
		}
		tmp = NULL;
	}
#endif
}




static char *batched_notify_userhosts = NULL;
static int batched_notifies = 0;

void batch_notify_userhost (char *nick)
{
	m_s3cat(&batched_notify_userhosts, space, nick);
	batched_notifies++;
}

void dispatch_notify_userhosts (void)
{
	if (batched_notify_userhosts)
	{
		if (x_debug & DEBUG_NOTIFY)
			yell("Dispatching notifies to server [%d], [%s]", from_server, batched_notify_userhosts);
		userhostbase(batched_notify_userhosts, notify_userhost_dispatch, 1, NULL);
		new_free(&batched_notify_userhosts);
		batched_notifies = 0;
	}
}

void notify_userhost_dispatch (UserhostItem *stuff, char *nick, char *text)
{
	char userhost[BIG_BUFFER_SIZE + 1];

	snprintf(userhost, BIG_BUFFER_SIZE, "%s@%s", stuff->user, stuff->host);
	notify_userhost_reply(stuff->nick, userhost);
}

#if 0
<MadHack> #0  0x809a571 in n_malloc_strcpy ()
<MadHack> #1  0x80b400e in notify_userhost_reply ()
<MadHack> #2  0x80b3f65 in notify_userhost_dispatch ()
<MadHack> #3  0x80d2ef0 in userhost_returned ()
<MadHack> #4  0x80b61bd in numbered_command ()
hard_uh_notify is on
#endif

void notify_userhost_reply (char *nick, char *userhost)
{
	NotifyItem *tmp;

	if ((tmp = (NotifyItem *)array_lookup(
				(Array *)NOTIFY_LIST(from_server), nick, 0, 0)))
	{
		set_display_target(nick, LOG_CRAP);
		malloc_strcpy(&tmp->nick, nick);
		if (userhost)
		{
			malloc_strcpy(&tmp->host, userhost);
			if (!wild_match(tmp->looking, userhost))
			{
				reset_display_target();
				return;
			}
			check_auto_invite(nick, userhost);
		}
		if ((do_hook(NOTIFY_SIGNON_LIST, "%s %s", tmp->nick, tmp->host?tmp->host:empty_string)))
		{
			put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_SIGNON_FSET), "%s %s %s",update_clock(GET_TIME),tmp->nick,tmp->host?tmp->host:empty_string));
                        logmsg(LOG_NOTIFY, tmp->nick,  0, "%s(%s) has logged on", tmp->host ? tmp->host : empty_string, tmp->looking);
		}
		malloc_strcpy(&last_notify_nick, nick);
		reset_display_target();
	}
}


/*
 * notify_mark: This marks a given person on the notify list as either on irc
 * (if flag is 1), or not on irc (if flag is 0).  If the person's status has
 * changed since the last check, a message is displayed to that effect.  If
 * the person is not on the noTify list, this call is ignored 
 * doit if passed as 0 means it comes from a join, or a msg, etc, not from
 * an ison reply.  1 is the other..
 */
void notify_mark(char *nick, char *userhost, int flag, int doit)
{
	NotifyItem 	*tmp;
	int		count = 0;
	int		loc = 0;
	
	if ((tmp = (NotifyItem *)find_array_item(
				(Array *)NOTIFY_LIST(from_server), nick, 
				&count, &loc)) && count < 0)
	{
		if (flag)
		{
			if (tmp->host && !wild_match(tmp->looking, tmp->host))
			{
				tmp->flag = 0;
				return;
			}
			if (tmp->flag != 1)
			{
			        batch_notify_userhost(nick);
				tmp->lastseen = 0;
				if (!tmp->added)
					tmp->added = now;
				tmp->times++;
			}
			tmp->flag = 1;
			if (!doit)
				dispatch_notify_userhosts();
		}
		else
		{
			if (tmp->flag == 1)
			{
				const char *who = NULL;
				unsigned long level = 0;
				save_display_target(&who, &level);
				set_display_target(nick, LOG_NOTIFY);
				check_orig_nick(nick);
				if ((do_hook(NOTIFY_SIGNOFF_LIST, "%s %s", tmp->nick, tmp->host?tmp->host:empty_string)) && doit)
					put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_SIGNOFF_FSET), "%s %s %s",update_clock(GET_TIME),tmp->nick,tmp->host?tmp->host:empty_string));
				tmp->period += now - tmp->added;
				tmp->lastseen = now;
	                        logmsg(LOG_NOTIFY, tmp->nick,  0, "%s(%s) has logged off", tmp->host ? tmp->host : empty_string, tmp->looking);
				restore_display_target(who, level);
			}
			tmp->flag = 0;
		}
	}
}

void save_notify(FILE *fp)
{
	int i = 0;
	if (server_list_size() && NOTIFY_MAX(0))
	{
		fprintf(fp, "NOTIFY");
		for (i = 0; i < NOTIFY_MAX(0); i++)
			fprintf(fp, " %s!%s", NOTIFY_ITEM(0, i)->nick, NOTIFY_ITEM(0, i)->looking);
		fprintf(fp, "\n");
	}
	if (NOTIFY_MAX(0) && do_hook(SAVEFILE_LIST, "Notify %d", NOTIFY_MAX(0)))
		bitchsay("Saved %d Notify entries", NOTIFY_MAX(0));
}

/* I hate broken compilers -mrg */
static	char	*vals[] = { "NOISY", "QUIET", "OLD", NULL };

void set_notify_handler(Window *win, char *value, int unused)
{
	int	len;
	int	i;
	char	*s;

	if (!value)
		value = empty_string;
	for (i = 0, len = strlen(value); (s = vals[i]); i++)
		if (0 == my_strnicmp(value, s, len))
			break;
	set_string_var(NOTIFY_HANDLER_VAR, s);
	return;
}

void make_notify_list (int servnum)
{
	NotifyItem *tmp;
	char *list = NULL;
	int i;

	server_list[servnum].notify_list.func = (alist_func)global[MY_STRICMP];
	server_list[servnum].notify_list.hash = HASH_INSENSITIVE;

	if (!get_int_var(NOTIFY_VAR))
		return;
	for (i = 0; i < NOTIFY_MAX(0); i++)
	{
		tmp = (NotifyItem *)new_malloc(sizeof(NotifyItem));
		tmp->nick = m_strdup(NOTIFY_ITEM(0, i)->nick);
		tmp->looking = m_strdup(NOTIFY_ITEM(0, i)->looking);
		tmp->flag = 0;

		add_to_array ((Array *)NOTIFY_LIST(servnum),
			      (Array_item *)tmp);
		m_s3cat(&list, space, tmp->nick);
	}
	if (list)
		isonbase(list, ison_notify);
	new_free(&list);
}

char *get_notify_nicks (int showserver, int showon, char *nick, int extra)
{
	char *list = NULL;
	int i = 0;
	
	if (showserver < 0 || showserver >= server_list_size())
		return m_strdup(empty_string);
 
	if (nick && *nick)
	{
		NotifyItem *tmp;
		for (i = 0; i < NOTIFY_MAX(showserver); i++)
		{
			if (my_stricmp(nick, NOTIFY_ITEM(showserver, i)->nick))
				continue;
			tmp = NOTIFY_ITEM(from_server, i);
			m_s3cat(&list, space, tmp->nick);
			m_s3cat(&list, space, tmp->host);
			m_s3cat(&list, space, ltoa(tmp->flag));
			m_s3cat(&list, space, ltoa(tmp->period));
			m_s3cat(&list, space, ltoa(tmp->times));
			m_s3cat(&list, space, ltoa(tmp->lastseen));
			break;
		}
	}
	else
	{
		for (i = 0; i < NOTIFY_MAX(showserver); i++)
		{
			if (showon == -1 || showon == NOTIFY_ITEM(showserver, i)->flag)
				m_s3cat(&list, space, NOTIFY_ITEM(showserver, i)->nick);
		}
	}
	return list ? list : m_strdup(empty_string);
}


char *get_watch_nicks (int showserver, int showon, char *nick, int extra)
{
	char *list = NULL;
	int i = 0;
	
	if (showserver < 0 || showserver >= server_list_size())
		return m_strdup(empty_string);
 
	if (nick && *nick)
	{
		NotifyItem *tmp;
		for (i = 0; i < WATCH_MAX(showserver); i++)
		{
			if (my_stricmp(nick, WATCH_ITEM(showserver, i)->nick))
				continue;
			tmp = WATCH_ITEM(from_server, i);
			m_s3cat(&list, space, tmp->nick);
			m_s3cat(&list, space, tmp->host);
			m_s3cat(&list, space, ltoa(tmp->flag));
			m_s3cat(&list, space, ltoa(tmp->period));
			m_s3cat(&list, space, ltoa(tmp->times));
			m_s3cat(&list, space, ltoa(tmp->lastseen));
			break;
		}
	}
	else
	{
		for (i = 0; i < WATCH_MAX(showserver); i++)
		{
			if (showon == -1 || showon == WATCH_ITEM(showserver, i)->flag)
				m_s3cat(&list, space, WATCH_ITEM(showserver, i)->nick);
		}
	}
	return list ? list : m_strdup(empty_string);
}


int iswatch(int serv, char *list, int all)
{
	if (all)
	{
		int servnum;
		for (servnum = 0; servnum < server_list_size(); servnum++)
		{
			if (get_server_watch(servnum) && is_server_connected(servnum))
				my_send_to_server(servnum, "WATCH %s", list);
		}
		return 0;
	}
	if (get_server_watch(serv) && is_server_connected(serv))
		my_send_to_server(serv, "WATCH %s", list);
	return 0;
}

void make_watch_list (int servnum)
{
	NotifyItem *tmp;
	char *list = NULL;
	int i;

	server_list[servnum].watch_list.func = (alist_func)global[MY_STRICMP];
	server_list[servnum].watch_list.hash = HASH_INSENSITIVE;

	for (i = 0; i < WATCH_MAX(0); i++)
	{
		if (!(tmp = (NotifyItem *)array_lookup((Array *)WATCH_LIST(servnum), WATCH_ITEM(0, i)->nick, 0, 0)))
		{
			tmp = (NotifyItem *)new_malloc(sizeof(NotifyItem));
			malloc_strcpy(&tmp->nick, WATCH_ITEM(0, i)->nick);
			add_to_array ((Array *)WATCH_LIST(servnum), (Array_item *)tmp);
		}
		tmp->flag = 0;
		if (!list)
			list = m_opendup(space_plus, tmp->nick, NULL);
		else
			m_s3cat(&list, space_plus, tmp->nick);
	}
	if (list)
		iswatch(servnum, list, 0);
	new_free(&list);
}


void show_watch_list(int all)
{
#if 0
int i;
char *list = NULL;
	if ((server != -1) && server < server_list_size() && WATCH_MAX(server))
	{
		put_it("Watch List");
		for (i = 0; i < WATCH_MAX(server); i++)
			m_s3cat(&list, space, WATCH_ITEM(server, i)->nick);
		put_it("%s", list);
	}
	new_free(&list);
#endif
int i, count = 0;
char period[80], timeson[80], lastseen[80];
NotifyItem *tmp;

	if (from_server == -1)
		return;
	for (i = 0; i < WATCH_MAX(from_server); i++)
	{
		tmp = WATCH_ITEM(from_server, i);
		if (tmp->flag)
		{
			strcpy(period, ltoa(tmp->period));
			strcpy(timeson, ltoa(tmp->times));
			strcpy(lastseen, ltoa(now - tmp->added));
			if (do_hook(WATCH_LIST, "%s %s %d %s %s %s", tmp->nick, tmp->host?tmp->host:"unknown at unknown", tmp->flag, timeson, period, lastseen))
			{
				if (!count)
				{
					put_it("%s", convert_output_format("$G Online Users", NULL, NULL));
					put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_ON_FSET), "%s %s %s %s %s", "Nick", "UserHost", "Times", "Period", "Last seen"));
				}
				put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_ON_FSET), "%s %s %s %s %s", tmp->nick, tmp->host?tmp->host:tmp->looking, timeson, lastseen, "now" ));
			}
			count++;
		}
	}
	count = 0;
	for (i = 0; i < WATCH_MAX(from_server); i++)
	{
		tmp = WATCH_ITEM(from_server, i);
		if ((all && !tmp->flag) || (tmp->times && !tmp->flag))
		{
			strcpy(period, ltoa(tmp->period));
			strcpy(timeson, ltoa(tmp->times));
			strcpy(lastseen, ltoa(now - tmp->lastseen));
			if (do_hook(WATCH_LIST, "%s %s %d %s %s %s", tmp->nick, tmp->host?tmp->host:"unknown at unknown", tmp->flag, timeson, period, lastseen))
			{
				if (!count)
				{
					put_it("%s", convert_output_format("$G Offline Users", NULL, NULL));
					put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_OFF_FSET), "%s %s %s %s %s", "Nick", "UserHost", "Times", "Period", "Last seen"));
				}

				if (!tmp->times)
					put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_OFF_FSET), "%s %s %s %s %s", tmp->nick, tmp->host?tmp->host:tmp->looking, "never", "none", "n/a"));
				else
					put_it("%s", convert_output_format(fget_string_var(FORMAT_NOTIFY_OFF_FSET), "%s %s %s %s %s", tmp->nick, tmp->host?tmp->host:tmp->looking, timeson, period, lastseen));
			}
			count++;
		}
	}

}

BUILT_IN_COMMAND(watchcmd)
{
char *nick;
char *list = NULL;
int servnum = 0;
	if (args && *args)
	{
		while ((nick = next_arg(args, &args)))
		{
			NotifyItem *new_n;
			int shown = 0;
			if (*nick == '+')
			{
				int added = 0;
				nick++;
				if (!*nick)
				{
					show_watch_list(0);
					continue;
				}
				if (strchr(nick, '*'))
					bitchsay("Wildcards not allowed in WATCH nicknames!");
				else
				{
					for (servnum = 0; servnum < server_list_size(); servnum++)
					{
						if ((new_n = (NotifyItem *)array_lookup(
							(Array *)WATCH_LIST(servnum), nick, 0, 0)))
						{
							new_n->flag = 0;
							continue;	/* Already there! */
						}
						if (WATCH_MAX(servnum) >= (is_server_connected(servnum) ? get_server_watch(servnum) : 128))
						{
							bitchsay("Too many WATCH entries [%d]", get_server_watch(servnum));
							continue;
						}
						new_n = (NotifyItem *)new_malloc(sizeof(NotifyItem));
						new_n->nick = m_strdup(nick);
						new_n->flag = 0;
						add_to_array((Array *)WATCH_LIST(servnum), 
						     (Array_item *)new_n);
						added = 1;
					}
					if (added)
					{
						if (!list)
							list = m_opendup(space_plus, new_n->nick, NULL);
						else
							m_s3cat(&list, space_plus, new_n->nick);
					}
					bitchsay("%s added to the watch list", nick);
				}
			}
			else if (*nick == '-')
			{
				nick++;
				if (!*nick)
					continue;
				for (servnum = 0; servnum < server_list_size(); servnum++)
				{
					if ((new_n = (NotifyItem *)remove_from_array(
						(Array *)WATCH_LIST(servnum), nick)))
					{
						if (!list)
							list = m_opendup(space_minus, nick, NULL);
						else
							m_s3cat(&list, space_minus, nick);
						new_free(&new_n->nick);
						new_free(&new_n->host);
						new_free(&new_n->looking);
						new_free((char **)&new_n);
						if (!shown)
						{
							bitchsay("%s removed from watch list", nick);
							shown = 1;
						}
					}
					else
					{
						if (!shown)
						{
							bitchsay("%s is not on the watch list", nick);
							shown = 1;
						}
					}
				}
			}
			else if (*nick == 'S')
				my_send_to_server(from_server, "%s S", command);
			else if (*nick == 'L')
				my_send_to_server(from_server, "%s L", command);
			else if (*nick == 'l')
				my_send_to_server(from_server, "%s l", command);
			else if (*nick == 'C')
			{
				for (servnum = 0; servnum < server_list_size(); servnum++)
				{
					if (!get_server_watch(servnum))
						continue;
					while ((new_n = (NotifyItem *)array_pop(
						(Array *)WATCH_LIST(servnum), 0)))
					{
						new_free(&new_n->nick);
						new_free(&new_n->looking);
						new_free(&new_n->host);
						new_free((char **)&new_n);
					}
					if (get_server_watch(servnum) && is_server_connected(servnum))
						my_send_to_server(servnum, "%s S", command);
				}
				bitchsay("Watch list cleared");
			}
		}
		if (list)
			iswatch(servnum, list, 1);
		new_free(&list);
	} else	
		show_watch_list(1);
}

void save_watch(FILE *fp)
{
	int i = 0;
	if (server_list_size() && WATCH_MAX(0))
	{
		fprintf(fp, "WATCH");
		for (i = 0; i < WATCH_MAX(0); i++)
			fprintf(fp, " +%s", WATCH_ITEM(0, i)->nick);
		fprintf(fp, "\n");
	}
	if (WATCH_MAX(0) && do_hook(SAVEFILE_LIST, "Watch %d", WATCH_MAX(0)))
		bitchsay("Saved %d Watch entries", WATCH_MAX(0));
}

void show_watch_notify(char *from, int online, char **args)
{
NotifyItem *new_n;
	if ((new_n = (NotifyItem *)array_lookup((Array *)WATCH_LIST(from_server),
		args[0], 0, 0)))
	{
		if (online)
		{
			new_free(&new_n->host);
			new_n->host = m_opendup(args[1], "@", args[2], NULL);
			if (!new_n->flag)
			{
				new_n->lastseen = 0;
				if (!new_n->added)
					new_n->added = now;
				new_n->times++;
	                        logmsg(LOG_NOTIFY, new_n->nick,  0, "%s has logged on", new_n->host ? new_n->host : empty_string);
			}
			new_n->period = my_atol(args[3]);
		}
		else
		{
			if (new_n->flag)
			{
				new_n->period = now - new_n->added;
				new_n->lastseen = now;
	                        logmsg(LOG_NOTIFY, new_n->nick,  0, "%s has logged off", new_n->host ? new_n->host : empty_string);
			}
		}
		new_n->flag = online;
		put_it("%s", convert_output_format(fget_string_var(online?FORMAT_WATCH_SIGNON_FSET:FORMAT_WATCH_SIGNOFF_FSET), "%s %s %s %s", args[0], args[1], args[2], args[3]));
	}
}

void send_watch(int server)
{
int i;
char *list = NULL;
NotifyItem *tmp;
	for (i = 0; i < WATCH_MAX(server); i++)
	{
		if (!(tmp = (NotifyItem *)array_lookup((Array *)WATCH_LIST(server), WATCH_ITEM(server, i)->nick, 0, 0)))
		{
			tmp = (NotifyItem *)new_malloc(sizeof(NotifyItem));
			malloc_strcpy(&tmp->nick, WATCH_ITEM(server, i)->nick);
			add_to_array ((Array *)WATCH_LIST(server), (Array_item *)tmp);
		}
		tmp->flag = 0;
		if (!list)
			list = m_opendup(space_plus, tmp->nick, NULL);
		else
		{
			if ((strlen(list) + strlen(tmp->nick)) > 490)
			{
				iswatch(server, list, 0);
				new_free(&list);
				list = m_opendup(space_plus, tmp->nick, NULL);
			}
			else
				m_s3cat(&list, space_plus, tmp->nick);
		}
	}
	if (list)
		iswatch(server, list, 0);
	new_free(&list);
}

--- NEW FILE: alias.c ---
/*
 * alias.c Handles command aliases for irc.c 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990, 1995 Michael Sandroff and others 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#define _cs_alist_hash_
#include "irc.h"
static char cvsrevision[] = "$Id: alias.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(alias_c)
#include "struct.h"
#include "alias.h"
#include "alist.h"
#include "array.h"
#include "dcc.h"
[...2451 lines suppressed...]
	{
		AliasStack	*tmp;

		say("%s STACK LIST", name);
		for (tmp = *aptrptr; tmp; tmp = tmp->next)
		{
			if (!tmp->list)
				say("\t%s\t<Placeholder>", tmp->name);

			else if (tmp->list->stub)
				say("\t%s STUBBED TO %s", tmp->name, tmp->list->stub);

			else
				say("\t%s\t%s", tmp->name, tmp->list->stuff);
		}
		return;
	}
	say("Unknown STACK type ??");
}


--- NEW FILE: screen.c ---
/*
 * screen.c
 *
 * Written By Matthew Green, based on window.c by Michael Sandrof
 * Copyright(c) 1993 Matthew Green
 * Significant modifications by Jeremy Nelson
 * Copyright 1997 EPIC Software Labs,
 * Significant additions by J. Kean Johnston
 * Copyright 1998 J. Kean Johnston, used with permission
 * Modifications Copyright Colten Edwards 1997-1998
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
 */


#include "irc.h"
static char cvsrevision[] = "$Id: screen.c,v 1.1.1.3 2003/07/31 07:01:08 root Exp $";
CVS_REVISION(screen_c)
#include "struct.h"

[...2947 lines suppressed...]
		return (current_term->TI_sgrstrs[TERM_SGR_NORMAL-1]);
	}

	if (fore == -1)
		fore = last_fore;
	if (back == -1)
		back = last_back;

	if (back == 58)
		strcat(retbuf, current_term->TI_sgrstrs[TERM_SGR_BLINK_ON - 1]);
	if (fore > -1)
		strcat(retbuf, current_term->TI_forecolors[fore_conv[fore]]);
	if (back > -1 && back < 58)
		strcat(retbuf, current_term->TI_backcolors[back_conv[back]]);

	last_fore = fore;
	last_back = back;

	return retbuf;
}

--- NEW FILE: history.c ---
/*
 * history.c: stuff to handle command line history 
 *
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: history.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(history_c)
#include "struct.h"

#include "ircaux.h"
#include "vars.h"
#include "history.h"
#include "output.h"
#include "input.h"
#define MAIN_SOURCE
#include "modval.h"

static	char	*history_match (char *);
static	void	add_to_history_list (int, char *);
static	char	*get_from_history_buffer (int);

typedef struct	HistoryStru
{
	int	number;
	char	*stuff;
	struct	HistoryStru *next;
	struct	HistoryStru *prev;
}	History;

/* command_history: pointer to head of command_history list */
static	History *command_history_head = NULL;
static	History *command_history_tail = NULL;
static	History *command_history_pos = NULL;

/* hist_size: the current size of the command_history array */
static	int	hist_size = 0;

/* hist_count: the absolute counter for the history list */
static	int	hist_count = 0;

/*
 * last_dir: the direction (next or previous) of the last get_from_history()
 * call.... reset by add to history 
 */
static	int	last_dir = -1;

/*
 * history pointer
 */
static	History	*tmp = NULL;

/*
 * history_match: using wild_match(), this finds the latest match in the
 * history file and returns it as the function result.  Returns null if there
 * is no match.  Note that this sticks a '*' at the end if one is not already
 * there. 
 */
static	char *history_match (char *match)
{
	char	*ptr;
	char	*match_str = NULL;

	if (*(match + strlen(match) - 1) == '*')
		match_str = LOCAL_COPY(match);
	else
	{
		match_str = alloca(strlen(match) + 3);
		strcpy(match_str, match);
		strcat(match_str, "*");
	}
	if (get_int_var(HISTORY_VAR))
	{
		if ((last_dir == -1) || (tmp == NULL))
			tmp = command_history_head;
		else
			tmp = tmp->next;
		if (tmp)
		{		
			for (; tmp; tmp = tmp->next)
			{
				ptr = tmp->stuff;
				while (ptr && strchr(get_string_var(CMDCHARS_VAR), *ptr))
					ptr++;

				if (wild_match(match_str, ptr))
				{
					last_dir = PREV;
					return (tmp->stuff);
				}
			}
		}
	}
	last_dir = -1;
	return NULL;
}

/* shove_to_history: a key binding that saves the current line into
 * the history and then deletes the whole line.  Useful for when you
 * are in the middle of a big line and need to "get out" to do something
 * else quick for just a second, and you dont want to have to retype
 * everything all over again
 */
extern void	shove_to_history (char unused, char *not_used)
{
	add_to_history(get_input());
	input_clear_line(unused, not_used);
}

static	void add_to_history_list(int cnt, char *stuff)
{
	History *new;

	if (get_int_var(HISTORY_VAR) == 0)
		return;
	if ((hist_size == get_int_var(HISTORY_VAR)) && command_history_tail)
	{
		if (hist_size == 1)
		{
			malloc_strcpy(&command_history_tail->stuff, stuff);
			return;
		}
		new = command_history_tail;
		command_history_tail = command_history_tail->prev;
		command_history_tail->next = NULL;
		new_free(&new->stuff);
		new_free((char **)&new);
		if (command_history_tail == NULL)
			command_history_head = NULL;
	}
	else
		hist_size++;
	new = (History *) new_malloc(sizeof(History));
	new->stuff = NULL;
	new->number = cnt;
	new->next = command_history_head;
	new->prev = NULL;
	malloc_strcpy(&(new->stuff), stuff);
	if (command_history_head)
		command_history_head->prev = new;
	command_history_head = new;
	if (command_history_tail == NULL)
		command_history_tail = new;
	command_history_pos = NULL;
}

/*
 * set_history_size: adjusts the size of the command_history to be size. If
 * the array is not yet allocated, it is set to size and all the entries
 * nulled.  If it exists, it is resized to the new size with a realloc.  Any
 * new entries are nulled. 
 */
void set_history_size(Window *win, char *unused, int size)
{
	int	i,
		cnt;
	History *ptr;

	if (size < hist_size)
	{
		cnt = hist_size - size;
		for (i = 0; i < cnt; i++)
		{
			ptr = command_history_tail;
			command_history_tail = ptr->prev;
			new_free(&(ptr->stuff));
			new_free((char **)&ptr);
		}
		if (command_history_tail == NULL)
			command_history_head = NULL;
		else
			command_history_tail->next = NULL;
		hist_size = size;
	}
}

/*
 * add_to_history: adds the given line to the history array.  The history
 * array is a circular buffer, and add_to_history handles all that stuff. It
 * automagically allocted and deallocated memory as needed 
 */
void add_to_history(char *line)
{
	char	*ptr;

	if (line && *line)
	{
		while (line && *line)
		{
			if ((ptr = sindex(line, "\n\r")) != NULL)
				*(ptr++) = '\0';
			add_to_history_list(hist_count, line);
			last_dir = PREV;
			hist_count++;
			line = ptr;
		}
	}
}

static	char	*get_from_history_buffer(int which)
{
	if ((get_int_var(HISTORY_VAR) == 0) || (hist_size == 0))
		return NULL;
	/*
	 * if (last_dir != which) { last_dir = which; get_from_history(which); }
	 */
	if (which == NEXT)
	{
		if (command_history_pos)
		{
			if (command_history_pos->prev)
				command_history_pos = command_history_pos->prev;
			else
				command_history_pos = command_history_tail;
		}
		else
		{
			add_to_history(get_input());
			command_history_pos = command_history_tail;
		}
		return (command_history_pos->stuff);
	}
	else
	{
		if (command_history_pos)
		{
			if (command_history_pos->next)
				command_history_pos = command_history_pos->next;
			else
				command_history_pos = command_history_head;
		}
		else
		{
			add_to_history(get_input());
			command_history_pos = command_history_head;
		}
		return (command_history_pos->stuff);
	}
}

/*
 * get_history: gets the next history entry, either the PREV entry or the
 * NEXT entry, and sets it to the current input string 
 */
extern void	get_history (int which)
{
	char	*ptr;

	if ((ptr = get_from_history(which)) != NULL)
	{
		set_input(ptr);
		update_input(UPDATE_ALL);
	}

}

char	*get_from_history(int which)
{
	return(get_from_history_buffer(which));
}

/* history: the /HISTORY command, shows the command history buffer. */
BUILT_IN_COMMAND(history)
{
	int	cnt,
		max = 0;
	char	*value;
	char	*match = NULL;
	
	if (get_int_var(HISTORY_VAR))
	{
		say("Command History:");
		if ((value = next_arg(args, &args)) != NULL)
		{
			if (my_strnicmp(value, "-CLEAR", 3))
			{
				for (tmp = command_history_head; command_history_head; tmp = command_history_head)
				{
					new_free(&tmp->stuff);
					command_history_head = tmp->next;
					new_free(&tmp);
				}
				hist_size = hist_count = 0;
				command_history_pos = NULL;
				command_history_tail = NULL;
				command_history_head = NULL;
				return;
			}
			if (isdigit((unsigned char)*value))
			{
				max = my_atol(value);
				if (max > get_int_var(HISTORY_VAR))
					max = get_int_var(HISTORY_VAR);
			} 
			else
				match = value;
		}
		else
			max = get_int_var(HISTORY_VAR);
		for (tmp = command_history_tail, cnt = 0; tmp && (match || (cnt < max));
				tmp = tmp->prev, cnt++)
		{
			if (!match || (match && wild_match(match, tmp->stuff)))
				put_it("%d: %s", tmp->number, tmp->stuff);
		}
	}
}

/*
 * do_history: This finds the given history entry in either the history file,
 * or the in memory history buffer (if no history file is given). It then
 * returns the found entry as its function value, or null if the entry is not
 * found for some reason.  Note that this routine mallocs the string returned  
 */
char	*do_history (char *com, char *rest)
{
	int	hist_num;
	char	*ptr, *ret = NULL;
	static	char	*last_com = NULL;

	if (!com || !*com)
	{
		if (last_com)
			com = last_com;
		else
			com = empty_string;
	}
	else
		malloc_strcpy(&last_com, com);

	if (!is_number(com))
	{
		if ((ptr = history_match(com)))
		{
			ret = m_strdup(ptr);
			m_s3cat_s(&ret, space, rest);
			return ret;
		}
		say("No Match");
	}
	else 
	{
		hist_num = my_atol(com);
		if (hist_num > 0)
		{
			for (tmp = command_history_head; tmp; tmp = tmp->next)
			{
				if (tmp->number == hist_num)
				{
					ret = m_strdup(tmp->stuff);
					m_s3cat_s(&ret, space, rest);
					return ret;
				}
			}
		}
		else
		{
			hist_num++;
			for (tmp = command_history_head; tmp && hist_num < 0; )
				tmp = tmp->next, hist_num++;
			if (tmp)
			{
				ret = m_strdup(tmp->stuff);
				m_s3cat_s(&ret, space, rest);
				return (ret);
			}
		}
		say("No such history entry: %d", hist_num);
	}
	return NULL;
}

--- NEW FILE: opendir.c ---
/* 
 * opendir.c --
 *
 *	This file provides dirent-style directory-reading procedures
 *	for V7 Unix systems that don't have such procedures.  The
 *	origin of this code is unclear, but it seems to have come
 *	originally from Larry Wall.
 *
 *
 * SCCS: @(#) opendir.c 1.3 96/02/15 12:08:21
 */

#include "tclInt.h"
#include "tclPort.h"

#undef DIRSIZ
#define DIRSIZ(dp) \
    ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))

/*
 * open a directory.
 */
DIR *
opendir(name)
char *name;
{
	register DIR *dirp;
	register int fd;
	char *myname;

	myname = ((*name == '\0') ? "." : name);
	if ((fd = open(myname, 0, 0)) == -1)
		return NULL;
	if ((dirp = (DIR *)ckalloc(sizeof(DIR))) == NULL) {
		close (fd);
		return NULL;
	}
	dirp->dd_fd = fd;
	dirp->dd_loc = 0;
	return dirp;
}

/*
 * read an old style directory entry and present it as a new one
 */
#ifndef pyr
#define	ODIRSIZ	14

struct	olddirect {
	ino_t	od_ino;
	char	od_name[ODIRSIZ];
};
#else	/* a Pyramid in the ATT universe */
#define	ODIRSIZ	248

struct	olddirect {
	long	od_ino;
	short	od_fill1, od_fill2;
	char	od_name[ODIRSIZ];
};
#endif

/*
 * get next entry in a directory.
 */
struct dirent *
readdir(dirp)
register DIR *dirp;
{
	register struct olddirect *dp;
	static struct dirent dir;

	for (;;) {
		if (dirp->dd_loc == 0) {
			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
			    DIRBLKSIZ);
			if (dirp->dd_size <= 0)
				return NULL;
		}
		if (dirp->dd_loc >= dirp->dd_size) {
			dirp->dd_loc = 0;
			continue;
		}
		dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
		dirp->dd_loc += sizeof(struct olddirect);
		if (dp->od_ino == 0)
			continue;
		dir.d_ino = dp->od_ino;
		strncpy(dir.d_name, dp->od_name, ODIRSIZ);
		dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
		dir.d_namlen = strlen(dir.d_name);
		dir.d_reclen = DIRSIZ(&dir);
		return (&dir);
	}
}

/*
 * close a directory.
 */
void
closedir(dirp)
register DIR *dirp;
{
	close(dirp->dd_fd);
	dirp->dd_fd = -1;
	dirp->dd_loc = 0;
	ckfree((char *) dirp);
}

--- NEW FILE: keys.c ---
/*
 * keys.c:  Keeps track of what happens whe you press a key.
 *
 * Written By Michael Sandrof
 * Copyright(c) 1990 Michael Sandrof
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 *
 * Substantial re-implementation by Jeremy Nelson
 * Copyright 1998 EPIC Software Labs
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#include "irc.h"
static char cvsrevision[] = "$Id: keys.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(keys_c)
#include "struct.h"
#include "config.h"
#include "commands.h"
#include "history.h"
[...1474 lines suppressed...]
	LKEY(key_home, SCROLL_START)
	LKEY(key_end, SCROLL_END)
	LKEY(key_ic, TOGGLE_INSERT_MODE)
	/*LKEY(key_dc, DELETE_CHARACTER)*/
	LKEY(key_f1, CHELP)
	LKEY(key_f2, CHANNEL_CHOPS)
	LKEY(key_f3, CHANNEL_NONOPS)
#ifdef WANT_CDCC
	LKEY(key_f4, CDCC_PLIST)
#endif
	LKEY(key_f5, DCC_PLIST)
	LKEY(key_f6, DCC_STATS)
	LKEY(key_dc, TOGGLE_CLOAK)
}


void disable_stop(void)
{
	snew_key(0, 26, "SELF_INSERT");
}

--- NEW FILE: array.c ---
/*
 * IRC-II Copyright (C) 1990 Michael Sandroff and others, 
 * This file - Copyright (C) 1993,1995 Aaron Gifford
 * This file written by Aaron Gifford and contributed to the EPIC
 *   project by Jeremy Nelson to whom it was contributed in Nov, 1993.
 *
 * Used with permission.  See the COPYRIGHT file for copyright info.
 */
/*
	DATE OF THIS VERSION:
	---------------------
	Sat Nov 27 23:00:20 MST 1993

	NEW SINCE 20 NOV 1993:
	----------------------
  	Two new functions were added: GETMATCHES() and GETRMATCHES()

	BUGS:
	-----
[...973 lines suppressed...]
	an_array *array;

	if ((name = next_arg(input, &input)) &&
		(array = get_array(name)) && *input)
	{
		if (*input)
		{
			for (item = 0; item < array->size; item++)
			{
				if (wild_match(array->item[item], input) > 0)
					m_s3cat(&result, space, ltoa(find_index(array, item)));
			}
		}
	}

	if (!result)
		RETURN_EMPTY;

	return result;
}

--- NEW FILE: encrypt.c ---
/*
 * crypt.c: handles some encryption of messages stuff. 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: encrypt.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(encrypt_c)
#include "struct.h"

#include "encrypt.h"
#include "vars.h"
#include "ircaux.h"
#include "list.h"
#include "ctcp.h"
#include "output.h"
#include "newio.h"
#define MAIN_SOURCE
#include "modval.h"

static	void	add_to_crypt (char *, char *);
static	int	remove_crypt (char *);
static	char	*do_crypt (char *, char *, int);

#define CRYPT_BUFFER_SIZE (IRCD_BUFFER_SIZE - 50)	/* Make this less than
							 * the trasmittable
							 * buffer */
/*
 * Crypt: the crypt list structure,  consists of the nickname, and the
 * encryption key 
 */
typedef struct	CryptStru
{
	struct	CryptStru *next;
	char	*nick;
	char	*key;
}	Crypt;

/* crypt_list: the list of nicknames and encryption keys */
static	Crypt	*crypt_list = NULL;

/*
 * add_to_crypt: adds the nickname and key pair to the crypt_list.  If the
 * nickname is already in the list, then the key is changed the the supplied
 * key. 
 */
static	void add_to_crypt(char *nick, char *key)
{
	Crypt	*new;

	if ((new = (Crypt *) remove_from_list((List **) &crypt_list, nick)) != NULL)
	{
		new_free(&(new->nick));
		new_free(&(new->key));
		new_free((char **)&new);
	}
	new = (Crypt *) new_malloc(sizeof(Crypt));
	malloc_strcpy(&(new->nick), nick);
	malloc_strcpy(&(new->key), key);
	add_to_list((List **) &crypt_list, (List *) new);
}

/*
 * remove_crypt: removes the given nickname from the crypt_list, returning 0
 * if successful, and 1 if not (because the nickname wasn't in the list) 
 */
static	int remove_crypt(char *nick)
{
	Crypt	*tmp;

	if ((tmp = (Crypt *) list_lookup((List **) &crypt_list, nick, !USE_WILDCARDS, REMOVE_FROM_LIST)) != NULL)
	{
		new_free(&(tmp->nick));
		new_free(&(tmp->key));
		new_free((char **)&tmp);
		return (0);
	}
	return (1);
}

/*
 * is_crypted: looks up nick in the crypt_list and returns the encryption key
 * if found in the list.  If not found in the crypt_list, null is returned. 
 */
char	* is_crypted(char *nick)
{
	Crypt	*tmp;

	if (!crypt_list)
		return NULL;
	if ((tmp = (Crypt *) list_lookup((List **) &crypt_list, nick, !USE_WILDCARDS, !REMOVE_FROM_LIST)) != NULL)
		return (tmp->key);
	return NULL;
}

/*
 * encrypt_cmd: the ENCRYPT command.  Adds the given nickname and key to the
 * encrypt list, or removes it, or list the list, you know. 
 */
BUILT_IN_COMMAND(encrypt_cmd)
{
	char	*nick,
	*key;

	if ((nick = next_arg(args, &args)) != NULL)
	{
		if ((key = next_arg(args, &args)) != NULL)
		{
			add_to_crypt(nick, key);
			say("%s added to the crypt with key %s", nick, key);
		}
		else
		{
			if (remove_crypt(nick))
				say("No such nickname in the crypt: %s", nick);
			else
				say("%s removed from the crypt", nick);
		}
	}
	else
	{
		if (crypt_list)
		{
			Crypt	*tmp;

			say("The crypt:");
			for (tmp = crypt_list; tmp; tmp = tmp->next)
				put_it("%s with key %s", tmp->nick, tmp->key);
		}
		else
			say("The crypt is empty");
	}
}

extern	void BX_my_encrypt (char *str, int len, char *key)
{
	int	key_len,
		key_pos,
		i;
	char	mix,
		tmp;

	if (!key)
		return;
		
	key_len = strlen(key);
	key_pos = 0;
	mix = 0;
	for (i = 0; i < len; i++)
	{
		tmp = str[i];
		str[i] = mix ^ tmp ^ key[key_pos];
		mix ^= tmp;
		key_pos = (key_pos + 1) % key_len;
	}
	str[i] = (char) 0;
}

extern	void BX_my_decrypt(char *str, int len, char *key)
{
	int	key_len,
		key_pos,
		i;
	char	mix,
		tmp;

	if (!key)
		return;
		
	key_len = strlen(key);
	key_pos = 0;
	/*    mix = key[key_len-1]; */
	mix = 0;
	for (i = 0; i < len; i++)
	{
		tmp = mix ^ str[i] ^ key[key_pos];
		str[i] = tmp;
		mix ^= tmp;
		key_pos = (key_pos + 1) % key_len;
	}
	str[i] = (char) 0;
}

static	char	*do_crypt(char *str, char *key, int flag)
{
	int	c;
	char	*ptr = NULL;

	c = strlen(str);
	if (flag)
	{
		my_encrypt(str, c, key);
		ptr = ctcp_quote_it(str, c);
	}
	else
	{
		ptr = ctcp_unquote_it(str, &c);
		my_decrypt(ptr, c, key);
	}
	return (ptr);
}

/*
 * crypt_msg: Executes the encryption program on the given string with the
 * given key.  If flag is true, the string is encrypted and the returned
 * string is ready to be sent over irc.  If flag is false, the string is
 * decrypted and the returned string should be readable 
 */
char	*crypt_msg(char *str, char *key)
{
	char	buffer[CRYPT_BUFFER_SIZE + 1];
	char	thing[6] = "";
	char	*ptr;

	sprintf(thing, "%cSED ", CTCP_DELIM_CHAR);
	*buffer = (char) 0;
	if ((ptr = do_crypt(str, key, 1)))
	{
		strmcat(buffer, thing, CRYPT_BUFFER_SIZE);
		strmcat(buffer, ptr, CRYPT_BUFFER_SIZE-1);
		strmcat(buffer, CTCP_DELIM_STR, CRYPT_BUFFER_SIZE);
		new_free(&ptr);
	}
	else
		strmcat(buffer, str, CRYPT_BUFFER_SIZE);

	return (m_strdup(buffer));
}

/*
 * Given a CTCP SED argument 'str', it attempts to unscramble the text
 * into something more sane.  If the 'key' is not the one used to scramble
 * the text, the results are unpredictable.  This is probably the point.
 *
 * Note that the retval MUST be at least 'BIG_BUFFER_SIZE + 1'.  This is
 * not an oversight -- the retval is passed is to do_ctcp() which requires
 * a big buffer to scratch around (The decrypted text could be a CTCP UTC
 * which could expand to a larger string of text.)
 */ 
char 	*decrypt_msg (char *str, char *key)
{
	char	*buffer = (char *)new_malloc(BIG_BUFFER_SIZE + 1);
	char	*ptr;

	if ((ptr = do_crypt(str, key, 0)) != NULL)
	{
		strmcpy(buffer, ptr, CRYPT_BUFFER_SIZE);
		new_free(&ptr);
	}
	else
		strmcat(buffer, str, CRYPT_BUFFER_SIZE);

	return buffer;
}

--- NEW FILE: banlist.c ---
/*
 * this code is Copyright Colten Edwards (c) 96
 */
 
#include "irc.h"
static char cvsrevision[] = "$Id: banlist.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(banlist_c)
#include "struct.h"
#include "commands.h"
#include "list.h"
#include "hook.h"
#include "ignore.h"
#include "ircaux.h"
#include "output.h"
#include "screen.h"
#include "server.h"
#include "window.h"
#include "who.h"
#include "whowas.h"
[...1501 lines suppressed...]
			defban = 5;
			break;
		case 'N':
			defban = 1;
			break;
		default :
			return;
	}
	bitchsay("BanType set to %s", 
		bantypes[defban >= 1 && defban <= 7 ? defban : 0]);
}

BUILT_IN_COMMAND(bantype)
{
	if (args && *args)
		set_default_bantype(*args, helparg);
	else
		bitchsay("Current BanType is %s", 
			bantypes[defban >= 1 && defban <= 7 ? defban : 0]);
}

--- NEW FILE: misc.c ---
/* 
 *  Copyright Colten Edwards (c) 1996
 */
#include "irc.h"
static char cvsrevision[] = "$Id: misc.c,v 1.1.1.3 2003/06/11 07:00:42 root Exp $";
CVS_REVISION(misc_c)
#include "struct.h"

#include "server.h"
#include "dcc.h"
#include "commands.h"
#include "encrypt.h"
#include "vars.h"
#include "ircaux.h"
#include "lastlog.h"
#include "window.h"
#include "screen.h"
#include "who.h"
#include "hook.h"
[...5086 lines suppressed...]
	{"NAME", "Individuals" },
	{"ORG", "Organization" },
	{"PRO", "Professional" },
	{NULL, NULL}
};
char *p;
int i = 0;
	if (!hostname || !*hostname || isdigit((unsigned char)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;
#endif
	return "unknown";
}


--- NEW FILE: winbitchx.c ---
#include <windows.h>
/*#include <mingw32/process.h>*/

/* Seems to have problems getting these function declarations
 * -- going to try createthread manually this time --
 */
/*unsigned long
	_beginthread	(void (*pfuncStart)(void *),
			 unsigned unStackSize, void* pArgList);
			 void	_endthread	();*/


#include "input.h"
#define         WIDTH   10

char *lastclicklinedata = NULL;
int lastclickcol, lastclickrow;

char *codeptr, *paramptr;
[...3376 lines suppressed...]
void gui_remove_menurefs(char *menu)
{
}

void gui_mdi(Window *window, char *text, int value)
{
}

void gui_update_nicklist(char *channel)
{
}

void gui_nicklist_width(int width, Screen *this_screen)
{
}

void gui_startup(int argc, char *argv[])
{
}


--- NEW FILE: cdcc.c ---
/* CDCC v2 1.00 (c) Copyright 1996 William Glozer  */
/* ----------------------------------------------- */
/* Last revision 04.19.96 by Ananda                */

/* New CDCC for BitchX and any other clients that deserve to run it :) */
/* Note that I did use a lot of code/ideas from a copy of CDCC written */
/* for BitchX by panasync, so thanks to him for alot of the code and   */
/* ideas :)  I would appreciate any bugs reported to me as soon as is  */
/* possible, and cdcc.c + cdcc.h for any mods you do... if you modify  */
/* it for your client, I can add those mods as #ifdefs so the next ver */
/* will work with your client and have all the new stuff.  -Ananda '96 */
/* Modifed even more by panasync (edwards at bitchx.dimension6.com) to    */
/* interface cleanly and nicely with BitchX. Blame all bugs on me      */
/* instead of Ananda                                                   */

#define CDCC_FLUD

#include "irc.h"
static char cvsrevision[] = "$Id: cdcc.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
[...1916 lines suppressed...]
#else /* WANT_CDCC */

/* this is required for functions.c to compile properly */

BUILD_IN_FUNCTION(function_cdcc)
{
	return m_strdup(empty_string);
}

BUILT_IN_FUNCTION(function_sendcdcc)
{
	return m_strdup(empty_string);
}

int get_num_queue(void)
{
	return 0;
}

#endif

--- NEW FILE: exec.c ---
/*
 * exec.c: handles exec'd process for IRCII 
 *
 * Copyright 1990 Michael Sandrof
 * Copyright 1997 EPIC Software Labs
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#include "irc.h"
static char cvsrevision[] = "$Id: exec.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(exec_c)
#include "struct.h"

#include "dcc.h"
#include "exec.h"
#include "vars.h"
#include "ircaux.h"
#include "commands.h"
#include "window.h"
[...1414 lines suppressed...]
 */
void set_process_bits(fd_set *rd)
{
	int	i;
	Process	*proc;

	if (process_list)
	{
		for (i = 0; i < process_list_size; i++)
		{
			if ((proc = process_list[i]) != NULL)
			{
				if (proc->p_stdout != -1)
					FD_SET(proc->p_stdout, rd);
				if (proc->p_stderr != -1)
					FD_SET(proc->p_stderr, rd);
			}
		}
	}
}

--- NEW FILE: server.c ---
/*
 * server.c: Things dealing with server connections, etc. 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */
#ifdef IRIX
#define _HAVE_SIN_LEN 1
#define _HAVE_SA_LEN 1
#define MAXDNAME 100
#endif

#include "irc.h"
static char cvsrevision[] = "$Id: server.c,v 1.1.1.2 2003/06/11 07:00:43 root Exp $";
CVS_REVISION(server_c)
#include "struct.h"
[...3697 lines suppressed...]
int 	save_servers (FILE *fp)
{
	int	i = 0;

	if (!server_list)
		return i;		/* no servers */

	for (i = 0; i < number_of_servers; i++)
	{
		/* SERVER -ADD server:port:password:nick */
		fprintf(fp, "SERVER -ADD %s:%d:%s:%s\n",
			server_list[i].name,
			server_list[i].port,
			server_list[i].password ? 
				server_list[i].password : empty_string,
			server_list[i].nickname ?
				server_list[i].nickname : empty_string);
	}
	return i;
}

--- NEW FILE: hook.c ---
/*
 * hook.c: Does those naughty hook functions. 
 *
 * Written By Michael Sandrof
 * Rewritten by Jeremy Nelson and then rewritten to use
 * hash'd lists by Colten Edwards
 * Copyright(c) 1997 
 */

#define __hook_c
#include "irc.h"
static char cvsrevision[] = "$Id: hook.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(hook_c)
#include "struct.h"

#include "hook.h"
#include "vars.h"
#include "ircaux.h"
#include "if.h"
[...1819 lines suppressed...]
			}
		}
	}

	for (x = 0; x < NUMBER_OF_LISTS; x++)
	{
		for (list = hook_functions[x].list; list; list = next)
		{
			next = list->next;
			if (!my_stricmp(list->filename, package))
			{
				remove_hook(x, list->nick, list->sernum, 1);
				ret++;
			}
		}
	}
	return ret;
}

#endif

--- NEW FILE: art.c ---

#include "irc.h"
#include "output.h"
#define MAIN_SOURCE
#include "modval.h"

void do_ansi_logo(int i)
{
	put_it("");

#ifdef ASCII_LOGO
#ifndef BITCHX_LITE
	switch(i)
	{
		case 0:
put_it("::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
put_it("::::.....:::::::::::::::::::.....::::::::::::::::::");
put_it("::::::::.:::::::::::::::::::::::::.::::::::::::::::::::::");
put_it("::::::::::.`:::::::::::::::::::::'.:::::\"`````````````````\"");
put_it("::::::::::::.`:::::::::::::::::'.:::::: BitchX by panasync!");
put_it("::::::::::::::.`:::::::::::::'.:::::::::...................");
put_it("::::::::::::::::.`:::::::::'.::::::::::::::::::::::::::::::");
put_it(":::\"\"````\"\"::::'.g$$S$'`:::::'\"::::\"\"```\":::::::::::::::::::::");
put_it("'s#S$$$\"$$S#n.` $$$$S\". s#S$$$ `:'   .g#S$$\"$$S#n. s#S$$$ `\"\":::::::::::::::::");
put_it(" $$$$$$_,$$$S'rE.g#S$$$ $$$$$$ssn    $$$$$$ $$$$$$ $$$$$$\"$$S#n.`:::::::::::::");
put_it(" $$$$$$`\"$$SSn. $$$$$$$ $$$$$$ gg#S$ $$$$$$ gggggn $$$$$$ $$$$$$ ::::\"````````");
put_it(" $$$$$$  $$$$$$ $$$$$$$ $$$$$$ $$$$$ $$$$$$ $$$$$$ $$$$$$ $$$$$$ ::: Greets To");
put_it(" $$$$$$  $$$$$$ $$$$$$$ $$$$$$ $$$$$ $$$$$$ $$$$$$ $$$$$$ $$$$$$ ::: Trench,");
put_it(" $$$$$$ ,$$$$$$ $$$$$$$ $$$$$$ $$$$$ $$$$$$ $$$$$$ $$$$$$ $$$$$$ ::: Lifendel,");
put_it(" $$$$$$ss$$$$S' $$$$$$$ `S$$$$s$$$S' `S$$$$s$$$$S' $$$$$$ $$$$$$ ::: JondalaR,");
put_it("..............::........::.::......:.......::: Zircon,");
put_it("::::::::::::::'.:::::::::::::.`::::::::\"```````' Otiluke,");
put_it("::::::::::::'.:::::::::::::::::.`::::: HappyCrappy, Yak,");
put_it("::::::::::'.:::::::::::::::::::::.`::: Masonry, BuddhaX..");
put_it("::::::::':::::::::::::::::::::::::`::....................");
put_it("::::\"\"'`\":::::::::::::::::::\"'`\"\"::::::::::::::::::");
put_it("::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");

			break;
		case 1:
put_it(" l$$S'.::::... `\"~^\"'  `\"l' ..::::::::::::.`\"\".::..\"|...:::.`S$$$l");
put_it(" $$$ ::::::::::.| ::::::::::::::::::::::::::.:::::::::.`$$$");
put_it(" $$$ ::::::::::::..  `:::::::::::::::::::::'.   .:::::::::::: $$$");
put_it(" $$l ::::::::::::::.`:::::::::::::::::'.:::::::::::::: l$$");
put_it(" $$| ::::::::::::::::.`:::::::::::::'.:::::::::::::::: l$'");
put_it(" $$'.::::::::::::::::::.`:::::::::'.:::::::::::::::::: l'_");
put_it(" $g ::::::\"\"````\"\"::::'.g$$S$'`:::::'\"::::\"\"```\"::::::::: | L");
put_it(" l\".:::'s#S$$$\"$$S#n.` $$$$S\". s#S$$$ `:'   .g#S$$\"$$S#n. s#S$$$ `\"\":::::: :,$");
put_it(" | :::: $$$$$$_,$$$S'rE.g#S$$$ $$$$$$ssn    $$$$$$ $$$$$$ $$$$$$\"$$S#n.`::',$$");
put_it("_,L`::: $$$$$$`\"$$SSn. $$$$$$$ $$$$$$ gg#S$ $$$$$$ gggggn $$$$$$ $$$$$$ :: $$$");
put_it("`\"$n\":: $$$$$$  $$$$$$ $$$$$$$ $$$$$$ $$$$$ $$$$$$ $$$$$$ $$$$$$ $$$$$$ :: $$$");
put_it(" $$'L:: $$$$$$ ,$$$$$$ $$$$$$$ $$$$$$ $$$$$ $$$$$$ $$$$$$ $$$$$$ $$$$$$ :: $$$");
put_it(" $$.\":: $$$$$$ss$$$$S' $$$$$$$ `S$$$$s$$$S' `S$$$$s$$$$S' $$$$$$ $$$$$$ :: $$$");
put_it(" $$. ::..............::...... BitchX by Panasync! ...:.......:: $$$");
put_it(" $$| ::::::::::::::::'.:::::::::::::.:::::::::::::::'.$$$");
put_it(" $$l.`:::::::::::::'.:::'\"::::::::::::.`::::::::::::: l$$$");
put_it(" $$$l `::::::::::'.::::: |`:::::::::::::.`::::::::::',$$$$");
put_it(" $$S$s,._`\"\":::':::::'_al-`::::::::::::::`::::\"\"',S$$$$$");
put_it(" $$$S$$$$$SS#ss,.__`\":::`$$s.__`\":::::::\"'  ___.,,,sss##SSSS$$$$$S\"`");
put_it(" `\"S$$$$$$$$$$$$$$$$$$$$$$$$$SSSSSS$'~\"\"\"\"\"\"\"\"\"~~~~~~~~\"\"\"\"\"\"\"\"\"\"\"\"``````'");
			break;
		case 2:
#endif
put_it("                                                                   ,");
put_it("                                           .                     ,$");
put_it("                 .                                              ,$'");
put_it("                                           .        .          ,$'");
put_it("                 :      ,g$p,              .         $,       ,$'");
put_it("               y&$       `\"` .,.           $&y       `$,     ,$'");
put_it("               $$$     o oooy$$$yoo o      $$$        `$,   ,$' -acidjazz");
put_it("         .     $$$%%yyyp, gyp`$$$'gyyyyyyp, $$$yyyyp,   `$, ,$'     .");
put_it("       . yxxxx $$$\"`\"$$$ $$$ $$$ $y$\"`\"$$$ $$$\"`\"$$$ xxx`$,$'xxxxxxy .");
put_it("         $     $$7   l$$ $$$ $$$ $$7   \"\"\" $$7   ly$     .$'       $");
put_it("         $     $$b   dy$ $$$ $y$ $$b   $$$ $$b   d$$    ,$`$,      $");
put_it("       . $xxxx $$$uuu$$$ $$$ $$$ $$$uuu$$$ $$$   $$$ x ,$'x`$, xxxx$ .");
put_it("         .           \"\"\" \"\"\" \"\"\"       \"\"\"       \"\"\"  ,$'   `$,    .");
put_it("           b i t c h    -      x                     ,$'     `$,");
put_it("                                                     $'       `$,");
put_it("                                                    '          `$,");
put_it("                                                                `$,");
put_it("                                                                 `$");
put_it("                                                                   `");


#ifndef BITCHX_LITE
			break;
		case 3:
put_it("                    :                            :");
put_it("                 ::::::::::                ::::::::::");
put_it("                 :  :    _:______    ______:_    :  :");
put_it("                 ::::  _\\       \\  /       /    ::::");
put_it("                         _ ___  _ \\/_      /__ _______");
put_it("     :::::::::  _________/\\___/\\__  /__ _____/\\__    /  ::::::::::");
put_it("     :         \\_____   ___  __   _________   __   /_           :");
put_it("     : :::  .     /  /____/   /   /___/    /___/  ___ \\    . ::: :");
put_it("     : : :  :   _/  /_   \\   /       //   /   /   /   /    : : : :");
put_it("     :::::  |  \\_____  /___/\\___  /\\____  /___/   /     | :::::");
put_it("       :    +------- \\/ ------- \\/ ---- \\/ - /___// ----+   :");
put_it("       :         :::::  //_______/  \\_______\\_ :::::          :");
put_it("       :             :    :                :    :              :");
put_it("                  [ b i t c h  X  i r c  c l i e n t ]");
put_it("                     :    :   by panasync  :    :");
put_it("                     ::::::::::        ::::::::::");
put_it("                          :   :[blaze] :   :");
put_it("                          :::::        :::::");
 
			break;
		case 4:
put_it("________   ________ ________     ________");
put_it("\\//___________\\/________\\\\/_________\\_//");
put_it("___\\    ___   _________ _________ \\//");
put_it("<<_____ \\  /> \\     /____\\     >>\\ ___");
put_it("____/______\\_____<<_____//___________>>  /_______\\   /_____>>sm");
put_it("<<___________   bitchx by panasync /______\\\\   ____");
put_it("/------------------------------------------------\\\\");
put_it("");
put_it("");
			break;
		default:
			break;
	}
#endif

#else

#ifndef BITCHX_LITE
	switch(i)
	{
		case 0:
put_it("   ");
put_it("     ÜÜÜ     ");
put_it("      ÜÛßßÜÜÜÜÜ°²                    ÞÜÜÜ ");
put_it("    ÜÜÛßßß   ÜÜÜÜÜÛßßß                  ß ßßßßÛÜÜÜÜ ÜÜÜ");
put_it(" ßß ÜÜÜÜßß²Ûß ß          ßß²²ÜÛÛßß");
put_it(" Û°°ÛÛ ÜÜÛÛ²Ýß ÞÛÝ Ý   ÜÜÜÜ      Þ  ÛÛ  ÞÛÛÛÛ   ÞÜÜÜÞÞÛÛßß  ");
put_it(" Û²²ÜÜÜÜÜÜÜÜÜÜÜ ßßßÛÜ    ÜÜÛÛ²ßÛÛÜÜ   Û°°°Û    ÜÜÜÛÛ°°°  ÞÞßÛÛÛÛÜÜÜ");
put_it(" ÛÛÛßÝÝßßßÛÛÜÜÜÜ ß ÞÞÛ²ÛÛÜÜÞÛÝÝ ÞÞ²²ÝÝÜÜÜÜÛ²Û²° Þ°²²²ÝÝ ÛÝß²²ÛÛÛßßß");
put_it(" ÞÞßÞÝÝßÝßßÛÛÛÛÜÜ ßßßßÞÛÛÛßß  ÛÛÛÛÝ ß   ÛÛÛÝÝ ÜÜÜÜÛÛ ÛÛ²ÝÝ ÛÝ Üßßß ÜÜÛ");
put_it("ÜÞÞÜ°ÛÛ ÝÜ°²ÞÞ²²Û²°Ý   ßßßß ÜÜ ÞÞÛÛÛ Ý   ÞÞÛÛܲ²Ûßßß  ÞÞÛÛÛÛ Þßß ÜÜÜÛÛÜÜÜ");
put_it("ÝÞÞÛ²Û  ßß   ÜÛßßßß   ÜÜÜÜÜÛÛÛÝÝ  ÛÛÛÛÛ   ÜÜÛÛÜÛßß       ÛÛÛÛ° ÜÜÞßßßßÛÛÛ²°");
put_it("Ý ÛÛÛÛÛßßßßÞÞÝ ÜÛÛ ÞÞÛ²ÛÝÝ  ÞÞÛÝßÛÝÝ   Pe^  ÞÞ²²ÛÛßß    ÛÛ²ÛÝÝ");
put_it("° ÛÛÛßßßßßßÛÛÜÜÜÜÜÜÞÞÝ°ÛÝÝ ÛÛÝ°ÛÝÝ   ßßÛÜÜÛÜÜ        ÛÛÛÛ      ÞÞß°Û Ü");
put_it("° ÛÛÛÛÝÝ  ÜÜÜ ßßßßÛÛßÛÜÜÜ  ÛÛܲÛÝÝÞÞÛ ÞÛÛ ÜÞ    ßßßÛÛÛÜÜÜ    ÞÞÛÛÝÝ °² ÞÞ ÞÝÝÝ");
put_it("  ÞÞß²ÝÝß°ÜÜÜÜ ßßÛÜ ßßÛÛÛÛÛÛÛÜܲßÛÛÛÛ ÛÜÞÞÛÛÛÛßßÛÛÛÜÜÜ  ÛÛÝÝÛÛÛÛÛÜÝ");
put_it("  ÞÞ²ÛÛÜÜ    ÞÞ²ßÝÝ ÞÞÛÝ °²²ÛÛÛÛÛÛÛÛÛÛßßß     ÜÜÛÛÛßß Ü ßßßÛÛÛÜÞÛÛÛ    Ûßß Ý°²");
put_it("  °²°Ýßßß    ßßßÜÜÛßÛ²²Ûßßßß    ßßßßÛÜÜÜÜÜÛßßßß ÜÜÜßÛÜÜ  ßßÛÛßÛÛ    ÛÜ    ");
put_it("  °²         ÜÜÜÜÜÛÛÛÝÝÞßß         ßßÛÛÜÜ  ßÛßÛÜ °²²°Ý  ÞÞ²²²°Ý    ");
put_it("         ÞÝPhonyEye    ÞÞÛ²°Ý  Þ ÞßÛ²Ûßß             ");
put_it("[%15s]  Þß^ÜÜÛßßß  ÜÛ ßÝßß°²Awe/Cia", irc_version);
put_it("             ß    ÛÛ ß      Ý");
put_it("                    ß   ");
put_it("             ");

			break;
		case 1:
#endif


put_it("                                                         ÛÛÛßÛÛÛÛÛÛÛÛÛßܱ²ÜßÛÛ");
put_it(" ±            ±ÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛ°       ÛßßÜÝÞÛÛÛÛÛÛßÜ°±²ÛßÜÛÛ");
put_it(" ²Ü    ß±²ÜÜÜÛÛÛÜß    Ü    ß±Ü   ÜÜÜÜ°±²ÝÞÛÛÛÛßÜ°±ßßßÜÛÛÛÛ");
put_it(" ÛÛÜßßßßßÛÛÛ²²Ûßßßß±²ÜßÛßÜßßÜÜÛÛÛÛÛÛÛÛ");
put_it(" ²²Û²²²ÜßßßßßÛ²ÛÛÛÛÜÜÜßþÜþÛÛÛÛÛÛÛÛÛÛÛÛ");
put_it(" ±±²±±±±²± ÛÛÛÛßÜ ÛÜßÜÜßßßÛÛÛÛÛÛÛ");
put_it(" °°±°°°°±° ÛÛßÜ°ÝÞÛÛÝÞÛ²±°ÜÜÜßÛÛÛ");
put_it("  Ü° Ü    ß    Ü Ü    ß    Ü°ÛÛßܱ° ÛÛÛÛ ²±± ° ÛÛÜÜÜ");
put_it("                                                    ÛÛßܲ±° ÝÞÛÛÛÛÛ ±°°ÛÛÛßßÜÜ");
put_it("ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÝÞ²±°  ÛÛÛÛÛÛÛ °ßßÜÜÛÛÛÛ");
put_it("ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜßßßßÜÛÛÛÛÛÛÛÛÛÜÛÛÛÛÛÛÛÛ");
put_it("");


#ifndef BITCHX_LITE
			break;
		case 2:
put_it("Üß  ß");
put_it("ÛÜþÜß ß Ü     ");
put_it("ß þ Ü ÜÛßÜÜ°±²²Ü ");
put_it("ßÜÛÜ ÜÜÛ°°±±²²±°   ");
put_it("    ßÜ  Ü  ÜÛ°°±±²²²±ß");
put_it("                °±°Û  ÜÛßÛ°°±±²²ß");
put_it("            Ü°²±°ÛÛÜÜÛÛ²±°ßßÛßß");
put_it("    ß±ÛÛÛÛÛÛÜÜ  ÜÛÜÛ²±°ß ");
put_it("ÜÜÜÜÜÜÜÜ     ÜÜÜÜÜÜßßßßßßÛÜÜÜÜ  ÜÛÜÛ²±°ß");
put_it(" ° Û   ÛÛ ßÛÛ ° ÛÛÜÜ  ° ÛÛÛÛÛÛÛÛÛÛÛÜÛÜÛÛ²±ß");
put_it("Û  ÜÛß  ÛÛÛÛ      ÛÛ ßÛÛÛÛÛÛÛÜÜÛÛÛß");
put_it("  Û  ßÛÜ  ÛÛÛÛ      ÛÛ  ÛÛÛß ÛÛÛßÛÛ   by panasync");
put_it(" Û   ÛÛ  ÛÛ   ÛÛ  ÛÛ  ÛÛ  ÛÛ°±²ÛÛÛßÛÛÛ ");
put_it("ÜÜ  ÜÜÛÛ ÜÛÛ ÜÜ ÛÜÜÛÛ Ü ÛÜÜÛÛÛßßÛÛÜÛÛÛÛÛÜ");
put_it("   Ü°±²ÛßÛß ßÛÛÛÛÛÛÛÛ ");
put_it("*Spark it Up*Ü°±²ÛßÛßßßÛÛÛÛÛÛÜ  ");
put_it("Ü°±²ÛßÛßßÛÛÛÛÛÛ ");
put_it("ß°±²ÛÛß  [Joker]ßßÛÛÛÛÜ");
put_it(" ß°ß  ßÛÛÜ");
put_it("   ßß");
put_it(space);
			break;
		case 3:
put_it(space);
put_it(space);
put_it(" ÜÜÜÜÜÜÜ°ÜÜÜ  Ü");
put_it("Þ°ÜÛÜÜÛßß±²ÛÛ²Üܱ߰ܲÛÛÜÜÜÜ²Ü   ßÜÜ  ÜÜß°±²ÛÛÝ Þ°±ÜÜÜÛß");
put_it("ÛÛÛ²ÛÝ  Þ°ÛÛÛÛß    ÜÜÜÜß°ÛÛ²Û  ² ÛÜÛÛÝ ÞÞßß±²ß ±²Û²²Û");
put_it("  Ûßßß Û°ß±²ÝÜÜ°±ß²Ûß Ü°Ü²ÛÛÛ °ßß±²Ýß°ßß±² ßßß°ßß±²Ü ÜÜÜ  ßßßßß°");
put_it("  Û ±² ÜÜÜÜÜÜ ßÜÜÜÜÜ  ÜÜÜÜÜÜÜ ÜÜÜÜÜÜ ßÜÜÜÜÜÛßÜÜÜÜÜ ß°ÜܲÛÛßÜÜÜÜÜÜßßßÜÜܱÜßÛÛ °");
put_it("  Û ÜÜÝÞ±²ÛÛÛÝ ÞÞÛÛÛÛÜ °ÛÛ²Û  Þ°±²ÛÛÝ Þ°±ÛÛÛÝÞ°±ÛÛÝ ÞÞÛÛ²²ÝÞ°ÞÛÛÛÝ°±ÞÞÛÛÛÝÞÛ Û");
put_it("  ° ÛÛ °±ÛÛ²²Û °±Û²²ÛÝÞ°ß±²ÛÝÜßßß±²ßßÜÜܲ²Ûß °±ßÛ²Û  °²ÛÛß Û°ßß±²Û  Ûßßß°ßÜß Û");
put_it("  ° ÛÜßßßßÜÜßßßÜ°ßßßÜßßÜÜÜßßß ²±ßßß   ßßß ²Ü±Ü±ßßßÜÜßßßÜÜÜÜßß ÜÛÛÛÜÜÜß Û");
put_it("  Û ÛÛÛß°±ÛÜßßßÜÜ  ßß±²ÛßÜÜßßÜÜÜÜÜÜÛÛÛ²²ÛÛÛÛ²ÜÜÜ  Ü  ÛÛÛ²²ÛÛÛÛÛß ß°±ÛÛß±ÛÛÜ  ±");
put_it("  Û ÛÛÜ  ßßÛÛ²±²ÛÛÜÜÛÛßÜß ÜܱÛÛßßßß  ܱßßß  ܱ²ÛÛÛÜßßÜßÛß²ÛÛÛÛÛÛÜܲÛß Ü²²ÛÛÛ ±");
put_it("  ± ÛÛÛÛܱ²ÛÛß ß°ßß²ÛÝÞÝpÞÞ²²²ÛÛÝ ° ÞÞÛÝ   ÞÞÛÛÛÛ²²ÝXÞÝß ß°ßÛ²²ÛÛÛÛÛÛÜ°±ÛÛÛÛ °");
put_it("  °ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÛÜ ßß±ÛÛÛ²²Ü ß°ß ÜÜÜ°ÛÛÛÛ²²ß ÜÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ°");
put_it("ßßß ÜÜÜß ß°ÜÜßßßßß ");
put_it("%17s irc client    Þ°Û²²ÜÜ Þ±²Ý", irc_version);
put_it("-by Panasyncß°ßßß Ü°Û²²");
put_it("ÜÜ°ÜÛ²²Ý");
put_it("ßßßß±ß");
put_it(space);
			break;
		case 4:
put_it("²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²²Û²Û²Û²Û²Û²Û²²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û²Û");
put_it("±²±²±²±²±²±²±²±²±²±²±²±²±±²±²±²±²±²±²±±²±²±²±²±²±²±²±²±²±²±²±²±²±²±²±²±²±²±²±²");
put_it("°±°±°±°±°±°±°±°±°±°±°±°±°°±°±°±°±°±°±°°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±");
put_it("Û°ÛßßßßßßßÛ°Û°Û°Û°Û°ßßßßßÛ°Û°Û°ßßßßßßßÛ°Û°Û°Û°Û°ÛßßßßßßßÛ°Û°Û°Û°Û°ßßßßßßÛ°Û°Û°");
put_it("ßÜÜܲÛÛßß ÛÛÛÛÛÛÛ ÜÜÜÛÛ°ÜÜÜÞ ÜÜܲÛÛßß ÛÛÛÛÛÛÛ ÜÜܲÛÛßßÛÜÜÜÜ ßß ÜÜܲÛÛßß ÛÛÛÛÛÛ");
put_it("Þ²Û²±²Ý ßßßßßßßÛÛ ßß°°ÛÛ°ßß Þ²Û²±²Ý ßßßßßßßÛßÞ²Û²±²Ý²²Û²Þ²Û²±²Ý ßßßßßßßß");
put_it("Û±²±°±ßßßßÛÛ±ÛÜ Û °ÜÜÜ ÜÜÜ°Û±²±°±ßßßßÛÛ±ÛÜ  Û±²±°±±±ßß Û±²±°±ßßßßÛÛ±ÛÜ");
put_it("Û°±°Û°Þ°±°Ý Þ±°±Û±Û°°Ý Û°±°Û°Û°±°Û°ÜÜÜ Û°±°Û°Þ°±°Ý");
put_it("ßßßßßßßßßß ßßßßßßßßß  ßßßßßßßßßßßßßßßß ßßßßßßßßßß");
put_it("Û°°±°±°°±²Û°°°±°±°±Û°°±°±°°±²Û°°±°±°°±²Û°°±°±°°±²");
put_it("Þ²±²±²Ý    Þ±±²Ý Þ±±±²±²±²Ý Þ²±²±²Ý    Þ±±²Ý Þ²±²±²Ý    Þ±±²Ý Þ²±²±²Ý    Þ±±²Ý");
put_it(" ß²Û²Û²ÜÜÜÛÛ²ßß   ²Û²Û²Û²ÛÛ  ß²Û²Û²ÜÜÜÛÛ²ßß ndß²Û²Û²ÜÜÜÛÛ²ßß   ß²Û²Û²ÜÛÛ²ßß");
put_it(space);
put_it("ßÛ²Ü    Ü°°ß");
put_it("±²Û ܱÛß");
put_it("ÜÜÞ±²Ýßß");
put_it("ܲ±ß °±±");
put_it("ÜÛ²ß    ß°ÛÜÜ");
put_it("ßßßßßß");

			break;
		case 5:
put_it(" ");
put_it(space);
put_it("Üþ");
put_it("ÞÝ°°");
put_it("ßÜ ßß° °°ÞÞÛÛÛÛ²²°°ÛÛÛÛ²²°° °");
put_it("ÜßÛÜÜÜ°° ßßßÛÛÛÛ²²°°ßßßß");
put_it("°ÝÛÛÛÛ²²ÛÜÜÜÜ ßßßß ÜÜßßßß");
put_it("²ÝÛÛÛÛÛÛÛÛ Ý ßßÜ ÜßÝÜÜÛßß");
put_it("ÛÝÛÛÛÛÛÛÛÛ ÝÞß ÞÛÝ ÝÞ°Ý°°");
put_it("ÜÜÛÛÛÛÛÛÛÛÜÛÜßÞß ßÜÛ ßÝ°°ßÜÜÜÜÛ");
put_it("Üß  Ûßßݲ²ßÛ Ý Üþ ß ÜÜÜÛܲ²ÜÜßÜÜÜÛÛßß");
put_it("ÞÝ °°°  Þ°° Û ÝÞÛ °ÜÜÛÛÛÛÛÛßßßÜÜ°²²ßß");
put_it("ßÜÜ ²²² Þ ÝÞ° ²ÞÛÛ²²ÛÛÝÝ° ÜÜÜÜÜÜÜ°ÛÜßÜÜß°²°ß");
put_it("Üßßßß °° Þ ÝÞ² Ûßßß°°ßßÛ ° ²Ý°   °ÜÜ ²Ý  þÜ  ßÜ  Üß  Üß");
put_it("Þ°°ÛÛÛÜÜßßÜÝßÛÜÞ°  ²²  ÝßÞ² ÞÝ²Ý Þ²Þ² ÞÝ  Þ²   °²Ü ßܲ");
put_it("Þ²²°° ÛÛÝ ÞÝÜÜ Üßþ °° Þ²°° ° ÞÝ ÞÝ°°ÞÛÝ ÞÝ° ÞÛÝ ÞÝ° ÞÝ°° Üß ßÜ ßÜ °° °");
put_it(" ° °°²²ÛÛ²²°°ß þßÜÞÝ ÜÜ Üßßß°Üßß ßþ  ßßþ  ßß  Üß  ÜßßÜ");
put_it("Þ²²ßßßß  ßßß ßÜÜ ÜÜßßßÜ°²°ÜßßÜ");
put_it("°°ÞÜßÜܲ²°ßßÜ");
put_it("°°ÞÝÜÜÛÛßßßÜ");
put_it("ßþ ßßß");
put_it("ÿ");
			break;
		case 6:
put_it("Ü");
put_it(" bitchx!        Û ÜÜÜ ßÜÜß");
put_it("  ÜÜÜÜÜÜÜ °°°°°°°°°°ÜÜÜÜÜÜÜÛÛ²ÜÜÜÜ °°°°  ßÜ °° ÜÜÜÜÜÜ   °°°°°°  °°°  ");
put_it("°ÛÛ²ÛÛÜÜÜÜ      ÜܲÜÜÜÜ ßßßß²°ßßßßß Û  Ü    Û²²ÛÜÜÜÜ");
put_it("   ÜÜ  Û²Û²²²°  ß²²ÛÜÜ  °ÛÛÛÛ²²  ²ÛÛ Û   °ÜÜÜÜßßß²ÛÛÜÜÜÛ°²²Û²²  ß²²ÛÜÜ  ");
put_it(" Û²²Û² °°°°°   ²±±°±ßß²Ü ß²°°°Û   ÛÛÛ  ܲ²ßßßÛ ÛÜ°°  ß  Û°°°°°     °ßß²Ü    ");
put_it("ÛÛÛÛÛÛÜÜÜ ÛÛÛÛÛÛÛ° ßÛÛÛÛ ²±°°° Û±°°°Ý ÛÛÛ   Ü ß  ÛÛÛÛÛß ²±°ÛÛÛÛÛ ±²²  ²");
put_it(" ²ßßßßßÛÛ  ÛÜÛÛÛ²ßßßßß ÛÛÛÛÛßßßÛÛßßßÛÛÛÛÜ ßßß ÜÜܲßßßß Ü    Ü ÛÛ°ßßßßßÛÛ   ");
put_it("²²²°°Û°ßßßßß        ÛßßßßßßÛ °±ß ßÜ  ÛÛÛÛÛ");
put_it("     ²Û Û sty (twilght°.  Üß    Û          ß");
put_it("ß                          ");
			break;
		case 7:
put_it(" ");
put_it(empty_string);
put_it("  Ü²Ü ");
put_it("Ü°±±²²Ü Üܱ ° °Ü");
put_it("°±°°±²°   Ü°±²ÜÜ°±²          ±°°Ü");
put_it("°²±°°±          Üß±°°±²ß Ü°°±²²Ü°°   ܲ±°°±Ü ");
put_it("°²±°Û°°Ü       ܱ²Ü ±²±°± ßß°Û°±²²²Ü°       ܲ±°Û°±²ßß");
put_it("°±±°ÛÛÛ°Û²Ü    ÜÜ²Ü  °°±±    ÜÜÜÜÜ°²°°Ü  ß°°°±²²²Ü°      ܲ±°Û°±²ß°°");
put_it("°° °°±²°ÛÛÛ° Þ±°ßÜ ßܱ±²± °Û°²ß ÜßÛÛÜܱ߰°°Û²Ü  ß°°°°±²²Ü°  ܲ±°Û°±²ß°±°°±±± °");
put_it("°±ÛÛÛÛ°  ß°°ÛßÜ Ý°± °°Û° ÜßÛÛÜß Ü²±°° ²±°ßÜ  ß°°Û°±²²Ü²²±°Û°±ß°±±°");
put_it("°°ÛÛ°°°  Þ°°ÛÛÝÞ°° °ÛÛ°°ÛÛÛ°°° ±±°±²  ß°±²ÛÜ  ß°°Û°±²±°°Û°ß°°°°°");
put_it("°ÛÛ °±°±±°°ÝÞ°± °Û°±°°Û°±°  °°±²Û   Þ²²Û²°  Þ°°Û°°°°°ÛÝ°°±°°");
put_it("°°°°°Û°±²°  °Û²±±ÝÞ±² °°±²°°±±²Ü  °±²Û²   ±²Û²°° Ü°°ÛÛ°Û°±±°ßÜ °°°±±° °");
put_it("°°°±²Û  ±±²²Ûß Û²Û °±²Û± ß²Û²²° ßßß²   ±±²°°Ü°°ÛÛ°°Üß²²±±°Û°Ü °°");
put_it("°±²ÛÛ² °°±±ß ܱ²Û²±Ü Û²²ß  ß²²±±±ß ²°°°Ü±°°Û°°Üß  °ßÛ²±°°°±Ü °");
put_it(" ²Û²²± °° °°°   ܲ°   ß±°  ±±   °ÜÛ²²±±°Ü±   °ß²°±±²²ÛÜ °enx! ");
put_it(" Û²²±± °°    °    ° °°± Üܲ²ÛÛ²²±°°  °±±²²ÛÛ²²ÜÜ");
put_it("  °±±°  °°  ±²±²²Û±°   °°ÛÛ²²±²± ");
put_it("°±°  .the bitch of IrC.        °°±±²°°²±±° ");
put_it("°°±°±°  ");
put_it("°°");
			break;
		case 8:
put_it("");
put_it(empty_string);
put_it("  ");
put_it("ÝÜÜÝ     ");
put_it("Üܲ²²ÛÜ°ß±²²ÜÜ     ");
put_it("Ü °°°±±²²ÝÜÜÜÜÜÜ°±²²ÛÜ  ");
put_it("ÜÜßÛÛ  ° °°±ßÝÜÝ°   ßßÝ °±±²Ü   ");
put_it("Üßßß °ßßÜß  °  ÜÜÜÜÜÜßßßßÞÜÜÜÜÜ Ü Þ°   °ÞßÝ°°°±±ßß  ");
put_it("Û  ÜÜÝ ß  ÞÜ ßß °Üß ß Þ°  ÞßÜßß°  °°ßÝ  Ý° °ÞÝ °fictionÞ");
put_it("Û° Þ Ü ÜÜÜ ß Ý°   ÝÜßßß°  °ÞÜßßßßßÜ°   °ÜþÜßÝÞÝ° °±ÝßÜÜßßßßÜÜÝ   ");
put_it("Þ° ° ÝÜß Ü ßßÜÞ°° °Þ ß Ü°°   °°ÜÜßÜ° ° °°Ý Ü ßßÞ°  °Üß     °°±Þ    ");
put_it("Þ°° ÞÝß ß ßÜ°°Ýß°  °±Û  Ý°  °Ýß ÜÞ±°°  °Þ ßÜÜÜÜß±°          °°±Ý  ");
put_it("Û²±°    ßÜß  °Þ Þ°  °ÞÝÝ°  °Þ ÛþÝÜÞ±°  °°ÝßÛ °°ÛÝ°°   °ÞßÜ°  °Þ    ");
put_it("   ÜÛÜÞ±°°        °°±Ý °  °°ÛÞ°°  °°Û Üß±ÛÝ°°  °ÞÜÞ  °±ÞÝ°  °°ÝÞ°  °°Ý    ");
put_it("    Ý°ß°° °    ° °°Üß Û±° °ÞÞÞ²±°   °°°ÜÜßÞ°    ° ß °°Û Ü°° °ÜßÞÜ   °Þ    ");
put_it("    ßßßÜÜ° °  ÜÜÜßßß ß°°   °ßÝßßÜÜÜÜßßß   ßÜÜ  °°ÜÜÜßß°ß ÜßßßÜ ÜÝ °°°Üß    ");
put_it("ßßßßß ßß° ÞÝßßÜÜÜßßÝÛß       ßßßßßßß° °°°ß°° °  ßßßß    ");
put_it("ßß°°°°ß°°ßßß°°ÛÝ     ßß ° °°±°°° °Ü        ");
put_it("ßܱ߱°°° ßßß±²±°ßß ");
put_it("ßÝÞ ß  ");
put_it("    ");
			break;
		case 9:
put_it(" þ²° ²° ÜÜÜ   ÜÛÛ ÜÜÜÜÜÜÜ Û°ÛÛÛÛÛÛ°°°ÜÜ °°²²ÛÛÜÜ  ");
put_it(" Ü°°ܲ°ÛÛ°ßÛÛÛßÛÛÛ ßÞ ÜÜÛÛÛÛÛÛ°°°°ÛÛÛÛ°°°²²²²ÛÝݱ° °²ÛÛÛÝ");
put_it("  ßß ßÝßÛÛÛßÛÛÛÛ°°ÛÛÛÜ°Û ßßÛ°°²²²  °°°²²²²ÛÛßßܲ°°° °²²²ÛÝ  [_v9(Vade79)ùFiRE]");
put_it(" ß ßß ÝÜÛÛ°°²²ÛÛÞ°   °     ßßÞÛ²²²ÛÛßßß °°ÛÛ²°±°°°°±ß   ßßß ßßßßßßßßßßßß ß");
put_it(" Üܲ߱ ÜÛÜÜ ÜÛ²²ÛÛÛÝÞ   Þ °²±ß²° Þß°ßÛÛÜÜÜ ÞÛ²°°Û°°²²Üß   ßß ßßÛßÛÛßÛÛßÛßß °²Ü");
put_it(" Ü°ÜÝß ß±Ûß ßÜ°ÛÜÜÜ ÞÝ Þ   ßßßßß ÞßÛß Û²ÜÛÛ ßß²²°ßßßßßßßßßßßßßßßßßßßßßßÛÜ°²");
put_it(" Û²Û   Ü  ÜÜÜ ßßßß  Þ    ÜÜÜÜÜÜÜ       ßßß   Þß  ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ ÜÜÜ   Û°°");
put_it(" ÛßÛ °Û²ÛÛ°ßßßÛÛÛ°°Ü °²ßßß²²°ßßßßßßßß ßß    ² Ü ßßßßßßßßÛÛÛßßßÛ°°ßßßÛÛÛß²° Û²Û");
put_it(" ÛÜÛ °²°Û°Û°Ü ÛÛ°ÛÛ°ÞÛ°ÜÜÜ°°Û ° Û Û ² ÛÛÛ   ²°Û°Ü ÛÛÛÜÜÜ°ÛÛ Ü °ÛÛ Ü ÛÛ  °° ÛÛÛ");
put_it(" Û°Û °°Û±²°°² °°²°°²Û °ßßßÛÛ°ÜÜÜÛ°  ± °°ÛÜÜÜ°ÛÛ°² Û°°ßßß²°° ² ²°°°² ÛÛ°°Û  ÛÜÛ");
put_it(" Û°Û °Û°²Û² ² ²²Û²²Û°°² Ü °°²°Û°°²Ý ² ²²±°° ²°° ° °²² ² Û²² ° Û²²°° Û°²²°° ÛßÛ");
put_it(" ÛÜÛ °Û²ÛÛÛ ° ÜÜÜ Ü ²²Û ² ²²Û²²²²ÛÝ ° ÛÛ²²²Þß²²°² ²Û² ² ²Ûß ² ÜÜÜ°² Þ²ÛÛ°  ÛßÛ");
put_it(" Û Û  ÛÛÛ²² Û °ÛÜ ² ²Û² ° ßßßß ßß ²ßßßßß ° Ûßß   ßß    ßÛ² ° Þß²²°  ÛÜÛ");
put_it(" ÛßÛ °ÛÛÛ°° ° ²Û² ° Ûßß  ÜÜÜÜÜÜÜÛÛÛÛÝ ÜÜÜÛÛÛßßß  ÜÜÜÜ°ÛÛÜÜÛÛ°²ÝÜÜÜÜ ßßßß   Û Û");
put_it(" ÛßÛ °°²ÝÜÜ ° ßßß   Ý Ü²²²Û²²ÛÛßÜß  ÜÜÜÜ°ÜÜÛ°°ÛÛÛÛßÛÛÛÛ°°°°°°ÛÛÛÛÛÛßÛ°² °  ² Û");
put_it(" ÛßÛ  ²°ßßÛÜÜÜܲ° ²Ý ²°°°°Üß ÜÛ²²²ßÜÜÜÜÜÛÛÛ ²²°°Ûß°°°°²²²²²²² °°ÛÛÛÛßß° °  ± Û");
put_it(" Û°Û°  ßßßßßßßßßßßß Þ±²²°Þ  °°²²ßß°±°° ÜÜÛÜÞÛÛ²²ÝÞ²²²²ÛÛÛÛÝßßÞ²²ß  ÜÜ°  °  ° ²");
put_it(" ۲߲ÜÜÜÜÜÜÜÜÜÜÜÜÜ  Û²°²±Ý Þ°²°°° °°°²Ýß°²Ûܲ²ßß  Ü°ßß Þܲ²ÜÜÞÛß²° ß ÜÜ   °");
put_it(" ßß°ÜÜÜÛÜÛܲÛÜÜÛÜÜÜ ÞÛ  ²Û  Û²±²°°²ÜÜ þ   ßßß  Ü° Ü  ÜÜÝßÜÛßßÞ ÜÜÜÜÛÜܲ°  ");
put_it(" Ü ÜÜÜÜÜÜÜÜÜÜÜ ÜÜÜ Ü ßÞÛ°°ÛÜ Û°²²²ÛÛÛÛÜ ßßÛÜÜ°ßÝ  ßÛÛÛßÝ  ÜÜ Þ ÜÜ");
put_it(" ÞßßÛÛÛÛÛÜ°ÜÛÞ°²²²Ý   Üßß۲ܰ     ÞßÛßß Ûß");
put_it(" ..B  i  t  c  h  X.. °  ßßßßßß ²²°°  Ü     ßß Ü²°  ß°");
			break;
		case 10:

put_it("²±°ßß ß  Ü     ÜÜ ß     ß ");
put_it("± ß      Ü  ß    ß Ü      ÞÜÜ  ²ÜÜÛÝ  ÛÜ");
put_it("Ýß °±   ÞÝ ÛÛÜÜÜÜÜÛÜÜÛÛÝ  ÜÜ ßß Þ²ÜÜ   ܲÝ");
put_it("  ÞÝÞÝ   ß ÞÞÛ²ßÛ߲߲²ßÜ Û  ß²ßßÛ Ü");
put_it(" ÞܱÝ     °ÜÜÜÜÛ ²Ûßß   ßß  ÜÜ ²°ÛÛÛÜÜ     ²²Ýß   °   Þ²²ß");
put_it("  Þ²Ûßßß²ßßÛÛÜÜÜÜ  ßßÛ²  ÜÛ° ß ÜÛ²Ý ÜÜÜÜ Ü°°°ßßßÛÜÜ  ßßß² Ü   °  Þ");
put_it("  ÛÛÝß ÜÛÜ   °°± Þ°°    ßßßß  ÞÛÛÞÛÝ ²ÝÞÛ²Ý ±°   ßßßݲ   Ý");
put_it("  ÞßßÝ          ÞÞÛÜÝ  Þ²Ý Þ±°±ßß°ßßßß² Û°ÛÛ²Ý ÞÝ     °±°ÜÜÜÜÜ²Ü   ßß");
put_it("    ÜÜÜÜ         ÛÛÛÛ ÞÞÛÝ  ²²ÝÝ ÞÛ°ÞÛÝÞÛÞ²ÛÝ    ßßß°±Ü  ");
put_it(" ÜÛ° ßßÛÜÜÜ      ÞÞÛÝÝÞÛÝÝ   ÛÛÝ  ßßßß ÞÛÝÞܲÝÞÛÛÝÞ²±Ý  ");
put_it("ÞÛ°ÛÛ ÛÞ²ÛÝÝ     ÛÛÛÜ ÜÛÛ   ÞÞÛ      ÞÜÜ°ÜÜÜÜÛÛ²Ý         ÞÛÛÝ ÞÛÝÞÛ²Ý  ");
put_it(" ßÛÛÜ ÜÛßßß     ÞÞÛ²ÛßÛÛÛ   ÞÞÛÝÝ    ÞÞ²ßÛ²ßßßßÛÛÜÜ        ÜÜÜÜÜÜÜÜÜ      ÞÛÝ");
put_it("    ßßßß       ÜÜÛ²Ûß°ÞÝÝ    °ÛÝÝ  ÜÜÜÛÝÜÝÝ    ßßßßÛ²°Ü ÜÛß   ÞßÝ   ßÜÜ   ÞÛÝ ");
put_it("    Û²ßÛÜܲÜÛÛßßßßßÛÜ ²ÝÝ  ß  ßßÝßßßßß ßßßÛ        ßÞ²ÝÞ°ÛÛÜÜÜ ß ÜÜÜ°²ÛÝ  Þ²ÛÝ");
put_it("    ßßß    ßßßÛÝ      ÞÝ ÛÛßß   ÜÜÜ   ßßÛÝ  Þß²Ý ");
put_it("      ÞÝ dark horizon(dh!toolshed)    Ý ÞÝ    ÞÛ°ÛÝ    ÞÝ  ");
put_it("     ß       Þ²Ý ßÜÜ    ÜÜ   ÜÜß   ");
put_it("[%s by panasync] ß     ßßßßßßßßß    ", irc_version);
			break;
		case 11:
put_it("°°° Û²Û  ²²Û²²²Û²²Û²²Û²²²Û °°° ÛÛÛÛÛÛÛÛú");
put_it("° ° ²±²²±±²²±²±±²±±²±±±²° ° ÛÛÛÛÛÛÛÛÛÛÛÛb");
put_it("° ° ±±±±±±±°  ±±±±±±±±±±±±° ° ÛÛÛÛ²²Û²²²ÛÛÛÛy");
put_it("° ° °±°±°°±°°  °±±°±±°±±°°° ° ÛÛ²²²²²²²²²²ÛÛÛú");
put_it("° ° °°°°°° ° °°°°°°°°°°°° °   Û²²±²²²°²²²ÛÛp");
put_it("° °° °° °²²°°²²°²°²²Ûa");
put_it("° °°°°  °°°° ° °°°°°   °°°°  ° °°°°    ²°°°°°²²²n");
put_it("°ÛÛÛÛÛÛ° °ÛÛ° °Û°°ÛÛÛÛ° °ÛÛÛÛÛ°²°°²a");
put_it("°Û°°°ÛÛ° °Û° °ÛÛ°°°° °ÛÛ°°°  °Û°°°ÛÛ° Û°°°°°°°s");
put_it("°ÛÛÛÛÛÛÛ° °Û°  °ÛÛÛÛ° °ÛÛÛÛÛ° °Û°  °Û° ÛÛ°°°°°°°y");
put_it("°°°°°°° °°°°   °°°°   °°°°°  °°°  °°° ÛÛÛÛ  °°°°n");
put_it("°°°°°c");
put_it("ÛÛÛÛÛÛÛÛ²²²²²±²²±±±±±°°°°°°°°°°°°°°°°°°°°ú");
put_it("f e e l i n '  f i n e ú a s c i i ú b y ú s o r t o f ú °");
put_it(empty_string);
			break;
		case 12:
put_it("ÜÜÜÜÜÛÛÛÜÜÜ");
put_it("ÜÜÜÜÜÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÛÛÛÛÜßßÛÛÛÛÜÜ");
put_it("°²ÞÛÞÛÛÛÛÛÛÝßßßÛÛÛÜÜÞÞÛÛÛÛÝ         ÞÞÛÛÛÛÛÝ°²  zalt");
put_it("ÜÜ  ÜÜÜÜÜÜÝÛÛÛÛÛÛÛÛÝ  ÜÜÜ  ÞÞÛÛÛÛÝ ÜÜÜ  ßßÛÛÛÜÜÜÜÜÛÛÛÛßß ÜÜÜÜÜÜÜÜÜÜ");
put_it(" ÛÝÞ °ÛÛÛÛÝÛ ÛÛÛÛÛÛÝ  ÜÜÜÜÜÜÛÛÛßßßßßÝ  ßßßÛÛÜ°ÜÜÜÜÜÛßßßß  ÜÛÛßÛÛÛÛÛÛÛÝ");
put_it("ÛÛÛÞÛÛßÛÛÛÝÞ°±²²²²²Ý ßßßÜßßÛÛÛÛÜÜÜ  ÜÜÜÜÛßÛÛÜßßßÜÛßÛÛÛÛÜÜÜÜ ßßÛßÛÛßß ");
put_it("°²² ÛÛÛÛÛÛÝÞÛ°±±±±±±Ý  ÜÜÜ ßßßÛÛÛÛÛÜÜÛÛÛßßßß       ßßß ßÛÛÛÛÛÜÜ   Þ°²ß  Ü");
put_it("ÛÛÝ ÛÛÜÜÛÛÛÝÛ °°°°°°Ý  ÛÛÛÛÜÜ   ÞÛÞÛÛÛÛÛÛ²ÛÝ   ÜÜÜÝÛÜÛÜ   ßÛßÛÛÛÛÛÝ   ß   ÛÛ");
put_it("ÛÛ  ÞÛßßÛÛÜÛÛÛÛÛÛÛ Û  ÞÛßßßÛÝ   Û Û²²²²²°Û   ÛßÛÝ ÛÛÛÝÝ   ÞÛÞÛÛÛÛÝ     ÜÛÛÛ");
put_it("ÛÝ   ÛÛÛßÛßßßßßÝ°²°²ßß  Û°²ßß   ÞÛ±±±±±°ÛÛÜÝ  Þ°²Ý ÞÛÛ°² ÛÛ²²²²²Ý   ÞÛÛ°²");
put_it("ßÝ    ßß°²ß    °²  °²°²     ÜÜß²²°°°ÛÛßß ßßß   ßßß   ÜÜÛ °°°°°ÝÜ   ßßßß");
put_it("°² ÜÜÜß °±±²²ÜßßßßßÜÛ      ÛÛ°²");
put_it("ÝÛÞÛÛÛÛÝßßßßßbITCHXß°²°²ß");
put_it(" ßßßÜß ÛÜby panasync °²°²");
put_it("ßß ÜÜ      ");
put_it("ßß Ü");
			break;
		case 13:

put_it("");
put_it("                     ÜÜÜÜÜ  ÛÛ²²                            ÛÛ²²²Û²±²°±");
put_it("ÛÛ²²²             Üß      ßÛÛ²±°      ÜÜÛßßÛ²Ü   ÛÛ²±        ²±²ÛÛ²±±°");
put_it(" ÛÛ²²±           Û          ²±±     ÜÛ²²   Þ²±°  Û²±±         ÛÛ²²Ý   Þ±°");
put_it("  ²²±±±   Û²±°Ü ÛÝ          Þ±Ý    Þ²°²Ý    Þ²±° Û±°²          ²±±²   ÛÛ");
put_it("   ²±°°   ܲ±±°±²²          ±²     °°°²      ²±²Ý²°±°           °²±² ±±°");
put_it("    ±±ÛÛÜÛß  °±²Ýß²Ü      Ü°²Ý    Þ°°²Ý      ±°°²±²±± ÜÛßßÛ±Ü    ²²±Ý°²");
put_it("     ±°°°     °±° ±°°°ÜÜÛ±°°±±ßßß ²±°°           °ÛÛ°²ß    °²Û    °±°±Ý");
put_it("      °Û°Ý    Þ°±°°²±°  ±²±²ß ÜÛ²±°²±°           °±±°      °Û²Ý   ²°±°");
put_it("       Û°²Ý    ±±±±²±° ²²²±Ý  Û²±±²Û²±       ²²±°Û²±Ý       ±±±   ²°°±Ý");
put_it("        °±±    ±²±°²±°ÞÛ²Û²   ±²±±ÞÛ±°Ý      ²°°°Û²Û        ²±²   °°Ý°±");
put_it("         ²±Ý  ÞÛ²²²Û²±ÛÛÛÛ    Û²±² ²²²±      ±±± ±²Ý       ÞÛ²Ý  ±°±  °°");
put_it("          ²° ÜÛ²² Þ²²±Û²ÛÛ±ÜÜÜÛ²²ß ÞÛ²²Ý    Þ²±Ý ²±        ²±±  Û²±    ±°");
put_it("           °°±²ß   Û²Û              ßÛ²²    ÛÛ²  ²Ý       ÞÛ²Û Û²²Ý     ±±");
put_it("            ²±      ÛÛÛ   ...mid      ßßÛÜÜÛÛß   Û        ÛÛÛ Û±²²      Þ²Û");
put_it("                      Û                                      Û²±²Û±²Ý");
put_it("                                                     Ü ÜÜÜÜÛ°±²²ÛÛÛ  ÜÜÛ²²²ÛÛ");

			break;
		case 14:
put_it("________   ________ ________     ________");
put_it("\\//___________\\/________\\\\/_________\\_//");
put_it("___\\    ___   _________ _________ \\//");
put_it("<<_____ \\\\  /> \\     /____\\     >>\\ ___");
put_it("____/______\\_____<<_____//___________>>  /_______\\   /_____>>sm");
put_it("<<___________   bitchx by panasync /______\\\\   ____");
put_it("/------------------------------------------------\\\\");
put_it("");
put_it("");
		default:
			break;
	}
#endif
#endif
}

--- NEW FILE: commands.c ---
/*
 * commands.c: This is really a mishmash of function and such that deal with IRCII
 * commands (both normal and keybinding commands) 
 *
 * Written By Michael Sandrof
 * Portions are based on EPIC.
 * Modified by panasync (Colten Edwards) 1995-97
 * Copyright(c) 1990 
 * Random Ircname by Christian Deimel
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#include "irc.h"
static char cvsrevision[] = "$Id: commands.c,v 1.1.1.2 2003/06/11 07:00:41 root Exp $";
CVS_REVISION(commands_c)

#include <sys/stat.h>
#include "struct.h"
[...5508 lines suppressed...]
	}
#ifdef WANT_DLL
	for (i = 0; i < cntdll && cmddll; cmddll = cmddll->next, i++)
	{
		strmcat(buffer, cmddll->name, BIG_BUFFER_SIZE);
		strmcat(buffer, space, BIG_BUFFER_SIZE);
		if (++c == 5)
		{
			put_it("%s", convert_output_format("$G $[13]0 $[13]1 $[13]2 $[13]3 $[13]4", "%s", buffer));
			*buffer = 0;
			c = 0;
		}
	}
#endif
	if (c)
		put_it("%s", convert_output_format("$G $[13]0 $[13]1 $[13]2 $[13]3 $[13]4", "%s", buffer));
	userage("help", "%R[%ncommand%R]%n or /command -help %n to get help on specific commands");
}



--- NEW FILE: expr.c ---
/*
 * expr.c -- The expression mode parser and the textual mode parser
 * #included by alias.c -- DO NOT DELETE
 *
 * Copyright 1990 Michael Sandrof
 * Copyright 1997 EPIC Software Labs
 * See the COPYRIGHT file for more info
 *
 * $Id: expr.c,v 1.1.1.2 2003/07/28 07:00:36 root Exp $
 */

#include "irc.h"
#include <math.h>

#undef PANA_EXP
#undef PANA_EXP1

/* Function decls */
static	void	TruncateAndQuote(char **, const char *, int, const char *, char);
[...1862 lines suppressed...]
		strformat(buffer, add, length, pad_char ? pad_char:get_int_var(PAD_CHAR_VAR));
		add = buffer;
	}
	if (quote_em && add)
	{
		char *ptr = alloca(strlen(add) * 2 + 2);
		add = double_quote(add, quote_em, ptr);
	}
	
	if (buff)
		malloc_strcat(buff, add);

	return;
}

static void	do_alias_string (char *unused, char *input)
{
	malloc_strcpy(&alias_string, input);
}


--- NEW FILE: X.c ---
#include <X11/X.h>
#include <X11/Xlib.h>
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#include <zvt/zvtterm.h>

void size_allocate (GtkWidget *widget, GtkWindow *window)
{
  ZvtTerm *term;
  XSizeHints sizehints;

  g_assert (widget != NULL);
  term = ZVT_TERM (widget);
  
  /* Not sure why it is x3 and x2.... klass shows as 2x2 and the variation is 6x4 */
  sizehints.base_width = 
    (GTK_WIDGET (window)->allocation.width) +
    (GTK_WIDGET (term)->style->klass->xthickness * 3) -
    (GTK_WIDGET (term)->allocation.width);
  
  sizehints.base_height =
    (GTK_WIDGET (window)->allocation.height) +
    (GTK_WIDGET (term)->style->klass->ythickness * 2) -
    (GTK_WIDGET (term)->allocation.height);
  
  sizehints.width_inc = term->charwidth;
  sizehints.height_inc = term->charheight;
  sizehints.min_width = sizehints.base_width + (sizehints.width_inc * 20);
  sizehints.min_height = sizehints.base_height + (sizehints.height_inc * 5);
  
  sizehints.flags = (PBaseSize|PMinSize|PResizeInc);
  
  XSetWMNormalHints (GDK_DISPLAY(),
		     GDK_WINDOW_XWINDOW (GTK_WIDGET (window)->window),
		     &sizehints);
  gdk_flush ();
}

--- NEW FILE: debug.c ---
/*
 * debug.c -- controll the values of x_debug.
 *
 * Written by Jeremy Nelson
 * Copyright 1997 EPIC Software Labs
 * See the COPYRIGHT file for more information
 */

#include "irc.h"
static char cvsrevision[] = "$Id: debug.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(debug_c)
#include "struct.h"

#include "ircaux.h"
#include "output.h"
#include "misc.h"
#include "window.h"
#include "hook.h"
#include "lastlog.h"
#include "cset.h"
#include "screen.h"
#include "input.h"
#include "status.h"
#include "vars.h"
#define MAIN_SOURCE
#include "modval.h"

unsigned long x_debug = 0;
unsigned long internal_debug = 0;
unsigned long alias_debug = 0;
unsigned int debug_count = 1;
int in_debug_yell = 0;

struct debug_opts
{
	char 	*command;
	int	flag;
};

static struct debug_opts opts[] = 
{
	{ "LOCAL_VARS",		DEBUG_LOCAL_VARS },
	{ "CTCPS",		DEBUG_CTCPS },
	{ "DCC_SEARCH",		DEBUG_DCC_SEARCH },
	{ "OUTBOUND",		DEBUG_OUTBOUND },
	{ "INBOUND",		DEBUG_INBOUND },
	{ "DCC_XMIT",		DEBUG_DCC_XMIT },
	{ "WAITS",		DEBUG_WAITS },
	{ "MEMORY",		DEBUG_MEMORY },
	{ "SERVER_CONNECT",	DEBUG_SERVER_CONNECT },
	{ "CRASH",		DEBUG_CRASH },
	{ "COLOR",		DEBUG_COLOR },
	{ "NOTIFY",		DEBUG_NOTIFY },
	{ "REGEX",		DEBUG_REGEX },
	{ "REGEX_DEBUG",	DEBUG_REGEX_DEBUG },
	{ "BROKEN_CLOCK",	DEBUG_BROKEN_CLOCK },
	{ "UNKNOWN",		DEBUG_UNKNOWN },
	{ "DEBUG",		DEBUG_DEBUGGER },
	{ "NEW_MATH",		DEBUG_NEW_MATH },
	{ "NEW_MATH_DEBUG",	DEBUG_NEW_MATH_DEBUG },
	{ "AUTOKEY",		DEBUG_AUTOKEY },
	{ "STRUCTURES",		DEBUG_STRUCTURES },
	{ "ALL",		DEBUG_ALL },
	{ NULL,			0 },
};



BUILT_IN_COMMAND(xdebugcmd)
{
	int cnt;
	int remove = 0;
	char *this_arg;

	if (!args || !*args)
	{
		char buffer[540];
		char *q;
		int i = 0;

		buffer[0] = 0;
		strmcat(buffer, "[-][+][option(s)] ", 511);
		q = &buffer[strlen(buffer)];
		for (i = 0; opts[i].command; i++)
		{
			if (q)
				strmcat(q, ", ", 511);
			strmcat(q, opts[i].command, 511);
		}
		return;
	}

	while (args && *args)
	{
		this_arg = upper(next_arg(args, &args));
		if (*this_arg == '-')
			remove = 1, this_arg++;
		else if (*this_arg == '+')
			this_arg++;

		for (cnt = 0; opts[cnt].command; cnt++)
		{
			if (!strncmp(this_arg, opts[cnt].command, strlen(this_arg)))
			{
				if (remove)
					x_debug &= ~opts[cnt].flag;
				else
					x_debug |= opts[cnt].flag;
				break;
			}
		}
		if (!opts[cnt].command)
			say("Unrecognized XDEBUG option '%s'", this_arg);
	}
}

void	debugyell(const char *format, ...)
{
const char *save_from;
unsigned long save_level;
unsigned long old_alias_debug = alias_debug;
	alias_debug = 0;
	save_display_target(&save_from, &save_level);
	set_display_target(NULL, LOG_DEBUG);
	if (format)
	{
		char debugbuf[BIG_BUFFER_SIZE+1];
		va_list args;
		va_start (args, format);
		*debugbuf = 0;
		vsnprintf(debugbuf, BIG_BUFFER_SIZE, format, args);
		va_end(args);
		in_debug_yell = 1;
		if (*debugbuf && do_hook(DEBUG_LIST, "%s", debugbuf))
			put_echo(debugbuf);
		in_debug_yell = 0;
	}
	alias_debug = old_alias_debug;
	reset_display_target();
	restore_display_target(save_from, save_level);
}

int parse_debug(char *value, int nvalue, char **rv)
{
	char	*str1, *str2;
	char	 *copy;
	char	*nv = NULL;

	if (rv)
		*rv = NULL;

	if  (!value)
		return 0;

	copy = alloca(strlen(value) + 1);
	strcpy(copy, value);
	
	while ((str1 = new_next_arg(copy, &copy)))
	{
		while (*str1 && (str2 = next_in_comma_list(str1, &str1)))
		{
			if (!my_strnicmp(str2, "ALL", 3))
				nvalue = (0x7F - (DEBUG_TCL));
			else if (!my_strnicmp(str2, "-ALL", 4))
				nvalue = 0;
			else if (!my_strnicmp(str2, "COMMANDS", 4))
				nvalue |= DEBUG_COMMANDS;
			else if (!my_strnicmp(str2, "-COMMANDS", 4))
				nvalue &= ~(DEBUG_COMMANDS);
			else if (!my_strnicmp(str2, "EXPANSIONS", 4))
				nvalue |= DEBUG_EXPANSIONS;
			else if (!my_strnicmp(str2, "-EXPANSIONS", 4))
				nvalue &= ~(DEBUG_EXPANSIONS);
			else if (!my_strnicmp(str2, "TCL", 3))
				nvalue |= DEBUG_TCL;
			else if (!my_strnicmp(str2, "-TCL", 3))
				nvalue &= ~(DEBUG_TCL);
			else if (!my_strnicmp(str2, "ALIAS", 3))
				nvalue |= DEBUG_CMDALIAS;
			else if (!my_strnicmp(str2, "-ALIAS", 3))
				nvalue &= ~(DEBUG_CMDALIAS);
			else if (!my_strnicmp(str2, "HOOK", 3))
				nvalue |= DEBUG_HOOK;
			else if (!my_strnicmp(str2, "-HOOK", 3))
				nvalue &= ~(DEBUG_HOOK);
			else if (!my_strnicmp(str2, "VARIABLES", 3))
				nvalue |= DEBUG_VARIABLE;
			else if (!my_strnicmp(str2, "-VARIABLES", 3))
				nvalue &= ~(DEBUG_VARIABLE);
			else if (!my_strnicmp(str2, "FUNCTIONS", 3))
				nvalue |= DEBUG_FUNC;
			else if (!my_strnicmp(str2, "-FUNCTIONS", 3))
				nvalue &= ~(DEBUG_FUNC);
			else if (!my_strnicmp(str2, "STRUCTURES", 3))
				nvalue |= DEBUG_STRUCTURES;
			else if (!my_strnicmp(str2, "-STRUCTURES", 3))
				nvalue &= ~(DEBUG_STRUCTURES);
		}
	}
	if (rv)
	{
		if (nvalue & DEBUG_COMMANDS)
			m_s3cat(&nv, comma, "COMMANDS");
		if (nvalue & DEBUG_EXPANSIONS)
			m_s3cat(&nv, comma, "EXPANSIONS");
		if (nvalue & DEBUG_TCL)
			m_s3cat(&nv, comma, "TCL");
		if (nvalue & DEBUG_CMDALIAS)
			m_s3cat(&nv, comma, "ALIAS");
		if (nvalue & DEBUG_HOOK)
			m_s3cat(&nv, comma, "HOOK");
		if (nvalue & DEBUG_VARIABLE)
			m_s3cat(&nv, comma, "VARIABLES");
		if (nvalue & DEBUG_FUNC)
			m_s3cat(&nv, comma, "FUNCTIONS");
		if (nvalue & DEBUG_STRUCTURES)
			m_s3cat(&nv, comma, "STRUCTURES");
		*rv = nv;
	}
	return nvalue;
}

void debug_window(Window *win, char *value, int unused)
{
	Window	*old_win = win;
	char	*nv = NULL;

	internal_debug = parse_debug(value, internal_debug, &nv);
	set_string_var(DEBUG_VAR, nv);

	if (internal_debug)
	{
		Window *tmp = NULL;
		if (!get_window_by_name("debug") && (tmp = new_window(win->screen)))
		{
			malloc_strcpy(&tmp->name, "debug");
			tmp->double_status = 0;
			hide_window(tmp);
			tmp->window_level = LOG_DEBUG;
			tmp->absolute_size = 1;
			tmp->skip = 1;
			debugging_window = tmp;
			set_wset_string_var(tmp->wset, STATUS_FORMAT1_WSET, DEFAULT_FORMAT_DEBUG_FSET);
			build_status(tmp, NULL, 0);
			update_all_windows();
			set_input_prompt(win, get_string_var(INPUT_PROMPT_VAR), 0);
			cursor_to_input();
			set_screens_current_window(old_win->screen, old_win);
		}
	}
	else
	{
		if ((old_win = get_window_by_name("debug")))
		{
			delete_window(old_win);
			debugging_window = NULL;
			update_all_windows();
			set_input_prompt(current_window, get_string_var(INPUT_PROMPT_VAR), 0);
			cursor_to_input();
		}
	}
	new_free(&nv);
}

--- NEW FILE: flood.c ---
/*
 * flood.c: handle channel flooding. 
 *
 * This attempts to give you some protection from flooding.  Basically, it keeps
 * track of how far apart (timewise) messages come in from different people.
 * If a single nickname sends more than 3 messages in a row in under a
 * second, this is considered flooding.  It then activates the ON FLOOD with
 * the nickname and type (appropriate for use with IGNORE). 
 *
 * Thanks to Tomi Ollila <f36664r at puukko.hut.fi> for this one. 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: flood.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(flood_c)
#include "struct.h"

#include "alias.h"
#include "hook.h"
#include "ircaux.h"
#include "ignore.h"
#include "flood.h"
#include "vars.h"
#include "output.h"
#include "list.h"
#include "misc.h"
#include "server.h"
#include "userlist.h"
#include "timer.h"
#include "ignore.h"
#include "status.h"
#include "hash2.h"
#include "cset.h"
#define MAIN_SOURCE
#include "modval.h"

static	char	*ignore_types[] =
{
	"",
	"MSG",
	"PUBLIC",
	"NOTICE",
	"WALL",
	"WALLOP",
	"CTCP",
	"INVITE",
	"CDCC",
	"ACTION",
	"NICK",
	"DEOP",
	"KICK",
	"JOIN"
};

#define FLOOD_HASHSIZE 31
HashEntry no_flood_list[FLOOD_HASHSIZE];
HashEntry flood_list[FLOOD_HASHSIZE];

static int remove_oldest_flood_hashlist(HashEntry *, time_t, int);




extern	char	*FromUserHost;
extern	unsigned int window_display;
extern	int	from_server;

static double allow_flood = 0.0;
static double this_flood = 0.0;

#define NO_RESET 0
#define RESET 1

char *get_flood_types(unsigned int type)
{
int x = 0;
	while (type)
	{
		type = type >> 1;
		x++;
	}
	return ignore_types[x];
}

#if 0
int get_flood_rate(int type, ChannelList * channel)
{
	int flood_rate = get_int_var(FLOOD_RATE_VAR);
	if (channel)
	{
		switch(type)
		{
			case JOIN_FLOOD:
				flood_rate = get_cset_int_var(channel->csets, JOINFLOOD_TIME_CSET);
				break;
			case PUBLIC_FLOOD:
				flood_rate = get_cset_int_var(channel->csets, PUBFLOOD_TIME_CSET);
				break;
			case NICK_FLOOD:
				flood_rate = get_cset_int_var(channel->csets, NICKFLOOD_TIME_CSET);
				break;
			case KICK_FLOOD:
				flood_rate = get_cset_int_var(channel->csets, KICKFLOOD_TIME_CSET);
				break;
			case DEOP_FLOOD:
				flood_rate = get_cset_int_var(channel->csets, DEOPFLOOD_TIME_CSET);
				break;
			default:
				break;
		}
	}
	else
	{
		switch(type)
		{
			case CDCC_FLOOD:
				flood_rate = get_int_var(CDCC_FLOOD_RATE_VAR);
				break;
			case CTCP_FLOOD:
				flood_rate = get_int_var(CTCP_FLOOD_RATE_VAR);
			case CTCP_ACTION_FLOOD:
			default:
				break;
		}
	}
	return flood_rate;
}

int get_flood_count(int type, ChannelList * channel)
{
	int flood_count = get_int_var(FLOOD_AFTER_VAR);
	if (channel) {
		switch(type)
		{
			case JOIN_FLOOD:
				flood_count = get_cset_int_var(channel->csets, KICK_ON_JOINFLOOD_CSET);
				break;
			case PUBLIC_FLOOD:
				flood_count = get_cset_int_var(channel->csets, KICK_ON_PUBFLOOD_CSET);
				break;
			case NICK_FLOOD:
				flood_count = get_cset_int_var(channel->csets, KICK_ON_NICKFLOOD_CSET);
				break;
			case KICK_FLOOD:
				flood_count = get_cset_int_var(channel->csets, KICK_ON_KICKFLOOD_CSET);
				break;
			case DEOP_FLOOD:
				flood_count = get_cset_int_var(channel->csets, KICK_ON_DEOPFLOOD_CSET);
				break;
			default:
			break;
		}
	} 
	else
	{
		switch(type)
		{
			case CDCC_FLOOD:
				flood_count = get_int_var(CDCC_FLOOD_AFTER_VAR);
				break;
			case CTCP_FLOOD:
				flood_count = get_int_var(CTCP_FLOOD_AFTER_VAR);
			case CTCP_ACTION_FLOOD:
			default:
				break;
		}
	}
	return flood_count;
}
#endif

void get_flood_val(ChannelList *chan, int type, int *flood_count, int *flood_rate)
{
	*flood_count = get_int_var(FLOOD_AFTER_VAR);
	*flood_rate = get_int_var(FLOOD_RATE_VAR);
	if (chan)
	{
		switch(type)
		{
			case JOIN_FLOOD:
				*flood_count = get_cset_int_var(chan->csets, KICK_ON_JOINFLOOD_CSET);
				*flood_rate = get_cset_int_var(chan->csets, JOINFLOOD_TIME_CSET);
				break;
			case PUBLIC_FLOOD:
				*flood_count = get_cset_int_var(chan->csets, KICK_ON_PUBFLOOD_CSET);
				*flood_rate = get_cset_int_var(chan->csets, PUBFLOOD_TIME_CSET);
				break;
			case NICK_FLOOD:
				*flood_count = get_cset_int_var(chan->csets, KICK_ON_NICKFLOOD_CSET);
				*flood_rate = get_cset_int_var(chan->csets, NICKFLOOD_TIME_CSET);
				break;
			case KICK_FLOOD:
				*flood_count = get_cset_int_var(chan->csets, KICK_ON_KICKFLOOD_CSET);
				*flood_rate = get_cset_int_var(chan->csets, KICKFLOOD_TIME_CSET);
				break;
			case DEOP_FLOOD:
				*flood_count = get_cset_int_var(chan->csets, KICK_ON_DEOPFLOOD_CSET);
				*flood_rate = get_cset_int_var(chan->csets, DEOPFLOOD_TIME_CSET);
				break;
			default:
			break;
		}
	}
	else
	{
		switch(type)
		{
			case CDCC_FLOOD:
				*flood_count = get_int_var(CDCC_FLOOD_AFTER_VAR);
				*flood_rate = get_int_var(CDCC_FLOOD_RATE_VAR);
				break;
			case CTCP_FLOOD:
				*flood_count = get_int_var(CTCP_FLOOD_AFTER_VAR);
				*flood_rate = get_int_var(CTCP_FLOOD_RATE_VAR);
			case CTCP_ACTION_FLOOD:
			default:
				break;
		}
	}
}

int set_flood(int type, time_t flood_time, int reset, NickList *tmpnick)
{
	if (!tmpnick)
		return 0;
	switch(type)
	{
		case JOIN_FLOOD:
			if (reset == RESET)
			{
				tmpnick->joincount = 1; 
				tmpnick->jointime = flood_time;
			} else tmpnick->joincount++;
			break;
		case PUBLIC_FLOOD:
			if (reset == RESET)
			{
				tmpnick->floodcount = 1;
				tmpnick->floodtime = tmpnick->idle_time = flood_time;
			} else tmpnick->floodcount++;
			break;
		case NICK_FLOOD:
			if (reset == RESET)
			{
				tmpnick->nickcount = 1;
				tmpnick->nicktime = flood_time;
			} else tmpnick->nickcount++;
			break;
		case KICK_FLOOD:
			if (reset == RESET)
			{
				tmpnick->kickcount = 1;
				tmpnick->kicktime = flood_time;
			} else tmpnick->kickcount++;
			break;
		case DEOP_FLOOD:
			if (reset == RESET)
			{
				tmpnick->dopcount = 1;
				tmpnick->doptime = flood_time;
			} else tmpnick->dopcount++;
			break;
		default:
		break;
	}
	return 1;
}

int BX_is_other_flood(ChannelList *channel, NickList *tmpnick, int type, int *t_flood)
{
time_t diff = 0, flood_time = 0;
int doit = 0;
int count = 0;
int flood_rate = 0, flood_count = 0;

	flood_time = now;
	
	
	if (!channel || !tmpnick)
		return 0;
	if (isme(tmpnick->nick))
		return 0;
	if (find_name_in_genericlist(tmpnick->nick, no_flood_list, FLOOD_HASHSIZE, 0))
		return 0;
	set_flood(type, flood_time, NO_RESET, tmpnick);
	switch(type)
	{
		case JOIN_FLOOD:
			if (!get_cset_int_var(channel->csets, JOINFLOOD_CSET))
				break;
			diff = flood_time - tmpnick->jointime;
			count = tmpnick->joincount;
			doit = 1;
			break;
		case PUBLIC_FLOOD:
			if (!get_cset_int_var(channel->csets, PUBFLOOD_CSET))
				break;
			diff = flood_time - tmpnick->floodtime;
			count = tmpnick->floodcount;
			doit = 1;
			break;
		case NICK_FLOOD:
			if (!get_cset_int_var(channel->csets, NICKFLOOD_CSET))
				break;
			diff = flood_time - tmpnick->nicktime;
			count = tmpnick->nickcount;
			doit = 1;
			break;
		case DEOP_FLOOD:
			if (!get_cset_int_var(channel->csets, DEOPFLOOD_CSET))
				break;
			diff = flood_time - tmpnick->doptime;
			count = tmpnick->dopcount;
			doit = 1;
			break;
		case KICK_FLOOD:
			if (!get_cset_int_var(channel->csets, KICKFLOOD_CSET))
				break;
			diff = flood_time - tmpnick->kicktime;
			count = tmpnick->kickcount;
			doit = 1;
			break;
		default:
			return 0;
			break;
	}
	if (doit)
	{
		int is_user = 0;
		if (!get_int_var(FLOOD_PROTECTION_VAR))
			return 0;
		get_flood_val(channel, type, &flood_count, &flood_rate);
		if ((tmpnick->userlist && (tmpnick->userlist->flags & ADD_FLOOD)))
			is_user = 1;
		if (!is_user && (count >= flood_count))
		{
			int flooded = 0;
			if (count >= flood_count)
			{
				if (!diff || (flood_rate && (diff < flood_rate)))
				{
					*t_flood = diff;
					flooded = 1;
					do_hook(FLOOD_LIST, "%s %s %s %s", tmpnick->nick, get_flood_types(type),channel?channel->channel:zero, tmpnick->host);
				}
				set_flood(type, flood_time, RESET, tmpnick);
				return flooded;
			}
			else if (diff > flood_rate)
				set_flood(type, flood_time, RESET, tmpnick);
		}
	} 
	return 0;
} 

/*
 * check_flooding: This checks for message flooding of the type specified for
 * the given nickname.  This is described above.  This will return 0 if no
 * flooding took place, or flooding is not being monitored from a certain
 * person.  It will return 1 if flooding is being check for someone and an ON
 * FLOOD is activated. 
 */

int BX_check_flooding(char *nick, int type, char *line, char *channel)
{
static	int	users = 0,
		pos = 0;
time_t flood_time = now,
		diff = 0;

Flooding 	*tmp;
int		flood_rate, 
		flood_count;


	if (!(users = get_int_var(FLOOD_USERS_VAR)) || !*FromUserHost)
		return 1;
	if (find_name_in_genericlist(nick, no_flood_list, FLOOD_HASHSIZE, 0))
		return 1;
	if (!(tmp = find_name_in_floodlist(nick, FromUserHost, flood_list, FLOOD_HASHSIZE, 0)))
	{
		if (pos >= users)
		{
			pos -= remove_oldest_flood_hashlist(&flood_list[0], 0, (users + 1 - pos));
		}
		tmp = add_name_to_floodlist(nick, FromUserHost, channel, flood_list, FLOOD_HASHSIZE);
		tmp->type = type;
		tmp->cnt = 1;
		tmp->start = flood_time;
		tmp->flood = 0;
		pos++;
		return 1;
	} 
	if (!(tmp->type & type))
	{
		tmp->type |= type; 
		return 1;
	}

#if 0
	flood_count = get_flood_count(type, NULL); /* FLOOD_AFTER_VAR */
	flood_rate = get_flood_rate(type, NULL); /* FLOOD_RATE_VAR */
#endif
	get_flood_val(NULL, type, &flood_count, &flood_rate);
	if (!flood_count || !flood_rate)
		return 1;
	tmp->cnt++;
	if (tmp->cnt > flood_count)
	{
		int ret;
		diff = flood_time - tmp->start;
		if (diff != 0)
			this_flood = (double)tmp->cnt / (double)diff;
		else
			this_flood = 0;
		allow_flood = (double)flood_count / (double)flood_rate;
		if (!diff || !this_flood || (this_flood > allow_flood))
		{
			if (tmp->flood == 0)
			{
				tmp->flood = 1;
				if ((ret = do_hook(FLOOD_LIST, "%s %s %s %s", nick, get_flood_types(type),channel?channel:zero, line)) != 1)
					return ret;
				switch(type)
				{
					case WALL_FLOOD:
					case MSG_FLOOD:
					case NOTICE_FLOOD:
					case CDCC_FLOOD:
					case CTCP_FLOOD:
						if (flood_prot(nick, FromUserHost, get_flood_types(type), type, get_int_var(IGNORE_TIME_VAR), channel))
							return 0;
						break;
					case CTCP_ACTION_FLOOD:
						if (flood_prot(nick, FromUserHost, get_flood_types(CTCP_FLOOD), type, get_int_var(IGNORE_TIME_VAR), channel))
							return 0;
					default:
						break;
				}
				if (get_int_var(FLOOD_WARNING_VAR))
					put_it("%s", convert_output_format(fget_string_var(FORMAT_FLOOD_FSET), "%s %s %s %s %s", update_clock(GET_TIME), get_flood_types(type), nick, FromUserHost, channel?channel:"unknown"));
			}
			return 1;
		}
		else
		{
			tmp->flood = 0;
			tmp->cnt = 1;
			tmp->start = flood_time;
		}
	}
	return 1;
}

void check_ctcp_ban_flood(char *channel, char *nick)
{
NickList *Nick = NULL;
ChannelList *chan = NULL;
	for (chan = get_server_channels(from_server); chan; chan = chan->next)
		if ((Nick = find_nicklist_in_channellist(nick, chan, 0)))
			break;
	if (chan && chan->chop && get_cset_int_var(chan->csets, CTCP_FLOOD_BAN_CSET) && Nick)
	{
		if (!Nick->userlist || (Nick->userlist && !(Nick->userlist->flags & ADD_FLOOD)))
		{
			if (!nick_isop(Nick) || get_cset_int_var(chan->csets, KICK_OPS_CSET))
			{
				char *ban, *u, *h;
				u = alloca(strlen(Nick->host)+1);
				strcpy(u, Nick->host);
				h = strchr(u, '@');
				*h++ = 0;
				ban = ban_it(Nick->nick, u, h, Nick->ip);
				if (!ban_is_on_channel(ban, chan) && !eban_is_on_channel(ban, chan))
					send_to_server("MODE %s +b %s", chan->channel, ban);
			}
		}
	}
}

int BX_flood_prot (char *nick, char *userhost, char *type, int ctcp_type, int ignoretime, char *channel)
{
ChannelList *chan;
NickList *Nick;
char tmp[BIG_BUFFER_SIZE+1];
char *uh;
int	old_window_display;
int	kick_on_flood = 1;

	if ((ctcp_type == CDCC_FLOOD || ctcp_type == CTCP_FLOOD || ctcp_type == CTCP_ACTION_FLOOD) && !get_int_var(CTCP_FLOOD_PROTECTION_VAR))
		return 0;
	else if (!get_int_var(FLOOD_PROTECTION_VAR))
		return 0;
	else if (!my_stricmp(nick, get_server_nickname(from_server)))
		return 0;
	switch (ctcp_type)
	{
		case WALL_FLOOD:
		case MSG_FLOOD:
			break;
		case NOTICE_FLOOD:
			break;
		case PUBLIC_FLOOD:
			if (channel)
			{
				if ((chan = lookup_channel(channel, from_server, 0)))
				{
					kick_on_flood = get_cset_int_var(chan->csets, PUBFLOOD_CSET);
					if (kick_on_flood && (Nick = find_nicklist_in_channellist(nick, chan, 0)))
					{
						if (chan->chop && (!Nick->userlist || (Nick->userlist && !(Nick->userlist->flags & ADD_FLOOD))))
							if (!nick_isop(Nick) || get_cset_int_var(chan->csets, KICK_OPS_CSET))
								send_to_server("KICK %s %s :\002%s\002 flooder", chan->channel, nick, type);
					} 
				}
			}
			break;
		case CTCP_FLOOD:
		case CTCP_ACTION_FLOOD:
			check_ctcp_ban_flood(channel, nick);
		default:
			if (get_int_var(FLOOD_KICK_VAR) && kick_on_flood && channel)
			{
				for (chan = get_server_channels(from_server); chan; chan = chan->next)
				{
					if (chan->chop && (Nick = find_nicklist_in_channellist(nick, chan, 0)))
					{
						if ((!Nick->userlist || (Nick->userlist && !(Nick->userlist->flags & ADD_FLOOD))))
							if (!nick_isop(Nick) || get_cset_int_var(chan->csets, KICK_OPS_CSET))
								send_to_server("KICK %s %s :\002%s\002 flooder", chan->channel, nick, type);
					}
				}
			}
	}
	if (!ignoretime)
		return 0;
	uh = clear_server_flags(userhost);
	sprintf(tmp, "*!*%s", uh);
	old_window_display = window_display;
	window_display = 0;
	ignore_nickname(tmp, ignore_type(type, strlen(type)), 0);
	window_display = old_window_display;
	sprintf(tmp, "%d ^IGNORE *!*%s NONE", ignoretime, uh);
	timercmd("TIMER", tmp, NULL, NULL);
	bitchsay("Auto-ignoring %s for %d minutes [\002%s\002 flood]", nick, ignoretime/60, type);
	return 1;
}

static int remove_oldest_flood_hashlist(HashEntry *list, time_t timet, int count)
{
Flooding *ptr;
register time_t t;
int total = 0;
register unsigned long x;
	t = now;
	if (!count)
	{
		for (x = 0; x < FLOOD_HASHSIZE; x++)
		{
			ptr = (Flooding *) (list + x)->list;
			if (!ptr || !*ptr->name)
				continue;
			while (ptr)
			{
				if ((ptr->start + timet) <= t)
				{
					if (!(ptr = find_name_in_floodlist(ptr->name, ptr->host, flood_list, FLOOD_HASHSIZE, 1)))
						continue;
					new_free(&(ptr->channel));
					new_free(&(ptr->name));
					new_free(&ptr->host);
					new_free((char **)&ptr);
					total++;
					ptr = (Flooding *) (list + x)->list;
				} else ptr = ptr->next;
			}
		}
	}
	else
	{
		for (x = 0; x < FLOOD_HASHSIZE; x++)
		{
			Flooding *next = NULL;
			ptr = (Flooding *) (list + x)->list;
			if (!ptr || !*ptr->name)
				continue;
			while(ptr && count)
			{
				if ((ptr = find_name_in_floodlist(ptr->name, ptr->host, flood_list, FLOOD_HASHSIZE, 1)))
				{
					next = ptr->next;
					new_free(&(ptr->channel));
					new_free(&(ptr->name));
					new_free(&ptr->host);
					new_free((char **)&ptr);
					total++; count--;			
					ptr = (Flooding *) (list + x)->list;
					ptr = next;
				}
			}
		}
	}
	return total;
}

void clean_flood_list()
{
	remove_oldest_flood_hashlist(&flood_list[0], get_int_var(FLOOD_RATE_VAR)+1, 0);
}

--- NEW FILE: names.c ---
/*
 * names.c: This here is used to maintain a list of all the people currently
 * on your channel.  Seems to work 
 *
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: names.c,v 1.1.1.2 2003/05/09 07:00:22 root Exp $";
CVS_REVISION(names_c)
#include "struct.h"

#include "ircaux.h"
[...2032 lines suppressed...]
			}
		}
	}
}

void check_channel_limits()
{
int i;
ChannelList *chan;
	for (i = 0; i < server_list_size(); i++)
	{
		for (chan = get_server_channels(i); chan; chan = chan->next)
		{
			if (!chan->chop || !chan->csets || !chan->csets->set_auto_limit)
				continue;
			if (chan->totalnicks + chan->csets->set_auto_limit != chan->limit)
				my_send_to_server(i, "MODE %s +l %d", chan->channel, chan->totalnicks + chan->csets->set_auto_limit);
		}
	}
}

--- NEW FILE: compat.c ---
/*
 * Everything that im not directly responsible for i put in here.  Almost
 * all of this stuff is either borrowed from somewhere else (for those poor
 * saps that dont have something you need), or i wrote (and put into the 
 * public domain) in order to make epic compile on some of the more painful
 * systems.  None of this is part of EPIC-proper, so dont feel that youre 
 * going to hurt my feelings if you re-use this.
 */

#include "defs.h"
#include "ircaux.h"
#include "irc_std.h"
#define MAIN_SOURCE
#include "modval.h"


/* --- start of tparm.c --- */
#ifndef HAVE_TPARM
/*
[...2364 lines suppressed...]
	}
	return 0;
}
#endif


#ifdef WINNT
int tputs(const unsigned char *str, int nlines, int (*outfun)(int))
{
register unsigned int count = 0;
  /* Safety check. */
	if (str)
	{
		/* Now output the capability string. */
		while (*str)
			(*outfun) (*str++), count++;
	}
	return count;
}
#endif

--- NEW FILE: who.c ---
/*
 * who.c -- The WHO queue.  The ISON queue.  The USERHOST queue.
 *
 * Written by Jeremy Nelson
 * Copyright 1996, 1997 EPIC Software Labs
 */

#include "irc.h"
static char cvsrevision[] = "$Id: who.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(who_c)
#include "struct.h"

#include "commands.h"
#include "ircaux.h"
#include "who.h"
#include "server.h"
#include "window.h"
#include "vars.h"
#include "hook.h"
[...1035 lines suppressed...]
	int old_from_server = from_server;

	if (i == -1 || !get_server_list() || !is_server_connected(i))
		return;		/* Whatever */

	from_server = i;

	while (who_queue_top(i))
		who_queue_pop();

	while (ison_queue_top(i))
		ison_queue_pop();

	while (userhost_queue_top(i))
		userhost_queue_pop();

	from_server = old_from_server;
}



--- NEW FILE: pmbitchx.def ---
NAME WINDOWAPI           

DESCRIPTION 'PMBitchX by panasync: PM by NuKe and rosmo'

--- NEW FILE: Makefile.in ---
# source/Makefile.in
#
# BitchX/source/Makefile
# Copyright Colten Edwards 1999

SHELL = @SHELL@

srcdir = @srcdir@
VPATH = @srcdir@
top_srcdir = @top_srcdir@
topdir = @topdir@
prefix = @prefix@
exec_prefix = @exec_prefix@

bindir = @bindir@
sbindir = @sbindir@
libexecdir = @libexecdir@
datadir = @datadir@
sysconfdir = @sysconfdir@
[...986 lines suppressed...]
 ../include/window.h ../include/lastlog.h ../include/commands.h \
 ../include/exec.h ../include/vars.h ../include/cset.h \
 ../include/server.h ../include/who.h ../include/names.h \
 ../include/list.h ../include/ircterm.h ../include/gui.h \
 ../include/gtkbitchx.h ../include/translat.h ../include/input.h \
 ../include/status.h ../include/output.h ../include/log.h \
 ../include/hook.h ../include/dcc.h ../include/misc.h \
 ../include/module.h
words.o: words.c ../include/irc.h ../include/defs.h \
 ../include/config.h ../include/../.config.h ../include/color.h \
 ../include/bsdglob.h ../include/irc_std.h ../include/debug.h \
 ../include/newio.h ../include/ircaux.h
wserv.o: wserv.c ../include/defs.h ../include/config.h \
 ../include/../.config.h ../include/color.h ../include/irc.h \
 ../include/bsdglob.h ../include/irc_std.h ../include/debug.h \
 ../include/newio.h ../include/struct.h ../include/alist.h \
 ../include/ircaux.h ../include/hash.h ../include/ircterm.h \
 ../include/screen.h ../include/window.h ../include/lastlog.h \
 ../include/gui.h ../include/gtkbitchx.h ../include/translat.h
X.o: X.c

--- NEW FILE: if.c ---
/*
 * if.c: handles the IF command for IRCII 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990, 1991 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: if.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(if_c)
#include "struct.h"

#include "alias.h"
#include "ircaux.h"
#include "window.h"
#include "vars.h"
#include "output.h"
#include "if.h"
#include "commands.h"
#include "misc.h"
#define MAIN_SOURCE
#include "modval.h"

/*
 * next_expr finds the next expression delimited by brackets. The type
 * of bracket expected is passed as a parameter. Returns NULL on error.
 */
char	*my_next_expr(char **args, char type, int whine)
{
	char	*ptr,
		*ptr2,
		*ptr3;

	if (!*args)
		return NULL;
	ptr2 = *args;
	if (!*ptr2)
		return 0;
	if (*ptr2 != type)
	{
		if (whine)
			say("Expression syntax");
		return 0;
	}							/* { */
	ptr = MatchingBracket(ptr2 + 1, type, (type == '(') ? ')' : '}');
	if (!ptr)
	{
		say("Unmatched '%c'", type);
		return 0;
	}
	*ptr = '\0';

	do
		ptr2++;
	while (my_isspace(*ptr2));

	ptr3 = ptr+1;
	while (my_isspace(*ptr3))
		ptr3++;
	*args = ptr3;
	if (*ptr2)
	{
		ptr--;
		while (my_isspace(*ptr))
			*ptr-- = '\0';
	}
	return ptr2;
}

extern char *next_expr_failok (char **args, char type)
{
	return my_next_expr (args, type, 0);
}

extern char *next_expr (char **args, char type)
{
	return my_next_expr (args, type, 1);
}

/*
 * All new /if command -- (jfn, 1997)
 *
 * Here's the plan:
 *
 *		if (expr) ......
 *		if (expr) {......}
 *		if (expr) {......} {......}
 *		if (expr) {......} else {......}
 *		if (expr) {......} elsif (expr2) {......}
 * etc.
 */

BUILT_IN_COMMAND(ifcmd)
{
	int unless_cmd;
	char *current_expr;
	char *current_expr_val;
	int result;
	char *current_line = NULL;
	int flag = 0;

	unless_cmd = (*command == 'U');
	if (!subargs)
		subargs = empty_string;

	while (args && *args)
	{
		while (my_isspace(*args))
			args++;

		current_expr = next_expr(&args, '(');
		if (!current_expr)
		{
			error("IF: Missing expression");
			return;
		}
		current_expr_val = parse_inline(current_expr, subargs, &flag);
		if (internal_debug & DEBUG_EXPANSIONS && !in_debug_yell)
			debugyell("%s expression expands to: (%s)", command, current_expr_val);
		result = check_val(current_expr_val);
		new_free(&current_expr_val);

		if (*args == '{')
		{
			current_line = next_expr(&args, '{');
		}
		else
			current_line = args, args = NULL;

		/* If the expression was FALSE for IF, and TRUE for UNLESS */
		if (unless_cmd == result)
		{
			if (args)
			{
				if (!my_strnicmp(args, "elsif ", 6))
				{
					args += 6;
					continue;
				}
				else if (!my_strnicmp(args, "else ", 5))
					args += 5;

				while (my_isspace(*args))
					args++;

				if (*args == '{')
					current_line = next_expr(&args, '{');
				else
					current_line = args, args = NULL;

			}
			else
				current_line = NULL;
		}

		if (current_line)
			parse_line(NULL, current_line, subargs, 0, 0, 1);

		break;
	}
}

BUILT_IN_COMMAND(docmd)
{
	char *body, *expr, *cmd, *ptr;
	char *newexp = NULL;
	int args_used = 0;
	int result;

	if (*args == '{')
	{
		if (!(body = next_expr(&args, '{')))
		{
			error("DO: unbalanced {");
			return;
		}	
		if (args && *args && (cmd = next_arg(args, &args)) && 
		     !my_stricmp (cmd, "while"))
		{
			if (!(expr = next_expr(&args, '(')))
			{
				error("DO: unbalanced (");
				return;
			}
			will_catch_break_exceptions++;
			will_catch_return_exceptions++;

			while (1)
			{
				parse_line (NULL, body, subargs ? subargs : empty_string, 0, 0, 1);
				if (break_exception)
				{
					break_exception = 0;
					break;
				}
				if (continue_exception)
				{
					continue_exception = 0;
					continue;
				}

				if (return_exception)
					break;
				malloc_strcpy(&newexp, expr);
				ptr = parse_inline(newexp, subargs ? subargs : empty_string,
					&args_used);
				result = check_val(ptr);
				new_free(&ptr);
				if (!result)
					break;
			}	
			new_free(&newexp);
			will_catch_break_exceptions--;
			will_catch_continue_exceptions--;
			return;
		}
		/* falls through to here if its /do {...} */
		parse_line (NULL, body, subargs ? subargs : empty_string, 0, 0, 1);
	}
	/* falls through to here if it its /do ... */
	parse_line (NULL, args, subargs ? subargs : empty_string, 0, 0, 1);
}

/*ARGSUSED*/
BUILT_IN_COMMAND(whilecmd)
{
	char	*exp = NULL,
		*ptr = NULL,
		*body = NULL,
		*newexp = NULL;
	int	args_used;	/* this isn't used here, but is passed
				 * to expand_alias() */
	int whileval = !strcmp(command, "WHILE");

	if (!(ptr = next_expr(&args, '(')))
	{
		error("WHILE: missing boolean expression");
		return;
	}
	exp = LOCAL_COPY(ptr);
	if ((ptr = next_expr_failok(&args, '{')) == (char *) 0)
		ptr = args;

	body = LOCAL_COPY(ptr);

	will_catch_break_exceptions++;
	will_catch_continue_exceptions++;
	make_local_stack(NULL);
	while (1)
	{
		newexp = LOCAL_COPY(exp);
		ptr = parse_inline(newexp, subargs ? subargs : empty_string, &args_used);
		if (check_val(ptr) != whileval)
			break;
		new_free(&ptr);
		parse_line(NULL, body, subargs ?  subargs : empty_string, 0, 0, 0);
		if (continue_exception)
		{
			continue_exception = 0;
			continue;
		}
		if (break_exception)
		{
			break_exception = 0;
			break;
		}
		if (return_exception)
			break;
	}
	will_catch_break_exceptions--;
	will_catch_continue_exceptions--;
	destroy_local_stack();
	new_free(&ptr);
}

BUILT_IN_COMMAND(foreach)
{
	char	*struc = NULL,
		*ptr,
		*body = NULL,
		*var = NULL;
	char	**sublist;
	int	total;
	int	i;
	int	slen;
	int	old_display;
	int     list = VAR_ALIAS;
	int	af;
	
        while (args && my_isspace(*args))
        	args++;

        if (*args == '-')
                args++, list = COMMAND_ALIAS;

	if ((ptr = new_next_arg(args, &args)) == NULL)
	{
		error("FOREACH: missing structure expression");
		return;
	}
	struc = upper(remove_brackets(ptr, subargs, &af));

	if ((var = next_arg(args, &args)) == NULL)
	{
		new_free(&struc);
		error("FOREACH: missing variable");
		return;
	}
	while (my_isspace(*args))
		args++;

	if ((body = next_expr(&args, '{')) == NULL)	/* } */
	{
		new_free(&struc);
		error("FOREACH: missing statement");
		return;
	}

	if ((sublist = get_subarray_elements(struc, &total, list)) == NULL)
	{
		new_free(&struc);
		return;		/* Nothing there. */
	}

	slen=strlen(struc);
	old_display=window_display;
	make_local_stack(NULL);
	for (i=0;i<total;i++)
	{
		window_display=0;
		add_local_alias(var, sublist[i]+slen+1);
		window_display=old_display;
		parse_line(NULL, body, subargs ? subargs:empty_string, 0, 0, 0);
		new_free(&sublist[i]);
	}
	destroy_local_stack();
	new_free((char **)&sublist);
	new_free(&struc);
}

/*
 * FE:  Written by Jeremy Nelson (jnelson at iastate.edu)
 *
 * FE: replaces recursion
 *
 * The thing about it is that you can nest variables, as this command calls
 * expand_alias until the list doesnt change.  So you can nest lists in
 * lists, and hopefully that will work.  However, it also makes it 
 * impossible to have $s anywhere in the list.  Maybe ill change that
 * some day.
 */

BUILT_IN_COMMAND(fe)
{
	char    *list = NULL,
		*templist = NULL,
		*placeholder,
		*sa,
		*vars,
		*var[255],
		*word = NULL,
		*todo = NULL,
		fec_buffer[2];
	int     ind, x, y, blah = 0, args_flag;
	int     old_display;
	int	doing_fe = !my_stricmp(command, "FE");

	for (x = 0; x <= 254; var[x++] = NULL)
		;

	list = next_expr(&args, '(');

	if (!list)
	{
		error("%s: Missing List for /%s", command, command);
		return;
	}

	sa = subargs ? subargs : space;

	templist = expand_alias(list, sa, &args_flag, NULL);
	if (!templist || !*templist)
	{
		new_free(&templist);
		return;
	}

	vars = args;
	if (!(args = strchr(args, '{')))		/* } */
	{
		error("%s: Missing commands", command);
		new_free(&templist);
		return;
	}
	if ((char *)var == (char *)args)
	{
		error("%s: You did not specify any variables", command);
		new_free(&templist);
		return;
	}
	args[-1] = '\0';
	ind = 0;

	while ((var[ind++] = next_arg(vars, &vars)))
	{
		if (ind == 255)
		{
			error("%s: Too many variables", command);
			new_free(&templist);
			return;
		}
	}
	ind = ind ? ind - 1: 0;

	if (!(todo = next_expr(&args, '{')))		/* } { */
	{
		error("%s: Missing }", command);		
		new_free(&templist);
		return;
	}

	old_display = window_display;

	if (!doing_fe)
		{ word = fec_buffer; word[1] = 0; }
		
	blah = ((doing_fe) ? (word_count(templist)) : (strlen(templist)));
	placeholder = templist;

	will_catch_break_exceptions++;
	will_catch_continue_exceptions++;

	make_local_stack(NULL);
	for ( x = 0 ; x < blah ; )
	{
		window_display = 0;
		for ( y = 0 ; y < ind ; y++ )
		{
			if (doing_fe)
				word = ((x+y) < blah)
				    ? new_next_arg(templist, &templist)
				    : empty_string;
			else
				word[0] = ((x+y) < blah)
				    ? templist[x+y] : 0;

			add_local_alias(var[y], word);
		}
		window_display = old_display;
		x += ind;
		parse_line(NULL, todo, subargs?subargs:empty_string, 0, 0, 0);
		if (continue_exception)
		{
			continue_exception = 0;
			continue;
		}
		if (break_exception)
		{
			break_exception = 0;
			break;
		}
		if (return_exception)
			break;
	}

	destroy_local_stack();
	will_catch_break_exceptions--;
	will_catch_continue_exceptions--;

	window_display = old_display;
	new_free(&placeholder);
}

/* FOR command..... prototype: 
 *  for (commence,evaluation,iteration)
 * in the same style of C's for, the for loop is just a specific
 * type of WHILE loop.
 *
 * IMPORTANT: Since ircII uses ; as a delimeter between commands,
 * commas were chosen to be the delimiter between expressions,
 * so that semicolons may be used in the expressions (think of this
 * as the reverse as C, where commas seperate commands in expressions,
 * and semicolons end expressions.
 */
/*  I suppose someone could make a case that since the
 *  foreach_handler() routine weeds out any for command that doesnt have
 *  two commans, that checking for those 2 commas is a waste.  I suppose.
 */
BUILT_IN_COMMAND(forcmd)
{
	char        *working        = NULL;
	char        *commence       = NULL;
	char        *evaluation     = NULL;
	char        *lameeval       = NULL;
	char        *iteration      = NULL;
	char        *sa             = NULL;
	int         argsused        = 0;
	char        *blah           = NULL;
	char        *commands       = NULL;

	/* Get the whole () thing */
	if ((working = next_expr(&args, '(')) == NULL)	/* ) */
	{
		error("FOR: missing closing parenthesis");
		return;
	}
	commence = LOCAL_COPY(working);

	/* Find the beginning of the second expression */

	evaluation = strchr(commence, ',');
	if (!evaluation)
	{
		error("FOR: no components!");
		return;
	}
	do 
		*evaluation++ = '\0';
	while (my_isspace(*evaluation));

	/* Find the beginning of the third expression */
	iteration = strchr(evaluation, ',');
	if (!iteration)
	{
		error("FOR: Only two components!");
		return;
	}
	do 
	{
		*iteration++ = '\0';
	}
	while (my_isspace(*iteration));

	working = args;
	while (my_isspace(*working))
		*working++ = '\0';

	if ((working = next_expr(&working, '{')) == NULL)		/* } */
	{
		error("FOR: badly formed commands");
		return;
	}

	make_local_stack(NULL);

	commands = LOCAL_COPY(working);

	sa = subargs?subargs:empty_string;
	parse_line(NULL, commence, sa, 0, 0, 0);

	will_catch_break_exceptions++;
	will_catch_continue_exceptions++;
	
	while (1)
	{
		lameeval = LOCAL_COPY(evaluation);

		blah = parse_inline(lameeval,sa,&argsused);
		if (!check_val(blah))
		{
			new_free(&blah);
			break;
		}

		new_free(&blah);
		parse_line(NULL, commands, sa, 0, 0, 0);
		if (break_exception)
		{
			break_exception = 0;
			break;
		}
		if (continue_exception)
			continue_exception = 0;	/* Dont continue here! */
		if (return_exception)
			break;
		parse_line(NULL, iteration, sa, 0, 0, 0);
	}

	destroy_local_stack();
	will_catch_break_exceptions--;
	will_catch_continue_exceptions--;

	new_free(&blah);
}

/*

  Need to support something like this:

	switch (text to be matched)
	{
		(sample text)
		{
			...
		}
		(sample text2)
		(sample text3)
		{
			...
		}
		...
	}

How it works:

	The command is technically made up a single (...) expression and
	a single {...} expression.  The (...) expression is taken to be
	regular expando-text (much like the (...) body of /fe.

	The {...} body is taken to be a series of [(...)] {...} pairs.
	The [(...)] expression is taken to be one or more consecutive
	(...) structures, which are taken to be text expressions to match
	against the header text.  If any of the (...) expressions are found
	to match, then the commands in the {...} body are executed.

	There may be as many such [(...)] {...} pairs as you need.  However,
	only the *first* pair found to be matching is actually executed,
	and the others are ignored, so placement of your switches are
	rather important:  Put your most general ones last.

*/
BUILT_IN_COMMAND(switchcmd)
{
	char *control, *body, *header, *commands;
	int af;
	int found_def = 0;
	char *def = NULL;
	
	if (!(control = next_expr(&args, '(')))
	{
		error("SWITCH: String to be matched not found where expected");
		return;
	}

	control = expand_alias(control, subargs, &af, NULL);
	if (internal_debug & DEBUG_EXPANSIONS && !in_debug_yell)
		debugyell("%s expression expands to: (%s)", command, control);

	if (!(body = next_expr(&args, '{')))
		error("SWITCH: Execution body not found where expected");

	make_local_stack(NULL);
	while (body && *body)
	{
		int hooked = 0;

		while (*body == '(')
		{
			if (!(header = next_expr(&body, '(')))
			{
				error("SWITCH: Case label not found where expected");
				new_free(&control);
				return;
			}
			if (!strcmp(header, "default"))
			{
				if (def)
				{
					error("SWITCH: No more than one \"default\" case");
					new_free(&control);
					return;
				}
				found_def = 1;
			}
			header = expand_alias(header, subargs, &af, NULL);
			if (internal_debug & DEBUG_EXPANSIONS && !in_debug_yell)
				debugyell("%s expression expands to: (%s)", command, header);
			if (wild_match(header, control))
				hooked = 1;
			new_free(&header);
			if (*body == ';')
				body++;		/* ugh. */
		}

		if (!(commands = next_expr(&body, '{')))
		{
			error("SWITCH: case body not found where expected");
			break;
		}

		if (hooked)
		{
			parse_line(NULL, commands, subargs, 0, 0, 0);
			def = NULL;
			break;
		} 
		else if (!def && found_def)
		{
			def = LOCAL_COPY(commands);
			found_def = 0;
		}

		if (*body == ';')
			body++;		/* grumble */
	}
	if (def && *def)
		parse_line(NULL, def, subargs, 0, 0, 0);
	destroy_local_stack();
	new_free(&control);
}

BUILT_IN_COMMAND(repeatcmd)
{
	char *num_expr = NULL;
	int value;

	while (isspace((unsigned char)*args))
		args++;

	if (*args == '(')
	{
		char *tmp_val;
		char *dumb_copy;
		int argsused;
		char *sa = subargs ? subargs : empty_string;

		num_expr = next_expr(&args, '(');
		dumb_copy = LOCAL_COPY(num_expr);
		tmp_val = parse_inline(dumb_copy,sa,&argsused);
		value = my_atol(tmp_val);
		new_free(&tmp_val);
	}
	else
	{
		char *tmp_val;
		int af;

		num_expr = new_next_arg(args, &args);
		tmp_val = expand_alias(num_expr, subargs, &af, NULL);
		value = my_atol(tmp_val);
		new_free(&tmp_val);
	}

	if (value <= 0)
		return;
	while (value--)
		parse_line(NULL, args, subargs ? subargs : empty_string, 0, 0, 1);

	return;
}

--- NEW FILE: tcl_public.c ---

#include "irc.h"
static char cvsrevision[] = "$Id: tcl_public.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(tcl_public_c)
#include "ircaux.h"
#include "struct.h"
#include "commands.h"
#include "screen.h"
#include "server.h"
#include "tcl_bx.h"
#include "misc.h"
#include "userlist.h"
#include "output.h"
#include "log.h"
#include "dcc.h"
#include "timer.h"
#define MAIN_SOURCE
#include "modval.h"

[...1530 lines suppressed...]
	for (x = 0; C_dcc[x].func; x++) 
	{
		if (!my_stricmp(c, C_dcc[x].name) )
		{
			if ((C_dcc[x].access & atr) || !C_dcc[x].access)
				(C_dcc[x].func)(idx,args);
			else
				dcc_printf(idx, "Access denied.\n");
			from_server = old_server;
			return 1;
		}
	}
	dcc_printf(idx, "Invalid command [%s]\n", c);
	from_server = old_server;
	return 1;
}


#endif


--- NEW FILE: output.c ---
/*
 * output.c: handles a variety of tasks dealing with the output from the irc
 * program 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#include "irc.h"
static char cvsrevision[] = "$Id: output.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(output_c)
#include "struct.h"              
#include <sys/stat.h>

#include <stdarg.h>

#include "output.h"
#include "vars.h"
#include "input.h"
#include "ircaux.h"
#include "ircterm.h"
#include "lastlog.h"
#include "window.h"
#include "screen.h"
#include "server.h"
#include "hook.h"
#include "ctcp.h"
#include "log.h"
#include "misc.h"
#define MAIN_SOURCE
#include "modval.h"

	int	in_help = 0;

/* make this buffer *much* bigger than needed */

#define LARGE_BIG_BUFFER_SIZE BIG_BUFFER_SIZE * 10

static	char	*putbuf = NULL; /*[LARGE_BIG_BUFFER_SIZE + 1];*/
extern	LastMsg	last_servermsg[];

char three_stars[4] = "***";

/* unflash: sends a ^[c to the screen */
/* Must be defined to be useful, cause some vt100s really *do* reset when
   sent this command. >;-) */

/* functions which switch the character set on the console */
/* ibmpc is not available on the xterm */

void charset_ibmpc (void)
{
	fwrite("\033(U", 3, 1, current_ftarget);	/* switch to IBM code page 437 */
}

void charset_lat1 (void)
{
	fwrite("\033(B", 3, 1, current_ftarget);	/* switch to Latin-1 (ISO 8859-1) */
}

void charset_cst(void)
{
	fwrite("\033(K", 3, 1, current_ftarget); /* switch too user-defined */
}

/* currently not used. */


/* Now that you can send ansi sequences, this is much less inportant.. */
void unflash (void)
{
#if !defined(WINNT) && !defined(__EMX__)

#if defined(HARD_UNFLASH) && !defined(CHARSET_CUSTOM)
	fwrite("\033c", 5, 1, current_ftarget);		/* hard reset */
#else
	fwrite("\033)0", 6, 1, current_ftarget);		/* soft reset */
#endif

#if defined(LATIN1)
	charset_lat1();
#elif defined(CHARSET_CUSTOM)
	charset_cst();
#else
	charset_ibmpc();
#endif

#endif
}

/*
 * refresh_screen: Whenever the REFRESH_SCREEN function is activated, this
 * swoops into effect 
 */
void refresh_screen (unsigned char dumb, char *dumber)
{
extern int need_redraw;

#if !defined(WINNT) && !defined(__EMX__)
	term_clear_screen();
	unflash();
#else
	xterm_settitle();
	term_clear_screen();
#endif

#if 0
	for (tmp = screen_list; tmp; tmp = tmp->next)
		tmp->co = TI_cols, tmp->li = TI_lines;
#endif
	if (term_resize())
		recalculate_windows(current_window->screen);
	else
		redraw_all_windows();
	if (need_redraw)
		need_redraw = 0;
	update_all_windows();
	update_input(UPDATE_ALL);
}

/*
 * refresh_window_screen: Updates and redraws only the window's 
 * screen that was passed as a parameter. 
 */

#ifdef GUI
void refresh_window_screen(Window *window)
{
   xterm_settitle();
   recalculate_windows(current_window->screen);
   update_all_windows();
   update_input(UPDATE_ALL);
}
#endif

int init_output(void)
{
	if (!putbuf)
		putbuf = new_malloc(LARGE_BIG_BUFFER_SIZE+1);
	return 0;
}

/* init_windows:  */
int init_screen (void)
{
extern int term_initialized;
	term_initialized = 1;
	term_clear_screen();
	term_resize();
	create_new_screen();
	new_window(main_screen);
	update_all_windows();
	term_move_cursor(0, 0);
	return 0;
}

void put_echo (char *str)
{
	add_to_log(irclog_fp, 0, str, logfile_line_mangler);
	add_to_screen(str);
}


/*
 * put_it: the irc display routine.  Use this routine to display anything to
 * the main irc window.  It handles sending text to the display or stdout as
 * needed, add stuff to the lastlog and log file, etc.  Things NOT to do:
 * Dont send any text that contains \n, very unpredictable.  Tabs will also
 * screw things up.  The calling routing is responsible for not overwriting
 * the 1K buffer allocated.  
 *
 * For Ultrix machines, you can't call put_it() with floating point arguements.
 * It just doesn't work.  - phone, jan 1993.
 */
void BX_put_it(const char *format, ...)
{
	if (window_display && format)
	{
		va_list args;
		memset(putbuf, 0, 200);
		va_start (args, format);
		vsnprintf(putbuf, LARGE_BIG_BUFFER_SIZE, format, args);
		va_end(args);
		if (*putbuf)
			put_echo(putbuf);
	}
}

/* This is an alternative form of put_it which writes three asterisks
 * before actually putting things out.
 */
void say (const char *format, ...)
{
int len = 0;
	if (window_display && format)
	{
		va_list args;
		va_start (args, format);
		if (thing_ansi)
			len = strlen(thing_ansi);
		else
			len = 3;
		vsnprintf(&(putbuf[len+1]), LARGE_BIG_BUFFER_SIZE, format, args);
		va_end(args);
		strcpy(putbuf, thing_ansi?thing_ansi:three_stars);
		putbuf[len] = ' ';
		if (strip_ansi_in_echo) 
		{
			register char *ptr;
			for (ptr = putbuf + len; *ptr; ptr++)
				if (*ptr < 31 && *ptr > 13)
					if (*ptr != 15 && *ptr != 22)
						*ptr = (*ptr & 127) | 64;
		}
		put_echo(putbuf);
	}
}

void BX_bitchsay (const char *format, ...)
{
int len;
	if (window_display && format)
	{
		va_list args;
		va_start (args, format);
		sprintf(putbuf, "%s \002%s\002: ", thing_ansi?thing_ansi:three_stars, version);
		len = strlen(putbuf);
		vsnprintf(&(putbuf[len]), LARGE_BIG_BUFFER_SIZE, format, args);
		va_end(args);
		if (strip_ansi_in_echo) 
		{
			register char *ptr;
			for (ptr = putbuf+len; *ptr; ptr++)
				if (*ptr < 31 && *ptr > 13)
					if (*ptr != 15 && *ptr != 22)
						*ptr = (*ptr & 127) | 64;
		}
		put_echo(putbuf);
	}
}

void	BX_yell(const char *format, ...)
{
	if (format)
	{
		va_list args;
		va_start (args, format);
		*putbuf = 0;
		vsnprintf(putbuf, LARGE_BIG_BUFFER_SIZE, format, args);
		va_end(args);
		if (*putbuf && do_hook(YELL_LIST, "%s", putbuf))
			put_echo(putbuf);
	}
}


void	log_put_it (const char *topic, const char *format, ...)
{
	if (format)
	{
		va_list args;
		va_start (args, format);
		vsnprintf(putbuf, LARGE_BIG_BUFFER_SIZE, format, args);
		va_end(args);

		in_help = 1;
		set_display_target(NULL, LOG_CURRENT);
		if (window_display)
			put_echo(putbuf);
		reset_display_target();
		in_help = 0;
	}
}

char *ov_server(int server)
{
	char *c;
	char *d;
	static char tmpstr[61];
	char *string = get_server_itsname(server);
	    
	if (!string || !*string)
		string = get_server_name(server);
	if (!string || !*string)
		return  empty_string;
	strmcpy(tmpstr, string, 60);
	if (!(c = strrchr(tmpstr,'.')))
		return(string);
	*c = 0;
	if (!(d = strrchr(tmpstr, '.'))) 
		d = ++c; /* Extract domain */
	d++;
	return(d);
}

void serversay(int save, int from_server, const char *format, ...)
{
	Window	*old_target_window = target_window;
	char 	servername[200];
	int	len = 0;	
	char	*out = NULL;
	if (get_int_var(OV_VAR))
		target_window = get_window_by_name("OPER_VIEW");
        if (window_display && format)
        {
		va_list args;
		va_start (args, format);
		vsnprintf(putbuf, LARGE_BIG_BUFFER_SIZE, format, args);
		va_end(args);
		strmcpy(servername, convert_output_format(get_string_var(SERVER_PROMPT_VAR), "%s", ov_server(from_server)?ov_server(from_server):empty_string), 79);
		len = strlen(putbuf);
		out = alloca(strlen(servername)+len+5);
		len = strlen(servername);
		strcpy(out, servername); out[len] = ' '; out[len+1] = 0;
		strcat(out, putbuf);
		if (*out)
			put_echo(out);
	}
	target_window = old_target_window;
	if (save && out)
		add_last_type(&last_servermsg[0], MAX_LAST_MSG, NULL, NULL, NULL, out);
}
/*
 * Error is exactly like yell, except that if the error occured while
 * you were loading a script, it tells you where it happened.
 */
void 	error (const char *format, ...)
{
	dump_load_stack(0);
	if (format)
	{
		va_list args;
		va_start (args, format);
		vsnprintf(putbuf, LARGE_BIG_BUFFER_SIZE, format, args);
		va_end(args);
		do_hook(YELL_LIST, "%s", putbuf);
		put_echo(putbuf);
	}
}

--- NEW FILE: ircaux.c ---
/*
 * ircaux.c: some extra routines... not specific to irc... that I needed 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990, 1991 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */
#include "irc.h"
static char cvsrevision[] = "$Id: ircaux.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(ircaux_c)
#include "struct.h"

#include "alias.h"
#include "log.h"
#include "misc.h"
#include "vars.h"
#include "screen.h"
[...3187 lines suppressed...]

	read(random_fd, (void *)&value, sizeof(value));
	return value;
}


unsigned long	BX_random_number (unsigned long l)
{
	switch (get_int_var(RANDOM_SOURCE_VAR))
	{
		case 0:
		default:
			return randd(l);
		case 1:
			return randm(l);
		case 2:
			return randt(l);
	}
}


--- NEW FILE: ctcp.c ---
/*
 * ctcp.c:handles the client-to-client protocol(ctcp). 
 *
 * Written By Michael Sandrof 
 * Copyright(c) 1990, 1995 Michael Sandroff and others
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 *
 * Serious cleanup by jfn (August 1996)
 */


#include "irc.h"
static char cvsrevision[] = "$Id: ctcp.c,v 1.1.1.2 2003/05/27 07:00:22 root Exp $";
CVS_REVISION(ctcp_c)
#include "struct.h"

#include <pwd.h>


[...1627 lines suppressed...]

	*ctcp_dest = *after_ctcp = 0;
	ctcp_start = strchr(raw_message, CTCP_DELIM_CHAR);
	if (!ctcp_start)
		return;		/* No CTCPs present. */

	*ctcp_start++ = 0;
	ctcp_end = strchr(ctcp_start, CTCP_DELIM_CHAR);
	if (!ctcp_end)
	{
		*--ctcp_start = CTCP_DELIM_CHAR;
		return;		/* Thats _not_ a CTCP. */
	}

	*ctcp_end++ = 0;
	strmcpy(ctcp_dest, ctcp_start, IRCD_BUFFER_SIZE-2);
	strmcpy(after_ctcp, ctcp_end, IRCD_BUFFER_SIZE-2);

	return;		/* All done! */
}

--- NEW FILE: vars.c ---
/*
 * vars.c: All the dealing of the irc variables are handled here. 
 *
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: vars.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(vars_c)
#include "struct.h"

#include "status.h"
#include "window.h"
[...1683 lines suppressed...]
			h = nv;
		else
			h = host_to_ip(nv);
		set_string_var(NAT_ADDRESS_VAR, h);
        	nat_address.s_addr = inet_addr(h);
		use_nat_address = 2;
	}
	else
	{
		set_string_var(NAT_ADDRESS_VAR, NULL);
		use_nat_address = 0;
	}
}

#ifdef GUI
IrcVariable *return_irc_var(int nummer)
{
   return &irc_variable[nummer];
}
#endif

--- NEW FILE: status.c ---
/*
 * status.c: handles the status line updating, etc for IRCII 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: status.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(status_c)
#include "struct.h"

#include "ircterm.h"
#include "status.h"
#include "server.h"
[...1629 lines suppressed...]
		RETURN_EMPTY;
}

static	char	*status_windowspec	(Window *window)
{
static char my_buffer[81];
	if (window->wset->window_special_format)
		strmcpy(my_buffer, window->wset->window_special_format, 80);
	else
		*my_buffer = 0;

	return my_buffer;
}

static	char	*status_percent		(Window *window)
{
	static	char	percent[] = "%";
	return	percent;
}


--- NEW FILE: funny.c ---
/*
 * funny.c: Well, I put some stuff here and called it funny.  So sue me. 
 *
 * written by michael sandrof
 *
 * copyright(c) 1990 
 *
 * see the copyright file, or do a help ircii copyright 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: funny.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(funny_c)
#include "struct.h"

#include "ircaux.h"
#include "hook.h"
#include "vars.h"
#include "funny.h"
#include "names.h"
#include "server.h"
#include "lastlog.h"
#include "ircterm.h"
#include "output.h"
#include "numbers.h"
#include "parse.h"
#include "status.h"
#include "misc.h"
#include "screen.h"
#define MAIN_SOURCE
#include "modval.h"

static	char	*match_str = NULL;

static	int	funny_min;
static	int	funny_max;
static	int	funny_flags;

void funny_match(char  *stuff)
{
	malloc_strcpy(&match_str, stuff);
}

void set_funny_flags(int min, int max, int flags)
{
	funny_min = min;
	funny_max = max;
	funny_flags = flags;
}

struct	WideListInfoStru
{
	char	*channel;
	int	users;
};

typedef	struct WideListInfoStru WideList;

static	WideList **wide_list = NULL;
static	int	wl_size = 0;
static	int	wl_elements = 0;

static	int	funny_widelist_users (WideList **, WideList **);
static	int	funny_widelist_names (WideList **, WideList **);

static	int funny_widelist_users(WideList **left, WideList **right)
{
	if ((**left).users > (**right).users)
		return -1;
	else if ((**right).users > (**left).users)
		return 1;
	else
		return my_stricmp((**left).channel, (**right).channel);
}

static	int funny_widelist_names(WideList **left, WideList **right)
{
	int	comp;

	if (!(comp = my_stricmp((**left).channel, (**right).channel)))
		return comp;
	else if ((**left).users > (**right).users)
		return -1;
	else if ((**right).users > (**left).users)
		return 1;
	else
		return 0;
}


void funny_print_widelist(void)
{
	int	i;
	char	buffer1[BIG_BUFFER_SIZE];
	char	buffer2[BIG_BUFFER_SIZE];
	char	*ptr;

	if (!wide_list)
		return;

	if (funny_flags & FUNNY_NAME)
		qsort((void *) wide_list, wl_elements, sizeof(WideList *),
		(int (*) (const void *, const void *)) funny_widelist_names);
	else if (funny_flags & FUNNY_USERS)
		qsort((void *) wide_list, wl_elements, sizeof(WideList *),
		(int (*) (const void *, const void *)) funny_widelist_users);

	set_display_target(NULL, LOG_CRAP);
	*buffer1 = '\0';
	for (i = 1; i < wl_elements; i++)
	{
		sprintf(buffer2, "%s(%d) ", wide_list[i]->channel,
				wide_list[i]->users);
		ptr = strchr(buffer1, '\0');
		if (strlen(buffer1) + strlen(buffer2) > current_term->TI_cols - 5)
		{
			if (do_hook(WIDELIST_LIST, "%s", buffer1))
				put_it("%s", convert_output_format(fget_string_var(FORMAT_WIDELIST_FSET), "%s %s", update_clock(GET_TIME), buffer1));
			*buffer1 = 0;
			strcat(buffer1, buffer2);
		}
		else
			strcpy(ptr, buffer2);
	}
	if (*buffer1 && do_hook(WIDELIST_LIST, "%s", buffer1))
		put_it("%s", convert_output_format(fget_string_var(FORMAT_WIDELIST_FSET), "%s %s", update_clock(GET_TIME), buffer1));

	reset_display_target();
	for (i = 0; i < wl_elements; i++)
	{
		new_free(&wide_list[i]->channel);
		new_free((char **)&wide_list[i]);
	}
	new_free((char **)&wide_list);
	wl_elements = wl_size = 0;
}

void funny_list(char *from, char **ArgList)
{
	char	*channel,
		*user_cnt,
		*line;
	WideList **new_list;
	int	cnt;
	static	char	format[30];
	static	int	last_width = -1;

	if (last_width != get_int_var(CHANNEL_NAME_WIDTH_VAR))
	{
		if ((last_width = get_int_var(CHANNEL_NAME_WIDTH_VAR)) != 0)
			snprintf(format, 25, "%%s %%-%u.%us %%-5s %%s", /*thing_ansi,*/
				(unsigned char) last_width,
				(unsigned char) last_width);
		else
			snprintf(format, 25, "%%s %%s %%-5s %%s"/*, thing_ansi*/);
	}
	channel = ArgList[0];
	user_cnt = ArgList[1];
	line = PasteArgs(ArgList, 2);
	if (funny_flags & FUNNY_TOPIC && !(line && *line))
			return;
	cnt = my_atol(user_cnt);
	if (funny_min && (cnt < funny_min))
		return;
	if (funny_max && (cnt > funny_max))
		return;
	if ((funny_flags & FUNNY_PRIVATE) && (*channel != '*'))
		return;
	if ((funny_flags & FUNNY_PUBLIC) && ((*channel == '*') || (*channel == '@')))
		return;
	if (match_str)
	{
		if (wild_match(match_str, channel) == 0)
			return;
	}
	if (funny_flags & FUNNY_WIDE)
	{
		if (wl_elements >= wl_size)
		{
			new_list = (WideList **) new_malloc(sizeof(WideList *) *
			    (wl_size + 50));
			memset(new_list, 0, sizeof(WideList *) * (wl_size + 50));
			if (wl_size)
				memcpy(new_list, wide_list, sizeof(WideList *) * wl_size);
			wl_size += 50;
			new_free((char **)&wide_list);
			wide_list = new_list;
		}
		wide_list[wl_elements] = (WideList *)
			new_malloc(sizeof(WideList));
		wide_list[wl_elements]->channel = NULL;
		wide_list[wl_elements]->users = cnt;
		malloc_strcpy(&wide_list[wl_elements]->channel,
				(*channel != '*') ? channel : "Prv");
		wl_elements++;
		return;
	}
	set_display_target(channel, LOG_CRAP);
	if (do_hook(current_numeric, "%s %s %s %s", from,  channel, user_cnt,
	    line) && do_hook(LIST_LIST, "%s %s %s", channel, user_cnt, line))
	{
		if (channel && user_cnt)
			put_it("%s", convert_output_format(fget_string_var(FORMAT_LIST_FSET),"%s %s %s %s", update_clock(GET_TIME), *channel == '*'?"Prv":channel, user_cnt, line));
	}
	reset_display_target();
}

void print_funny_names(char *line)
{
register char *t;
int count = 0;
char buffer[BIG_BUFFER_SIZE+1];
char special = '\0';
int cols = get_int_var(NAMES_COLUMNS_VAR);
	if (!cols)
		cols = 1;
	if (line && *line)
	{	
		*buffer = 0;
		t = next_arg(line, &line);
		do {
			if (!count && fget_string_var(FORMAT_NAMES_BANNER_FSET))
				strcpy(buffer, convert_output_format(fget_string_var(FORMAT_NAMES_BANNER_FSET), NULL, NULL));
			if (*t == '@' || *t == '+' || *t == '~' || *t == '-')
			{
				special = *t;
				if (special == '+')
					strcat(buffer, convert_output_format(fget_string_var(FORMAT_NAMES_VOICECOLOR_FSET),"%c %s", special, ++t));
				else
					strcat(buffer, convert_output_format(fget_string_var(FORMAT_NAMES_OPCOLOR_FSET),"%c %s", special, ++t));
			}
			else
				strcat(buffer, convert_output_format(fget_string_var(FORMAT_NAMES_NICKCOLOR_FSET), "$ %s", t));
			strcat(buffer, space);
			if (count++ >= (cols - 1))
			{
				put_it("%s", buffer);
				*buffer = 0;
				count = 0;
			}
		} while ((t = next_arg(line, &line)));

		if (buffer)
			put_it("%s", buffer);
	}
}

void funny_namreply(char *from, char **Args)
{
char	*type,
	*channel;
static	char	format[40];
static	int	last_width = -1;
register char	*ptr;
register char	*line;
int user_count = 0;

	PasteArgs(Args, 2);
	type = Args[0];
	channel = Args[1];
	line = Args[2];

	/* protocol violation by server */
	if (!channel || !line)
		return;

	ptr = line;
	while (*ptr)
	{
		while (*ptr && (*ptr != ' '))
			ptr++;
		user_count++;
		while (*ptr && (*ptr == ' '))
			ptr++;
	}

	if (in_join_list(channel, from_server))
	{
		set_display_target(channel, LOG_CRAP);
		if (do_hook(current_numeric, "%s %s %s %s", from, type, channel,line) 
			&& do_hook(NAMES_LIST, "%s %s", channel, line)
			&& get_int_var(SHOW_CHANNEL_NAMES_VAR))
		{
			put_it("%s", convert_output_format(fget_string_var(FORMAT_NAMES_FSET), "%s %s %d",update_clock(GET_TIME), channel, user_count));
			print_funny_names(line);
		} 
		if ((user_count == 1) && (*line == '@'))
		{
			ChannelList *chan;
			if ((chan = lookup_channel(channel, from_server, CHAN_NOUNLINK)))
				if ((ptr = get_cset_str_var(chan->csets, CHANMODE_CSET)))
					my_send_to_server(from_server, "MODE %s %s", channel, ptr);
		}
		got_info(channel, from_server, GOTNAMES);
		reset_display_target();
		return;
	}
	if (last_width != get_int_var(CHANNEL_NAME_WIDTH_VAR))
	{
		if ((last_width = get_int_var(CHANNEL_NAME_WIDTH_VAR)) != 0)
			sprintf(format, "%%s: %%-%u.%us %%s",
				(unsigned char) last_width,
				(unsigned char) last_width);
		else
			strcpy(format, "%s: %s\t%s");
	}
	if (funny_min && (user_count < funny_min))
		return;
	else if (funny_max && (user_count > funny_max))
		return;
	if ((funny_flags & FUNNY_PRIVATE) && (*type == '='))
		return;
	if ((funny_flags & FUNNY_PUBLIC) && ((*type == '*') || (*type == '@')))
		return;
	if (type && channel)
	{
		if (match_str)
		{
			if (wild_match(match_str, channel) == 0)
				return;
		}
		if (do_hook(current_numeric, "%s %s %s %s", from, type, channel, line) && do_hook(NAMES_LIST, "%s %s", channel, line))
		{
			set_display_target(channel, LOG_CRAP);
			if (fget_string_var(FORMAT_NAMES_FSET))
			{
				put_it("%s", convert_output_format(fget_string_var(FORMAT_NAMES_FSET), "%s %s %d", update_clock(GET_TIME), channel, user_count));
				print_funny_names(line);
			} 
			else
			{
				switch (*type)
				{
				case '=':
					if (last_width &&(strlen(channel) > last_width))
					{
						channel[last_width-1] = '>';
						channel[last_width] = (char) 0;
					}
					put_it(format, "Pub", channel, line);
					break;
				case '*':
					put_it(format, "Prv", channel, line);
					break;
				case '@':
					put_it(format, "Sec", channel, line);
					break;
				}
			}
			reset_display_target();
		}
	}
}

void funny_mode(char *from, char **ArgList)
{
	char	*mode, *channel;
	ChannelList *chan = NULL;
		
	if (!ArgList[0]) return;

	channel = ArgList[0];
	mode = ArgList[1];
	PasteArgs(ArgList, 1);

	if((channel && in_join_list(channel, from_server)) || get_chan_from_join_list(from_server))
	{
		if (!channel)
			channel = get_chan_from_join_list(from_server);
		update_channel_mode(from, channel, from_server, mode, chan);
		update_all_status(current_window, NULL, 0);
		got_info(channel, from_server, GOTMODE);
	}
	else
	{
		if (channel)
		{
			set_display_target(channel, LOG_CRAP);
			if (do_hook(current_numeric, "%s %s %s", from, channel, mode))
				put_it("%s", convert_output_format(fget_string_var(FORMAT_MODE_CHANNEL_FSET), "%s %s %s %s %s", update_clock(GET_TIME), from, *FromUserHost ? FromUserHost:"ÿ", channel, mode));
			reset_display_target();
		}
		else
		{
			if (do_hook(current_numeric, "%s %s", from, mode))
				put_it("%s", convert_output_format(fget_string_var(FORMAT_MODE_CHANNEL_FSET), "%s %s %s %s", update_clock(GET_TIME), from, *FromUserHost ? FromUserHost:"ÿ", mode));
		}
	}
}

void update_user_mode(char *modes)
{
	int	onoff = 1;
	char	*p_umodes = get_possible_umodes(from_server);

	for (; *modes; modes++)
	{
		if (*modes == '-')
			onoff = 0;
		else if (*modes == '+')
			onoff = 1;

		else if   ((*modes >= 'a' && *modes <= 'z')
			|| (*modes >= 'A' && *modes <= 'Z'))
		{
			size_t 	idx;
			int 	c = *modes;

			idx = ccspan(p_umodes, c);
			if (p_umodes[idx] == 0)
				ircpanic("Invalid user mode referenced");
			set_server_flag(from_server, idx, onoff);

			if (c == 'o' || c == 'O')
				set_server_operator(from_server, onoff);
#if 0
			char c = tolower(*modes);
			size_t idx = (size_t) (strchr(umodes, c) - umodes);

			set_server_flag(from_server, USER_MODE << idx, onoff);

			if (c == 'o' || c == 'O')
				set_server_operator(from_server, onoff);
#endif
		}
	}
}

void	reinstate_user_modes (void)
{
	char *modes = get_umode(from_server);
	if (modes && *modes)
		send_to_server("MODE %s +%s", get_server_nickname(from_server), modes);
}

--- NEW FILE: pmbitchx.c ---
/* pmbitchx.c Written by: Brian Smith (NuKe) and rosmo for BitchX */


#include <sys/stat.h>
/* These headers from the Warp 4 toolkit */
#include <os2/mciapi.h>
#include <os2/mcios2.h>
#include <io.h>
#include <stddef.h>
/* We now require dynamic windows */
#include <dw.h>
#include "window.h"
#include "gui.h"
#include "server.h"
#include "hook.h"
#include "list.h"
#include "commands.h"
#include "hash2.h"
#include "status.h"
[...3703 lines suppressed...]

	pFEA2->oNextEntryOffset = 0;

	eaop2.fpFEA2List->cbList = ((PCHAR)pData+2*sizeof(USHORT)+
									 pFEA2->cbValue)-((PCHAR)eaop2.fpFEA2List);

	rc = DosSetPathInfo(fullname,
						FIL_QUERYEASIZE,
						&eaop2,
						sizeof(eaop2),
						0);

	free((void *)eaop2.fpFEA2List);
}

void gui_setfd(fd_set *rd)
{
	/* Set the GUI IPC pipe readable for select() */
	FD_SET(guiipc[0], rd);
}

--- NEW FILE: list.c ---
/*
 * list.c: some generic linked list managing stuff 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: list.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(list_c)
#include "struct.h"

#include "list.h"

#include "ircaux.h"
#define MAIN_SOURCE
#include "modval.h"

static	int	add_list_stricmp (List *, List *);
static	int	list_stricmp (List *, char *);
static	int	list_match (List *, char *);

/*
 * These have now been made more general. You used to only be able to
 * order these lists by alphabetical order. You can now order them
 * arbitrarily. The functions are still called the same way if you
 * wish to use alphabetical order on the key string, and the old
 * function name now represents a stub function which calls the
 * new with the appropriate parameters.
 *
 * The new function name is the same in each case as the old function
 * name, with the addition of a new parameter, cmp_func, which is
 * used to perform comparisons.
 *
 */

/*
static	int add_list_strcmp(List *item1, List *item2)
{
	return strcmp(item1->name, item2->name);
}
*/

static	int add_list_stricmp(List *item1, List *item2)
{
	return my_stricmp(item1->name, item2->name);
}

/*
static	int list_strcmp(List *item1, char *str)
{
	return strcmp(item1->name, str);
}
*/

static	int list_stricmp(List *item1, char *str)
{
	return my_stricmp(item1->name, str);
}

int list_strnicmp(List *item1, char *str)
{
	return my_strnicmp(item1->name, str, strlen(str));
}

static int  list_wildstrcmp(List *item1, char *str)
{
	if (wild_match(item1->name, str) || wild_match(str, item1->name))
		return 0;
	else
		return 1;
}

static	int list_match(List *item1, char *str)
{
	return wild_match(item1->name, str);
}

/*
 * add_to_list: This will add an element to a list.  The requirements for the
 * list are that the first element in each list structure be a pointer to the
 * next element in the list, and the second element in the list structure be
 * a pointer to a character (char *) which represents the sort key.  For
 * example 
 *
 * struct my_list{ struct my_list *next; char *name; <whatever else you want>}; 
 *
 * The parameters are:  "list" which is a pointer to the head of the list. "add"
 * which is a pre-allocated element to be added to the list.  
 */
void BX_add_to_list_ext(List **list, List *add, int (*cmp_func)(List *, List *))
{
register List	*tmp;
	 List	*last;

	if (!cmp_func)
		cmp_func = add_list_stricmp;
	last = NULL;
	for (tmp = *list; tmp; tmp = tmp->next)
	{
		if (cmp_func(tmp, add) > 0)
			break;
		last = tmp;
	}
	if (last)
		last->next = add;
	else
		*list = add;
	add->next = tmp;
}

void BX_add_to_list(List **list, List *add)
{
	add_to_list_ext(list, add, NULL);
}


/*
 * find_in_list: This looks up the given name in the given list.  List and
 * name are as described above.  If wild is true, each name in the list is
 * used as a wild card expression to match name... otherwise, normal matching
 * is done 
 */
List	* BX_find_in_list_ext(register List **list, char *name, int wild, int (*cmp_func)(List *, char *))
{
register List	*tmp;
	int	best_match,
		current_match;

	if (!cmp_func)
		cmp_func = wild ? list_match : list_stricmp;
	best_match = 0;

	if (wild)
	{
		register List	*match = NULL;

		for (tmp = *list; tmp; tmp = tmp->next)
		{
			if ((current_match = cmp_func(tmp, name)) > best_match)
			{
				match = tmp;
				best_match = current_match;
			}
		}
		return (match);
	}
	else
	{
		for (tmp = *list; tmp; tmp = tmp->next)
			if (cmp_func(tmp, name) == 0)
				return (tmp);
	}
	return NULL;
}

List	* BX_find_in_list(List **list, char *name, int wild)
{
	return find_in_list_ext(list, name, wild, NULL);
}

/*
 * remove_from_list: this remove the given name from the given list (again as
 * described above).  If found, it is removed from the list and returned
 * (memory is not deallocated).  If not found, null is returned. 
 */
List	*BX_remove_from_list_ext(List **list, char *name, int (*cmp_func)(List *, char *))
{
register	List	*tmp;
		List 	*last;

	if (!cmp_func)
		cmp_func = list_stricmp;
	last = NULL;
	for (tmp = *list; tmp; tmp = tmp->next)
	{
		if (!cmp_func(tmp, name))
		{
			if (last)
				last->next = tmp->next;
			else
				*list = tmp->next;
			return (tmp);
		}
		last = tmp;
	}
	return NULL;
}

List	*BX_remove_from_list(List **list, char *name)
{
	return remove_from_list_ext(list, name, NULL);
}

List	*BX_removewild_from_list(List **list, char *name)
{
	return remove_from_list_ext(list, name, list_wildstrcmp);
}


/*
 * list_lookup: this routine just consolidates remove_from_list and
 * find_in_list.  I did this cause it fit better with some already existing
 * code 
 */
List	*BX_list_lookup_ext(register List **list, char *name, int wild, int delete, int (*cmp_func)(List *, char *))
{
register List	*tmp;

	if (delete)
		tmp = remove_from_list_ext(list, name, cmp_func);
	else
		tmp = find_in_list_ext(list, name, wild, cmp_func);
	return (tmp);
}

List	*BX_list_lookup(List **list, char *name, int wild, int delete)
{
	return list_lookup_ext(list, name, wild, delete, NULL);
}

--- NEW FILE: timer.c ---
/*
 * timer.c -- handles timers in ircII
 * Copyright 1993, 1996 Matthew Green
 * This file organized/adapted by Jeremy Nelson
 *
 * This used to be in edit.c, and it used to only allow you to
 * register ircII commands to be executed later.  I needed more
 * generality then that, specifically the ability to register
 * any function to be called, so i pulled it all together into
 * this file and called it timer.c
 */

#include "irc.h"
static char cvsrevision[] = "$Id: timer.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(timer_c)
#include "struct.h"

#include "ircaux.h"
#include "lastlog.h"
#include "window.h"
#include "timer.h"
#include "hook.h"
#include "output.h"
#include "commands.h"
#include "misc.h"
#include "vars.h"
#include "server.h"
#include "screen.h"
#include "tcl_bx.h"
#define MAIN_SOURCE
#include "modval.h"

static	void	show_timer (char *command);
	void	BX_delete_all_timers (void);
int	timer_exists (char *);
static	TimerList *get_timer (char *ref);

#define RETURN_INT(x) return m_strdup(ltoa(x))

/*
 * timercmd: the bit that handles the TIMER command.  If there are no
 * arguements, then just list the currently pending timers, if we are
 * give a -DELETE flag, attempt to delete the timer from the list.  Else
 * consider it to be a timer to add, and add it.
 */
BUILT_IN_COMMAND(timercmd)
{
	char	*waittime,
		*flag;
	char	*want = empty_string;
	char	*ptr;
	double	interval;
	long	events = -2;
	int	update = 0;
	int	winref = current_window->refnum;
	
	while (*args == '-' || *args == '/')
	{
		flag = next_arg(args, &args);
		if (!flag || !*flag)
			break;

		if (!my_strnicmp(flag+1, "D", 1))	/* DELETE */
		{
			if (!(ptr = next_arg(args, &args)))
				say("%s: Need a timer reference number for -DELETE", command);
			else
			{
				if (timer_exists(ptr))
					delete_timer(ptr);
				else if (!my_strnicmp(ptr, "A", 1))
					delete_all_timers();
			}
			return;
		}
		else if (!my_strnicmp(flag+1, "REP", 3))
		{
			char *na = next_arg(args, &args);
			if (!na || !*na)
			{
				say("%s: Missing argument to -REPEAT", command);
				return;
			}
			if (!strcmp(na, "*") || !strcmp(na, "-1"))
				events = -1;
			else if ((events = my_atol(na)) == 0)			
				return;			
		}
		else if (!my_strnicmp(flag+1, "REF", 3))	/* REFNUM */
		{
			want = next_arg(args, &args);
			if (!want || !*want)
			{
				say("%s: Missing argument to -REFNUM", command);
				return;
			}
		}
		else if (!my_strnicmp(flag + 1, "L", 1))
			show_timer(command);
		else if (!my_strnicmp(flag + 1, "W", 1))	/* WINDOW */
		{
			char 	*na;

			if ((na = next_arg(args, &args)))
				winref = get_winref_by_desc(na);

			if (winref == -1 && my_stricmp(na, "-1"))
			{
				say("%s: That window doesnt exist!", command);
				return;
			}
		}
		else if (!my_strnicmp(flag + 1, "UPDATE", 1))	/* UPDATE */
			update = 1;
		else
			say("%s: %s no such flag", command, flag);
	}

	/* else check to see if we have no args -> list */

	waittime = next_arg(args, &args);
	if (update || waittime)
	{
		if (update && !timer_exists(want))
		{
			say("%s: To use -UPDATE you must specify a valid refnum", command);
			return;
		}

		if (!waittime)
			interval = -1;
		else
			interval = atof(waittime) * 1000.0;

		if (!update && events == -2)
			events = 1;
		else if (events == -2)
			events = -1;
		
		add_timer(update, want, interval, events, NULL, args, subargs, winref, NULL);
	} 
	else
		show_timer(command);
	return;
}

/*
 * This is put here on purpose -- we dont want any of the above functions
 * to have any knowledge of this struct.
 */
static TimerList *PendingTimers;
static char *schedule_timer (TimerList *ntimer);

static char *current_exec_timer = empty_string;

/*
 * ExecuteTimers:  checks to see if any currently pending timers have
 * gone off, and if so, execute them, delete them, etc, setting the
 * current_exec_timer, so that we can't remove the timer while its
 * still executing.
 *
 * changed the behavior: timers will not hook while we are waiting.
 */
extern	void ExecuteTimers (void)
{
	TimerList	*current;
        static int parsingtimer = 0;
	int		old_from_server = from_server;
	struct timeval	now1;
		
        /* We do NOT want to parse timers while waiting
         * cause it gets icky recursive
         */
        if (!PendingTimers || parsingtimer)
                return;
	get_time(&now1);
	
        parsingtimer = 1;
	while (PendingTimers && BX_time_diff(now1, PendingTimers->time) < 0)
	{
		int old_refnum = current_window->refnum;

		current = PendingTimers;
		PendingTimers = current->next;

		make_window_current_by_winref(current->window);		
		/*
		 * Restore from_server and curr_scr_win from when the
		 * timer was registered
		 */

		if (is_server_connected(current->server))
			from_server = current->server;
		else if (is_server_connected(get_window_server(0)))
			from_server = get_window_server(0);
		else
			from_server = -1;
						
		/* 
		 * If a callback function was registered, then
		 * we use it.  If no callback function was registered,
		 * then we use ''parse_line''.
		 */
		current_exec_timer = current->ref;
		if (current->callback)
			(*current->callback)(current->command, current->subargs);
		else
			parse_line("TIMER",(char *)current->command, current->subargs, 0, 0, 1);

		current_exec_timer = empty_string;
		from_server = old_from_server;
		make_window_current_by_winref(old_refnum);

		/*
		 * Clean up or reschedule the timer
		 */
		switch (current->events)
		{
			case 0:
			case 1:
			{
				/* callback cleans up command */
				if (!current->callback)
					new_free(&current->command);
				new_free(&current->subargs);
				new_free(&current->whom);
				new_free(&current);
				break;
			}
			default:
				current->events--;
			case -1:
			{
#if 1
				double milli, seconds;
				milli = current->interval * 1000 * 1000 + current->time.tv_usec;
				seconds = current->time.tv_sec + (milli / 1000000);
				milli = ((unsigned long)current) % 1000000;
				current->time.tv_sec = seconds;
				current->time.tv_usec = milli;
#else
				current->time.tv_usec += (current->interval * 1000);
				current->time.tv_sec += (current->time.tv_usec / 1000000);
				current->time.tv_usec %= 1000000;
#endif
				schedule_timer(current);
				break;	
			}
		}
		if (current && current->delete)
		{
			if (!current->callback)
				new_free(&current->command);
			new_free(&current->subargs);
			new_free(&current->whom);
			new_free(&current);
		}
	}
        parsingtimer = 0;
}

/*
 * show_timer:  Display a list of all the TIMER commands that are
 * pending to be executed.
 */
static	void	show_timer (char *command)
{
	TimerList	*tmp;
	struct timeval	current;
	double		time_left;
	int count = 0;
	
	for (tmp = PendingTimers; tmp; tmp = tmp->next)
		count++;

	if (!count)
	{
		say("%s: No commands pending to be executed", command);
		return;
	}

	get_time(&current);
	put_it("%s", convert_output_format(fget_string_var(FORMAT_TIMER_FSET), "%s %s %s %s","Timer","Seconds","Events","Command"));
	for (tmp = PendingTimers; tmp; tmp = tmp->next)
	{
		char buf[40];
		time_left = BX_time_diff(current, tmp->time);
		if (time_left < 0)
			time_left = 0;
		sprintf(buf, "%0.3f", time_left);
		put_it("%s", convert_output_format(fget_string_var(FORMAT_TIMER_FSET), "%s %s %d %s %s", tmp->ref, buf, tmp->events, tmp->callback? "(internal callback)" : (tmp->command? tmp->command : ""), tmp->whom ? tmp->whom : empty_string ));
	}
}

/*
 * create_timer_ref:  returns the lowest unused reference number for a timer
 *
 * This will never return 0 for a refnum because that is what atol() returns
 * on case of error, so that it can never happen that a timer has a refnum
 * of zero which would be tripped if the user did say,
 *	/TIMER -refnUm foobar 3 blah blah blah
 * which should elicit an error, not be silently punted.
 */
static	int	create_timer_ref (char *refnum_want, char *refnum_gets)
{
	TimerList       *tmp;
	int             refnum = 0;
                
	/* Max of 10 characters. */
	if (strlen(refnum_want) > REFNUM_MAX)
		refnum_want[REFNUM_MAX] = 0;

	/* If the user doesnt care */
	if (!strcmp(refnum_want, empty_string))
	{
		/* Find the lowest refnum available */
		for (tmp = PendingTimers; tmp; tmp = tmp->next)
		{
			long ref;
			ref = my_atol(tmp->ref);
			if (refnum < ref)
				refnum = ref;
		}
		strmcpy(refnum_gets, ltoa(refnum+1), REFNUM_MAX);
	}
	else
	{
		/* See if the refnum is available */
		for (tmp = PendingTimers; tmp; tmp = tmp->next)
		{
			if (!my_stricmp(tmp->ref, refnum_want))
				return -1;
		}
		strmcpy(refnum_gets, refnum_want, REFNUM_MAX);
	}

	return 0;
}

/*
 * Deletes a refnum.  This does cleanup only if the timer is a 
 * user-defined timer, otherwise no clean up is done (the caller
 * is responsible to handle it)  This shouldnt output an error,
 * it should be more general and return -1 and let the caller
 * handle it.  Probably will be that way in a future release.
 */
extern int BX_delete_timer (char *ref)
{
	TimerList	*tmp,
			*prev;

	if (current_exec_timer != empty_string)
	{
		say("You may not remove a TIMER from another TIMER");
		return -1;
	}

	for (prev = tmp = PendingTimers; tmp; prev = tmp, tmp = tmp->next)
	{
		/* can only delete user created timers */
		if (!my_stricmp(tmp->ref, ref))
		{
			if (tmp == prev)
				PendingTimers = PendingTimers->next;
			else
				prev->next = tmp->next;
			if (!tmp->callback)
				new_free(&tmp->command);
			new_free(&tmp->subargs);
			new_free(&tmp->whom);
			new_free((char **)&tmp);
			return 0;
		}
	}
	say("TIMER: Can't delete %s, no such refnum", ref);
	return -1;
}

int kill_timer(char *ref)
{
	TimerList	*tmp;
	for (tmp = PendingTimers; tmp; tmp = tmp->next)
	{
		/* can only delete user created timers */
		if (!my_stricmp(tmp->ref, ref))
		{
			tmp->delete = 1;
			return 0;
		}
	}
	return -1;
}

int get_delete_timer(char *ref)
{
	TimerList	*tmp;
	for (tmp = PendingTimers; tmp; tmp = tmp->next)
	{
		/* can only delete user created timers */
		if (!my_stricmp(tmp->ref, ref))
			return tmp->delete;
	}
	return -1;

}

void BX_delete_all_timers (void)
{
	while (PendingTimers)
		delete_timer(PendingTimers->ref);
	return;
}

int timer_exists (char *ref)
{
	if (get_timer(ref))
		return 1;
	return 0;
}

BUILT_IN_FUNCTION(function_istimer)
{
char *timer = NULL;

	if ((timer = next_arg(input, &input)))
		if (timer_exists(timer))
			RETURN_INT(1);
	RETURN_INT(0);
}

int timer_callback_exists (void *ref)
{
TimerList *t;
	for (t = PendingTimers; t; t = t->next)
	{
		if (t->callback && ref == t->callback)
			return 1;
	}
	return 0;
}

static	TimerList *get_timer (char *ref)
{
	TimerList *tmp;

	for (tmp = PendingTimers; tmp; tmp = tmp->next)
	{
		if (!my_stricmp(tmp->ref, ref) || (tmp->whom && !my_stricmp(tmp->whom, ref)))
			return tmp;
	}

	return NULL;
}

char *function_timer(char *n, char *args)
{
char *ref;
char *out = NULL;
TimerList *tmp;
	ref = next_arg(args, &args);
	if (ref && *ref && (tmp = get_timer(ref)))
	{
		double time_left;
		struct timeval current;
		char buf[40];
		get_time(&current);
		time_left = BX_time_diff(current, tmp->time);
		if (time_left < 0)
			time_left = 0.0;
		sprintf(buf, "%0.3f", time_left);
		malloc_sprintf(&out, "%s %d %d %d %d %s %s %s", tmp->ref, tmp->server, tmp->window, tmp->interval, tmp->events, buf, tmp->callback? "(internal callback)" : (tmp->command? tmp->command : ""), tmp->whom ? tmp->whom : empty_string );
		return ref;
	}
	return m_strdup(empty_string);
}

/*
 * You call this to register a timer callback.
 *
 * The arguments:
 *  refnum_want: The refnUm requested.  This should only be sepcified
 *		 by the user, functions wanting callbacks should specify
 *		 the value -1 which means "dont care".
 * The rest of the arguments are dependant upon the value of "callback"
 *	-- if "callback" is NULL then:
 *  callback:	 NULL
 *  what:	 some ircII commands to run when the timer goes off
 *  subargs:	 what to use to expand $0's, etc in the 'what' variable.
 *
 *	-- if "callback" is non-NULL then:
 *  callback:	 function to call when timer goes off
 *  what:	 argument to pass to "callback" function.  Should be some
 *		 non-auto storage, perhaps a struct or a malloced char *
 *		 array.  The caller is responsible for disposing of this
 *		 area when it is called, since the timer mechanism does not
 *		 know anything of the nature of the argument.
 * subargs:	 should be NULL, its ignored anyhow.
 */
char *BX_add_timer(int update, char *refnum_want, double when, long events, int (callback) (void *, char *), char *what, char *subargs, int winref, char *whom)
{
	TimerList	*ntimer, *otimer = NULL;
	char		refnum_got[REFNUM_MAX+1] = "";
	double		seconds = 0.0, milli = 0.0;	
extern	double		fmod(double, double);
	
	ntimer = (TimerList *) new_malloc(sizeof(TimerList));

	get_time(&ntimer->time);
#if 1
	milli = when * 1000 + ntimer->time.tv_usec;
	seconds = ntimer->time.tv_sec + (milli / 1000000);

	milli = ((unsigned long)milli) % 1000000;
	ntimer->time.tv_sec = seconds;
	ntimer->time.tv_usec = milli;
#else
	ntimer->time.tv_usec += (unsigned long)when;
	ntimer->time.tv_sec += ((when + ntimer->time.tv_usec) / 1000);
	ntimer->time.tv_usec %= 1000;
#endif

	ntimer->interval = when / 1000;
	ntimer->events = events;
	ntimer->server = from_server;
	ntimer->window = winref;
	if (whom) 
		ntimer->whom = m_strdup(whom);
	if (update)
		otimer = get_timer(refnum_want);
                        
	if (otimer)
	{
		if (when == -1)
		{
			ntimer->time = otimer->time;
			ntimer->interval = otimer->interval;
		}

		if (events == -1)
			ntimer->events = otimer->events;

		ntimer->callback = otimer->callback;
		if (what && *what)
		{
			ntimer->command = (void *)what;
			if (subargs)
				ntimer->subargs = m_strdup(subargs);
		}
		else
		{
			ntimer->command = m_strdup(otimer->command);
			ntimer->subargs = m_strdup(otimer->subargs);
		}

		delete_timer(refnum_want);
	}
	else 
	{
		if ((ntimer->callback = callback) != NULL)
			ntimer->command = (void *)what;
		else
			ntimer->command = m_strdup(what);
		if (subargs)
			ntimer->subargs = m_strdup(subargs);
	}

	if (create_timer_ref(refnum_want, refnum_got) == -1)
	{
		say("TIMER: Refnum %s already exists", refnum_want);
		new_free(&ntimer->command);
		new_free(&ntimer->subargs);
		new_free(&ntimer->whom);
		new_free(&ntimer);
		return NULL;
	}

	strcpy(ntimer->ref, refnum_got);	

	/* we've created it, now put it in order */
	return schedule_timer(ntimer);
}

static char *schedule_timer (TimerList *ntimer)
{
	TimerList **slot;

	/* we've created it, now put it in order */
	for (slot = &PendingTimers; *slot; slot = &(*slot)->next)
	{
		if (BX_time_diff((*slot)->time, ntimer->time) < 0)
			break;
	}
	ntimer->next = *slot;
	*slot = ntimer;
	return ntimer->ref;
}

static	struct timeval current;


/*
 * TimerTimeout:  Called from irc_io to help create the timeout
 * part of the call to select.
 */

time_t TimerTimeout (void)
{
	time_t	timeout_in;
	time_t t = MAGIC_TIMEOUT;
	
	if (is_server_queue() && (t = get_int_var(QUEUE_SENDS_VAR)))
		;
	else
		t = MAGIC_TIMEOUT;
	get_time(&current);
	if (!PendingTimers)
		timeout_in = tclTimerTimeout(MAGIC_TIMEOUT);
	else
	{
		timeout_in = (time_t)(BX_time_diff(current, PendingTimers->time) * 1000);
		timeout_in = (time_t)(tclTimerTimeout((timeout_in < 0) ? 0 : timeout_in));
	}
	
	if (t < timeout_in)
		timeout_in = t * 1000;
		
	return (timeout_in < 0) ? 0 : timeout_in + 100;
}

--- NEW FILE: user.c ---

#include "irc.h"
static char cvsrevision[] = "$Id: user.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(user_c)
#include "struct.h"

#include "ircaux.h"
#include "list.h"
#include "hash.h"
#include "struct.h"
#include "userlist.h"
#include "misc.h"
#include "output.h"
#define MAIN_SOURCE
#include "modval.h"

#define USERHOST_HASHSIZE 501
#define USERCHAN_HASHSIZE 20

#ifdef WANT_USERLIST
HashEntry UserListByHost_Table[USERHOST_HASHSIZE];
HashEntry UserListByChannel_Table[USERCHAN_HASHSIZE];

UserList *user_list = NULL;

static long hash_name(char *str, unsigned int size)
{
	const unsigned char *p = (const unsigned char *)str;
	unsigned long g, hash = 0;
	if (!p) return -1;
	while (*p)
	{
		if (*p == '*' || *p =='?' || *p == ',')
			return -1;
		hash = (hash << 4) + ((*p >= 'A' && *p <= 'Z') ? (*p+32) : *p);
		if ((g = hash & 0xF0000000))
			hash ^= g >> 24;
		hash &= ~g;
		p++;
	}
	return (hash % size);
}

  /* userlist entry can be put into our tier 1 */
static inline void add_userlist_to_tier_1(UserList *ptr, unsigned long hvalue)
{
        ptr->next = (UserList *) UserListByHost_Table[hvalue].list;
        /* assign our new linked list into array spot */
        UserListByHost_Table[hvalue].list = (void *) ptr;
        /* quick tally of userlists in chain in this array spot */
        UserListByHost_Table[hvalue].links++;
}

static inline void add_userlist_to_tier_2(UserList *ptr, unsigned long hvalue)
{
	ptr->next = (UserList *) UserListByChannel_Table[hvalue].list;
	/* assign our new linked list into array spot */
	UserListByChannel_Table[hvalue].list = (void *) ptr;
	/* quick tally of userlists in chain in this array spot */
	UserListByChannel_Table[hvalue].links++;
}

static inline void add_userlist_to_tier_3(UserList *uptr)
{
	add_to_list((List **)&user_list, (List *)uptr);
}

void add_userlist(UserList *uptr)
{
char *ptr;
long hvalue;
	if (!(ptr = strrchr(uptr->host, '@')))
	{
		add_userlist_to_tier_3(uptr);
		return;
	}
	if ((hvalue = hash_name(ptr, USERHOST_HASHSIZE)) != -1)
	{
		add_userlist_to_tier_1(uptr, hvalue);
		return;
	}
#if 0
	if (!strchr(uptr->channels, '*'))
	{
		char *channel, *ptr;
		channel = alloca(strlen(uptr->channels)+1);
		strcpy(channel, uptr->channels);
		while ((ptr = next_in_comma_list(channel, &channel)))
		{
			if (!*ptr)
				return;
			hvalue = hash_name(ptr, USERCHAN_HASHSIZE);
			add_userlist_to_tier_2(uptr, hvalue);
			if (channel && *channel)
			{
				UserList *newptr;
				newptr = new_malloc(sizeof(UserList));
				memcpy(newptr, uptr, sizeof(UserList));
				uptr = newptr;
			}
		}
	}
#else
	if ((hvalue = hash_name(uptr->channels, USERCHAN_HASHSIZE)) != -1)
	{
		add_userlist_to_tier_2(uptr, hvalue);
		return;
	}
#endif
	add_userlist_to_tier_3(uptr);
}

/*
 * move_link_to_top: used by find routine, brings link
 * to the top of the list in the specific array location
 */
static inline void move_link_to_top(UserList *tmp, UserList *prev, HashEntry *location)
{
	if (prev) 
	{
		UserList *old_list;
		old_list = (UserList *) location->list;
		location->list = (void *) tmp;
		prev->next = tmp->next;
		tmp->next = old_list;
	}
}

/*
 * remove_link_from_list: used by find routine, removes link
 * from our chain of hashed entries.
 */
static inline void remove_link_from_list(UserList *tmp, UserList *prev, HashEntry *location)
{
	if (prev) 
	{
		/* remove the link from the middle of the list */
		prev->next = tmp->next;
	}
	else {
		/* unlink the first link, and connect next one up */
		location->list = (void *) tmp->next;
	}
	/* set tmp's next to NULL, as its unlinked now */
	tmp->next = NULL;
}



static inline UserList *find_userlist_tier3(char *host, char *channel, int remove)
{
	register UserList *tmp;
	for (tmp = user_list; tmp; tmp = tmp->next)
	{
		if (wild_match(tmp->host, host) || !my_stricmp(tmp->host, host))
		{
			if (check_channel_match(tmp->channels, channel) || !strcmp(tmp->channels, channel))
			{
				if (remove)
					tmp = (UserList *)remove_from_list((List **)&user_list, tmp->nick);
				return tmp;
			}
		}
	}
	return NULL;
}

static inline UserList *find_userlist_tier1(char *userhost, char *channel, unsigned long hvalue, int remove)
{
HashEntry *location;
register UserList *tmp, *prev = NULL;
	location = &UserListByHost_Table[hvalue];
	for (tmp = (UserList *)location->list; tmp; prev = tmp, tmp = tmp->next)
	{
		if ((wild_match(tmp->host, userhost) ||!my_stricmp(tmp->host, userhost)) && check_channel_match(tmp->channels, channel))
		{
			if (remove)
			{
				location->links--;
				remove_link_from_list(tmp, prev, location);
			} else
				move_link_to_top(tmp, prev, location);
			location->hits++;
			return tmp;
		}
	}
	return NULL;
}

static inline UserList *find_userlist_tier2(char *userhost,unsigned long hvalue, int remove)
{
HashEntry *location;
register UserList *tmp, *prev = NULL;
	location = &UserListByChannel_Table[hvalue];
	for (tmp = (UserList *)location->list; tmp; prev = tmp, tmp = tmp->next)
	{
		if (wild_match(tmp->host, userhost) || !my_stricmp(tmp->host, userhost))
		{
			if (remove)
			{
				location->links--;
				remove_link_from_list(tmp, prev, location);
			} else
				move_link_to_top(tmp, prev, location);
			location->hits++;
			return tmp;
		}
	}
	return NULL;
}

UserList *find_userlist(char *userhost, char *channel, int remove)
{
char *ptr;
long hvalue;
/*char *host = clear_server_flags(userhost);*/
char *host = userhost;
	if (!(ptr = strrchr(userhost, '@')))
		return find_userlist_tier3(host, channel, remove);
	if ((hvalue = hash_name(ptr, USERHOST_HASHSIZE)) != -1)
	{
		UserList *tmp;
		if ((tmp = find_userlist_tier1(host, channel, hvalue, remove)))		
			return tmp;
	}	
	if ((hvalue = hash_name(channel, USERCHAN_HASHSIZE)) != -1)
	{
		UserList *tmp;
		if ((tmp = find_userlist_tier2(host, hvalue, remove)))
			return tmp;
	}	
	/* if we got here this is shitty */
	return find_userlist_tier3(host, channel, remove);
}

UserList *create_sorted_userlist(void)
{

	UserList *tmp = NULL, *t, *new;
	int i;
	for (i = 0; i < USERHOST_HASHSIZE; i++)
	{
		t = (UserList *)UserListByHost_Table[i].list;
		while (t)
		{
			new = new_malloc(sizeof(UserList));
			memcpy(new, t, sizeof(UserList));
			add_to_list((List **)&tmp, (List *)new);
			t = t->next;
		}
	}
	for (i = 0; i < USERCHAN_HASHSIZE; i++)
	{
		t = (UserList *)UserListByChannel_Table[i].list;
		while (t)
		{
			new = new_malloc(sizeof(UserList));
			memcpy(new, t, sizeof(UserList));
			add_to_list((List **)&tmp, (List *)new);
			t = t->next;
		}
	}
	for (t = user_list; t; t = t->next)
	{
		new = new_malloc(sizeof(UserList));
		memcpy(new, t, sizeof(UserList));
		add_to_list((List **)&tmp, (List *)new);
	}
	return tmp;
}

void destroy_sorted_userlist(UserList **uptr)
{
UserList *t;
	if (!uptr || !*uptr)
		return;
	while (*uptr)
	{
		t = (*uptr)->next;
		new_free(uptr);
		*uptr = t;
	}
}

#if 0
UserHist *next_userlist(UserList *uptr, int *size)
{
	long hvalue = -1;
	static HashEntry *location = NULL;
	
	if (!uptr)
	{
		location = &UserListByHost_Table[0];
		*size = USERHOST_HASHSIZE;
		hvalue = 0;
		while(((UserList *)location[hvalue].list) == NULL)
		{
			hvalue++;
			if (hvalue == USERHOST_HASHSIZE && *size == USERHOST_HASHSIZE)
			{
				location = &UserListByChannel_Table[0];
				hvalue = 0;
				*size = USERCHAN_HASHSIZE;
			}
			else if (hvalue == USERCHAN_HASHSIZE && *size == USERCHAN_HASHSIZE)
			{
				hvalue = -1;
				break;
			}
		}		
		if (hvalue != -1 && location[hvalue].list != NULL)
			return ((UserHist *)location[hvalue].list);
		location = NULL;		
		*size = -1;
		return (user_list);
	}
	if (uptr->next)
		return uptr->next;
	else if (location && !uptr->next)
	{
		if (*size > -1 && location)
		{
			if (*size == USERHOST_HASHSIZE)
				hvalue = hash_name(strrchr(uptr->host, '@'), *size);
			else if (*size == USERCHAN_HASHSIZE)
				hvalue = hash_name(uptr->channels, *size);
			if (hvalue == -1)
			{
				location = NULL;
				*size = -1;
				return user_list;
			}
			hvalue++;
			if (*size == USERHOST_HASHSIZE && hvalue >= USERHOST_HASHSIZE)
			{
				/* end of current list */
				location = &UserListByChannel_Table[0];
				*size = USERCHAN_HASHSIZE;
			}
			while (((UserList *)location[hvalue].list) == NULL)
			{
				hvalue++;
				if (hvalue >= USERHOST_HASHSIZE)
				{
					location = &UserListByChannel_Table[0];
					hvalue = 0;
					*size = USERCHAN_HASHSIZE;
				}
				else if (*size == USERCHAN_HASHSIZE && hvalue == USERCHAN_HASHSIZE)
					break;
			}
			if (location[hvalue].list != NULL)
				return ((UserList *)location[hvalue].list);
			location = NULL;		
			*size = -1;
			return user_list;
		}
	}	
	return NULL;
}
#endif

static inline int check_best_passwd(char *passwd, char *test)
{
	if (passwd && test)
		return !checkpass(test, passwd) ? 1 : 0;
/*		return !strcmp(passwd, test) ? 1 : 0;*/
	return 0;
}

UserList *find_bestmatch(char *nick, char *userhost, char *channel, char *passwd)
{
UserList *best = NULL;
UserList *best_passwd = NULL;
register UserList *tmp;
long hvalue = 0;
int	best_user_match = 0,
	best_chan_match = 0;
int	chan_match,
	user_match,
	passwd_match = 0;
char	*check;

	/*check = clear_server_flags(userhost);*/
	check = userhost;
	if ((hvalue = hash_name(strrchr(check, '@'), USERHOST_HASHSIZE)) != -1)
	{
		for (tmp = (UserList *)UserListByHost_Table[hvalue].list; tmp; tmp = tmp->next)
		{
			user_match = wild_match(tmp->host, check);
			chan_match = check_channel_match(tmp->channels, channel);
			if (user_match > best_user_match && chan_match > best_chan_match)
			{
				best_chan_match = chan_match;
				best_user_match = user_match;
				best = tmp;
				if ((passwd_match = check_best_passwd(tmp->password, passwd)))
					best_passwd = tmp;
			} 
			else if (best_user_match && user_match == best_user_match)
			{
				if (chan_match > best_chan_match)
				{
					best_chan_match = chan_match;
					best_user_match = user_match;
					best = tmp;
					if ((passwd_match = check_best_passwd(tmp->password, passwd)))
						best_passwd = tmp;
				}
			}
		}
	}
	if ((hvalue = hash_name(channel, USERCHAN_HASHSIZE)) != -1)
	{
		for (tmp = (UserList *)UserListByChannel_Table[hvalue].list; tmp; tmp = tmp->next)
		{
			user_match = wild_match(tmp->host, check);
			if (user_match > best_user_match)
			{
				best_user_match = user_match;
				best = tmp;
				if ((passwd_match = check_best_passwd(tmp->password, passwd)))
					best_passwd = tmp;
			}
		}
	}
	else if (hvalue == -1)
	{
		for (hvalue = 0; hvalue < USERCHAN_HASHSIZE; hvalue++)
		{
			for (tmp = (UserList *)UserListByChannel_Table[hvalue].list; tmp; tmp = tmp->next)
			{
				user_match = wild_match(tmp->host, check);
				chan_match = check_channel_match(tmp->channels, channel);
				if (user_match > best_user_match && chan_match > best_chan_match)
				{
					best_user_match = user_match;
					best_chan_match = chan_match;
					best = tmp;
					if ((passwd_match = check_best_passwd(tmp->password, passwd)))
						best_passwd = tmp;
				}
				else if (best_user_match && user_match == best_user_match)
				{
					if (chan_match > best_chan_match) 
					{
						best_chan_match = chan_match;
						best_user_match = user_match;
						best = tmp;
						if ((passwd_match = check_best_passwd(tmp->password, passwd)))
							best_passwd = tmp;
					}
				}
			}
		}
	}
	for (tmp = user_list; tmp; tmp = tmp->next) 
	{
		if ((chan_match = check_channel_match(tmp->channels, channel)) > 0) 
		{
			user_match = wild_match(tmp->host, check);
			if (user_match > best_user_match && chan_match > best_chan_match)
			{
				best_chan_match = chan_match;
				best_user_match = user_match;
				best = tmp;
				if ((passwd_match = check_best_passwd(tmp->password, passwd)))
					best_passwd = tmp;
			}
			else if (best_user_match && user_match == best_user_match)
			{
				if (chan_match > best_chan_match) 
				{
					best_chan_match = chan_match;
					best_user_match = user_match;
					best = tmp;
					if ((passwd_match = check_best_passwd(tmp->password, passwd)))
						best_passwd = tmp;
				}
			}
		}
	}
	if (passwd)
	{
		if (!best_passwd)
			return NULL;
		else if (best_passwd)
			return best_passwd;
	}
	return best;
}

BUILT_IN_COMMAND(debug_user)
{
HashEntry *location;
UserList *user;
int i;
int tier1 = 0, tier2 = 0, tier3 = 0;
	for (i = 0; i < USERHOST_HASHSIZE; i++)
	{
		user = (UserList *)UserListByHost_Table[i].list;
		location = &UserListByHost_Table[i];
		while (user)
		{
			tier1++;
			put_it("Tier1[%d] %d %d %s %s", i, location->links, location->hits, user->host, user->channels);
			user = user->next;
		}
	}
	for (i = 0; i < USERCHAN_HASHSIZE; i++)
	{
		user = (UserList *)UserListByChannel_Table[i].list;
		location = &UserListByChannel_Table[i];
		while (user)
		{
			tier2++;
			put_it("Tier2[%d] %d %d %s %s", i, location->links, location->hits, user->host, user->channels);
			user = user->next;
		}
	}
	for (tier3 = 0, user = user_list; user; user = user->next, tier3++)
		put_it("Tier3[%d] %s %s", tier3, user->host, user->channels);
	put_it("Tier1 = [%d] Tier2 = [%d] Tier3 = [%d]", tier1, tier2, tier3); 

}

UserList *next_userlist(UserList *uptr, int *size, void **location)
{
unsigned long hvalue = -1;
	/* we start iterating the list will a null pointer */
	if (!uptr)
	{
		*location = &UserListByHost_Table[0];
		*size = USERHOST_HASHSIZE;
		hvalue = 0;
		while(((UserList *)((HashEntry*)*location)[hvalue].list) == NULL)
		{
			hvalue++;
			if (hvalue == USERHOST_HASHSIZE && *size == USERHOST_HASHSIZE)
			{
				*location = &UserListByChannel_Table[0];
				hvalue = 0;
				*size = USERCHAN_HASHSIZE;
			}
			else if (hvalue == USERCHAN_HASHSIZE && *size == USERCHAN_HASHSIZE)
			{
				hvalue = -1;
				break;
			}
		}		
		if (hvalue != -1 && ((HashEntry *)*location)[hvalue].list != NULL)
			return ((UserList *)((HashEntry *)*location)[hvalue].list);
		*location = NULL;		
		*size = -1;
		return (user_list);
	}
	else if (uptr->next)
		return uptr->next;
	else if (*location)
	{
		if (*size > -1)
		{
			if (*size == USERHOST_HASHSIZE)
				hvalue = hash_name(strrchr(uptr->host, '@'), *size);
			else if (*size == USERCHAN_HASHSIZE)
				hvalue = hash_name(uptr->channels, *size);
			if (hvalue == -1)
			{
				*location = NULL;
				*size = -1;
				return user_list;
			}
			hvalue++;
			if (*size == USERHOST_HASHSIZE && hvalue >= USERHOST_HASHSIZE)
			{
				/* end of current list */
				*location = &UserListByChannel_Table[0];
				*size = USERCHAN_HASHSIZE;
			}
			while (((UserList *)((HashEntry *)*location)[hvalue].list) == NULL)
			{
				hvalue++;
				if (hvalue >= USERHOST_HASHSIZE)
				{
					*location = &UserListByChannel_Table[0];
					hvalue = 0;
					*size = USERCHAN_HASHSIZE;
				}
				else if (*size == USERCHAN_HASHSIZE && hvalue >= USERCHAN_HASHSIZE)
				{
					hvalue = -1;
					break;
				}
			}
			if (hvalue != -1 && ((HashEntry *)*location)[hvalue].list != NULL)
				return ((UserList *)((HashEntry *)*location)[hvalue].list);
			*location = NULL;		
			*size = -1;
			return user_list;
		}
	}
	return NULL;
}
#endif

--- NEW FILE: mail.c ---
/*
 * Mail check routines. Based on EPIC's mail check
 */


#include "irc.h"
static char cvsrevision[] = "$Id: mail.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(mail_c)
#include "struct.h"

#include "mail.h"
#include "lastlog.h"
#include "hook.h"
#include "vars.h"
#include "ircaux.h"
#include "output.h"
#include "window.h"
#include "status.h"
#include "misc.h"
#include "module.h"
#define MAIN_SOURCE
#include "modval.h"

#include <sys/stat.h>

#if	!defined(HAVE_QMAIL)
static	char	*mail_path = NULL;
#endif

#if	defined(HAVE_QMAIL)
/*
 * count_maildir_mail: counts total # of mails (in cur and new)
 */
int count_maildir_mail(void)
{
	int count = 0;
	DIR *dp;
	struct dirent *dir;
	char *mail_path, *m;
	
	m = m_sprintf("%s/cur", UNIX_MAIL);
	mail_path = expand_twiddle(m);
	new_free(&m);
        
	if ((dp = opendir(mail_path)))
	{
		while ((dir = readdir(dp)))
		{
			if (!dir->d_ino || (dir->d_name[0] == '.'))
				continue;
			count++;
		}
	
		closedir(dp);
        }
        
        new_free(&mail_path);
        m = m_sprintf("%s/new", UNIX_MAIL);
	mail_path = expand_twiddle(m);           
	new_free(&m);
                                
	if ((dp = opendir(mail_path)))
	{
		while ((dir = readdir(dp)))
		{
			if (!dir->d_ino || (dir->d_name[0] == '.'))
				continue;
			count++;
		}
	
		closedir(dp);
        }
        
        new_free(&mail_path);

        return count;
}
#endif

#ifndef UNIX_MAIL
#define UNIX_MAIL "/var/spool/mail"
#endif

#ifndef MAIL_DELIMITER
#define MAIL_DELIMITER "From "
#endif


#ifdef WANT_DLL
#define check_ext_mail global_table[CHECK_EXT_MAIL]
#define check_ext_mail_status global_table[CHECK_EXT_MAIL_STATUS]
#endif

/*
 * check_mail_status: returns 0 if mail status has not changed, 1 if mail
 * status has changed 
 */
#ifdef PUBLIC_ACCESS
int check_mail_status(void)
{
	return 0;
}
#else
int check_mail_status(void)
{
#if defined(HAVE_QMAIL)
	int count = 0;
	static int c = 0;
#else
static time_t old_stat = 0;
struct stat stat_buf;
#endif        
#ifdef WANT_DLL
	if (check_ext_mail)
		return (*check_ext_mail_status)();
#endif
	if (!get_int_var(MAIL_VAR))
	{
#if defined(HAVE_QMAIL)
		c = 0;
#else
		old_stat = 0;
#endif
		return (0);
	}
	
#if defined(HAVE_QMAIL)
	count = count_maildir_mail();
	if (count > c)
	{
		c = count;
		return c;
	}
	if (count < c)
	{
		int diff;
		diff = count - c;
		c = count;
		return diff;
	}
#else
	if (!mail_path)
	{
		char *tmp_mail_path;
		if ((tmp_mail_path = getenv("MAIL")) != NULL)
			mail_path = m_strdup(tmp_mail_path);
                else
#ifdef __EMX__
                        mail_path = m_sprintf("%s/mqueue", getenv("ETC"));
#else
                        mail_path = m_3dup(UNIX_MAIL, "/", username);
#endif
	}

	if (stat(mail_path, &stat_buf) == -1)
		return 0;

	if (stat_buf.st_ctime > old_stat)
	{
		old_stat = stat_buf.st_ctime;
		if (stat_buf.st_size)
			return 2;
	}
	if (stat_buf.st_size)
		return 1;
#endif		
	return 0;
}
#endif


/*
 * check_mail: This here thing counts up the number of pieces of mail and
 * returns it as static string.  If there are no mail messages, null is
 * returned. 
 */

char	*check_mail (void)
{
static 	int old_count = 0;
static	char ret_str[12];
static  int	i = 0;
#ifdef WANT_DLL
	if (check_ext_mail)
		return (char *)(*check_ext_mail)();
#endif
	switch (get_int_var(MAIL_VAR))
	{
		case 0:
			return NULL;
		case 1:
		{
			char this[] = "\\|/-";
#if defined(HAVE_QMAIL)
			int count = check_mail_status();
			if (count > 0)
			{
				set_display_target(NULL, LOG_CRAP);
				if (do_hook(MAIL_LIST, "%s %s", "Mail", "Yes"))
					put_it("%s", convert_output_format(fget_string_var(FORMAT_MAIL_FSET), "%s %s %s", update_clock(GET_TIME), "Mail", "Yes"));
				++i;
				reset_display_target();
				if (i == 4)
					i = 0;
			}
			sprintf(ret_str, "%c", this[i]);

			return ret_str;
#else
			switch(check_mail_status())
			{
				case 2:
					set_display_target(NULL, LOG_CRAP);
					if (do_hook(MAIL_LIST, "%s %s", "Mail", "Yes"))
						put_it("%s", convert_output_format(fget_string_var(FORMAT_MAIL_FSET), "%s %s %s", update_clock(GET_TIME), "Mail", "Yes"));
					reset_display_target();
					if (i == 4)
						i = 0;
					sprintf(ret_str, "%c", this[i++]);
				case 1:
					if (!*ret_str)
						return NULL;
					return ret_str;
				case 0:
					i = 0;
					return NULL;
			}
#endif
		}
		case 2:
		{
			register int count = 0;
#if defined(HAVE_QMAIL)
			count = count_maildir_mail();
			if (count == 0)
			{
				old_count = 0;
				return NULL;
			}
			if  (count > old_count)
			{
				set_display_target(NULL, LOG_CRAP);
				if (do_hook(MAIL_LIST, "%d %d", count - old_count, count))
					say("You have new email.");
				reset_display_target();
			}

			old_count = count;
			sprintf(ret_str, "%d", old_count);
			return ret_str;
#else
			FILE *mail;
			char buffer[255];
			switch(check_mail_status())
			{

				case 0:
					old_count = 0;
					return NULL;
				case 2:
				{
					if (!(mail = fopen(mail_path, "r")))
						return NULL;

					while (fgets(buffer, 254, mail))
						if (!strncmp(MAIL_DELIMITER, buffer, 5))
							count++;

					fclose(mail);
	
					if (count > old_count)
					{
						set_display_target(NULL, LOG_CRAP);
						if (do_hook(MAIL_LIST, "%d %d", count - old_count, count))
							say("You have new email.");
						reset_display_target();
					}
	
					old_count = count;
					sprintf(ret_str, "%d", old_count);
				}
				case 1:
					if (*ret_str)
						return ret_str;
			}
#endif
		}
	}
	return NULL;
}

--- NEW FILE: hash.c ---
/************
 *  hash.c  *
 ************
 *
 * My hash routines for hashing NickLists, and eventually ChannelList's
 * and WhowasList's
 *
 * These are not very robust, as the add/remove functions will have
 * to be written differently for each type of struct
 * (To DO: use C++, and create a hash "class" so I don't need to
 * have the functions different.)
 *
 *
 * Written by Scott H Kilau
 *
 * Copyright(c) 1997
 *
 * Modified by Colten Edwards for use in BitchX.
 * Added Whowas buffer hashing.
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
 */

#include "irc.h"
static char cvsrevision[] = "$Id: hash.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(hash_c)
#include "struct.h"
#include "ircaux.h"
#include "hook.h"
#include "vars.h"
#include "output.h"
#include "misc.h"
#include "server.h"
#include "list.h"
#include "window.h"

#include "hash.h"
#include "hash2.h"
#define MAIN_SOURCE
#include "modval.h"

/*
 * hash_nickname: for now, does a simple hash of the 
 * nick by counting up the ascii values of the lower case, and 
 * then %'ing it by NICKLIST_HASHSIZE (always a prime!)
 */
unsigned long hash_nickname(char *nick, unsigned int size)
{
        register u_char  *p = (u_char *) nick;
	unsigned long hash = 0, g;
	if (!nick) return -1;
	while (*p)
	{
		hash = (hash << 4) + ((*p >= 'A' && *p <= 'Z') ? (*p+32) : *p);
		if ((g = hash & 0xF0000000))
			hash ^= g >> 24;
		hash &= ~g;
		p++;
	}
	return (hash %= size);
}

/*
 * move_link_to_top: used by find routine, brings link
 * to the top of the list in the specific array location
 */
static inline void move_link_to_top(NickList *tmp, NickList *prev, HashEntry *location)
{
	if (prev) 
	{
		NickList *old_list;
		old_list = (NickList *) location->list;
		location->list = (void *) tmp;
		prev->next = tmp->next;
		tmp->next = old_list;
	}
}

/*
 * remove_link_from_list: used by find routine, removes link
 * from our chain of hashed entries.
 */
static inline void remove_link_from_list(NickList *tmp, NickList *prev, HashEntry *location)
{
	if (prev) 
	{
		/* remove the link from the middle of the list */
		prev->next = tmp->next;
	}
	else {
		/* unlink the first link, and connect next one up */
		location->list = (void *) tmp->next;
	}
	/* set tmp's next to NULL, as its unlinked now */
	tmp->next = NULL;
}

void BX_add_name_to_genericlist(char *name, HashEntry *list, unsigned int size)
{
	List *nptr;
	unsigned long hvalue = hash_nickname(name, size);

	nptr = (List *) new_malloc(sizeof(List));
	nptr->next = (List *) list[hvalue].list;
	nptr->name = m_strdup(name);

	/* assign our new linked list into array spot */
	list[hvalue].list = (void *) nptr;
	/* quick tally of nicks in chain in this array spot */
	list[hvalue].links++;
	/* keep stats on hits to this array spot */
	list[hvalue].hits++;
}

/*
 * move_link_to_top: used by find routine, brings link
 * to the top of the list in the specific array location
 */
static inline void move_gen_link_to_top(List *tmp, List *prev, HashEntry *location)
{
	if (prev) 
	{
		List *old_list;
		old_list = (List *) location->list;
		location->list = (void *) tmp;
		prev->next = tmp->next;
		tmp->next = old_list;
	}
}

/*
 * remove_link_from_list: used by find routine, removes link
 * from our chain of hashed entries.
 */
static inline void remove_gen_link_from_list(List *tmp, List *prev, HashEntry *location)
{
	if (prev) 
	{
		/* remove the link from the middle of the list */
		prev->next = tmp->next;
	}
	else {
		/* unlink the first link, and connect next one up */
		location->list = (void *) tmp->next;
	}
	/* set tmp's next to NULL, as its unlinked now */
	tmp->next = NULL;
}

List *BX_find_name_in_genericlist(char *name, HashEntry *list, unsigned int size, int remove)
{
	HashEntry *location;
	register List *tmp, *prev = NULL;
	unsigned long hvalue = hash_nickname(name, size);

	location = &(list[hvalue]);

	/* at this point, we found the array spot, now search
	 * as regular linked list, or as ircd likes to say...
	 * "We found the bucket, now search the chain"
	 */
	for (tmp = (List *) location->list; tmp; prev = tmp, tmp = tmp->next) 
	{
		if (!my_stricmp(name, tmp->name)) 
		{
			if (remove != REMOVE_FROM_LIST)
				move_gen_link_to_top(tmp, prev, location);
			else 
			{
				location->links--;
				remove_gen_link_from_list(tmp, prev, location);
			}
			return tmp;
		}
	}
	return NULL;
}

/*
 * add_nicklist_to_channellist: This function will add the nicklist
 * into the channellist, ensuring that we hash the nicklist, and
 * insert the struct correctly into the channelist's Nicklist hash
 * array
 */
void BX_add_nicklist_to_channellist(NickList *nptr, ChannelList *cptr)
{
	unsigned long hvalue = hash_nickname(nptr->nick, NICKLIST_HASHSIZE);

	/* take this nicklist, and attach it as the HEAD pointer
	 * in our chain at the hashed location in our array...
	 * Note, by doing this, this ensures that the "most active"
	 * users always remain at the top of the chain... ie, faster
	 * lookups for active users, (and as a side note, makes
	 * doing the add quite simple!)
	 */
	nptr->next = (NickList *) cptr->NickListTable[hvalue].list;

	/* assign our new linked list into array spot */
	cptr->NickListTable[hvalue].list = (void *) nptr;
	/* quick tally of nicks in chain in this array spot */
	cptr->NickListTable[hvalue].links++;
	/* keep stats on hits to this array spot */
	cptr->NickListTable[hvalue].hits++;
}

NickList *BX_find_nicklist_in_channellist(char *nick, ChannelList *cptr, int remove)
{
	HashEntry *location;
	register NickList *tmp, *prev = NULL;
	unsigned long hvalue = hash_nickname(nick, NICKLIST_HASHSIZE);

	if (!cptr)
		return NULL;
	location = &(cptr->NickListTable[hvalue]);

	/* at this point, we found the array spot, now search
	 * as regular linked list, or as ircd likes to say...
	 * "We found the bucket, now search the chain"
	 */
	for (tmp = (NickList *) location->list; tmp; prev = tmp, tmp = tmp->next) 
	{
		if (!my_stricmp(nick, tmp->nick)) 
		{
			if (remove != REMOVE_FROM_LIST)
				move_link_to_top(tmp, prev, location);
			else 
			{
				location->links--;
				remove_link_from_list(tmp, prev, location);
			}
			return tmp;
		}
	}
	return NULL;
}

/*
 * Basically this makes the hash table "look" like a straight linked list
 * This should be used for things that require you to cycle through the
 * full list, ex. for finding ALL matching stuff.
 * : usage should be like :
 *
 *	for (nptr = next_nicklist(cptr, NULL); nptr; nptr = 
 *	     next_nicklist(cptr, nptr))
 *		YourCodeOnTheNickListStruct
 */
NickList *BX_next_nicklist(ChannelList *cptr, NickList *nptr)
{
	unsigned long hvalue = 0;
	if (!cptr) 
		/* No channel! */
		return NULL;
	else if (!nptr) 
	{
		/* wants to start the walk! */
		while ((NickList *) cptr->NickListTable[hvalue].list == NULL) 
		{
			hvalue++;
			if (hvalue >= NICKLIST_HASHSIZE)
				return NULL;
		}
		return (NickList *) cptr->NickListTable[hvalue].list;
	}
	else if (nptr->next) 
	{
		/* still returning a chain! */
		return nptr->next;
	}
	else if (!nptr->next) 
	{
		int hvalue;
		/* hit end of chain, go to next bucket */
		hvalue = hash_nickname(nptr->nick, NICKLIST_HASHSIZE) + 1;
		if (hvalue >= NICKLIST_HASHSIZE) 
		{
			/* end of list */
			return NULL;
		}
		else 
		{
			while ((NickList *) cptr->NickListTable[hvalue].list == NULL) 
			{
				hvalue++;
				if (hvalue >= NICKLIST_HASHSIZE)
					return NULL;
			}
			/* return head of next filled bucket */
                        return (NickList *) cptr->NickListTable[hvalue].list;
		}
	}
	else 
		/* shouldn't ever be here */
		say ("HASH_ERROR: next_nicklist");
	return NULL;
}

List *BX_next_namelist(HashEntry *cptr, List *nptr, unsigned int size)
{
	unsigned long hvalue = 0;
	if (!cptr) 
		/* No channel! */
		return NULL;
	else if (!nptr) 
	{
		/* wants to start the walk! */
		while ((List *) cptr[hvalue].list == NULL) 
		{
			hvalue++;
			if (hvalue >= size)
				return NULL;
		}
		return (List *) cptr[hvalue].list;
	}
	else if (nptr->next) 
	{
		/* still returning a chain! */
		return nptr->next;
	}
	else if (!nptr->next) 
	{
		int hvalue;
		/* hit end of chain, go to next bucket */
		hvalue = hash_nickname(nptr->name, size) + 1;
		if (hvalue >= size) 
		{
			/* end of list */
			return NULL;
		}
		else 
		{
			while ((List *) cptr[hvalue].list == NULL) 
			{
				hvalue++;
				if (hvalue >= size)
					return NULL;
			}
			/* return head of next filled bucket */
                        return (List *) cptr[hvalue].list;
		}
	}
	else 
		/* shouldn't ever be here */
		say ("HASH_ERROR: next_namelist");
	return NULL;
}

void clear_nicklist_hashtable(ChannelList *cptr)
{
	if (cptr) 
	{
		memset((char *) cptr->NickListTable, 0, 
		   sizeof(HashEntry) * NICKLIST_HASHSIZE);
	}
}


void show_nicklist_hashtable(ChannelList *cptr)
{
        int count, count2;
        NickList *ptr;

        for (count = 0; count < NICKLIST_HASHSIZE; count++) 
        {
		if (cptr->NickListTable[count].links == 0)
			continue;
                say("HASH DEBUG: %d   links %d   hits %d",
                    count,
                    cptr->NickListTable[count].links,
                    cptr->NickListTable[count].hits);

                for (ptr = (NickList *) cptr->NickListTable[count].list,
                    count2 = 0; ptr; count2++, ptr = ptr->next) 
                {
                        say("HASH_DEBUG: %d:%d  %s!%s", count, count2,
                            ptr->nick, ptr->host);
                }
        }
}

void show_whowas_debug_hashtable(WhowasWrapList *cptr)
{
        int count, count2;
        WhowasList *ptr;

        for (count = 0; count < WHOWASLIST_HASHSIZE; count++) 
        {
		if (cptr->NickListTable[count].links == 0)
			continue;
                say("HASH DEBUG: %d   links %d   hits %d",
                    count,
                    cptr->NickListTable[count].links,
                    cptr->NickListTable[count].hits);

                for (ptr = (WhowasList *) cptr->NickListTable[count].list,
                    count2 = 0; ptr; count2++, ptr = ptr->next) 
                {
                        say("HASH_DEBUG: %d:%d  %10s %s!%s", count, count2,
                            ptr->channel, ptr->nicklist->nick, ptr->nicklist->host);
                }
        }
}

BUILT_IN_COMMAND(show_hash)
{
char *c;
ChannelList *chan = NULL, *chan2;
extern int from_server;
extern WhowasWrapList whowas_userlist_list;
extern WhowasWrapList whowas_reg_list;
extern WhowasWrapList whowas_splitin_list;
	if (args && *args)
		c = next_arg(args, &args);
	else
		c = get_current_channel_by_refnum(0);
	if (c && from_server > -1)
	{
		chan2 = get_server_channels(from_server);
		chan = (ChannelList *)find_in_list((List **)&chan2, c, 0);
	}
	if (chan)
		show_nicklist_hashtable(chan);
	show_whowas_debug_hashtable(&whowas_userlist_list);
	show_whowas_debug_hashtable(&whowas_reg_list);
	show_whowas_debug_hashtable(&whowas_splitin_list);
}

/*
 * the following routines are written by Colten Edwards (panasync)
 * to hash the whowas lists that the client keeps.
 */

static unsigned long hash_userhost_channel(char *userhost, char *channel, unsigned int size)
{
	register const unsigned char *p = (const unsigned char *)userhost;
	unsigned long g, hash = 0;
	if (!userhost) return -1;
	while (*p)
	{
		hash = (hash << 4) + ((*p >= 'A' && *p <= 'Z') ? (*p+32) : *p);
		if ((g = hash & 0xF0000000))
			hash ^= g >> 24;
		hash &= ~g;
		p++;
	}
	p = (const unsigned char *)channel;
	if (p)
	{
		while (*p)
		{
			if (*p == ',')
				return -1;
			hash = (hash << 4) + ((*p >= 'A' && *p <= 'Z') ? (*p+32) : *p);
			if ((g = hash & 0xF0000000))
				hash ^= g >> 24;
			hash &= ~g;
			p++;
		}
	}
	return (hash % size);
}

/*
 * move_link_to_top: used by find routine, brings link
 * to the top of the list in the specific array location
 */
static inline void move_link_to_top_whowas(WhowasList *tmp, WhowasList *prev, HashEntry *location)
{
	if (prev) 
	{
		WhowasList *old_list;
		old_list = (WhowasList *) location->list;
		location->list = (void *) tmp;
		prev->next = tmp->next;
		tmp->next = old_list;
	}
}

/*
 * remove_link_from_list: used by find routine, removes link
 * from our chain of hashed entries.
 */
static inline void remove_link_from_whowaslist(WhowasList *tmp, WhowasList *prev, HashEntry *location)
{
	if (prev) 
	{
		/* remove the link from the middle of the list */
		prev->next = tmp->next;
	}
	else {
		/* unlink the first link, and connect next one up */
		location->list = (void *) tmp->next;
	}
	/* set tmp's next to NULL, as its unlinked now */
	tmp->next = NULL;
}

/*
 * add_nicklist_to_channellist: This function will add the nicklist
 * into the channellist, ensuring that we hash the nicklist, and
 * insert the struct correctly into the channelist's Nicklist hash
 * array
 */
void BX_add_whowas_userhost_channel(WhowasList *wptr, WhowasWrapList *list)
{
	unsigned long hvalue = hash_userhost_channel(wptr->nicklist->host, wptr->channel, WHOWASLIST_HASHSIZE);

	/* take this nicklist, and attach it as the HEAD pointer
	 * in our chain at the hashed location in our array...
	 * Note, by doing this, this ensures that the "most active"
	 * users always remain at the top of the chain... ie, faster
	 * lookups for active users, (and as a side note, makes
	 * doing the add quite simple!)
	 */
	wptr->next = (WhowasList *) list->NickListTable[hvalue].list;

	/* assign our new linked list into array spot */
	list->NickListTable[hvalue].list = (void *) wptr;
	/* quick tally of nicks in chain in this array spot */
	list->NickListTable[hvalue].links++;
	/* keep stats on hits to this array spot */
	list->NickListTable[hvalue].hits++;
	list->total_links++;
}

WhowasList *BX_find_userhost_channel(char *host, char *channel, int remove, WhowasWrapList *wptr)
{
	HashEntry *location;
	register WhowasList *tmp, *prev = NULL;
	unsigned long hvalue;

	hvalue = hash_userhost_channel(host, channel, WHOWASLIST_HASHSIZE);
	location = &(wptr->NickListTable[hvalue]);

	/* at this point, we found the array spot, now search
	 * as regular linked list, or as ircd likes to say...
	 * "We found the bucket, now search the chain"
	 */
	for (tmp = (WhowasList *) (&(wptr->NickListTable[hvalue]))->list; tmp; prev = tmp, tmp = tmp->next) 
	{
		if (!tmp->nicklist->host || !tmp->channel || !host || !channel)
			continue;
		if (!my_stricmp(host, tmp->nicklist->host) && !my_stricmp(channel, tmp->channel))
		{
			if (remove != REMOVE_FROM_LIST)
				move_link_to_top_whowas(tmp, prev, location);
			else 
			{
				location->links--;
				remove_link_from_whowaslist(tmp, prev, location);
				wptr->total_unlinks++;
			}
			wptr->total_hits++;
			return tmp;
		}
	}
	return NULL;
}

/*
 * Basically this makes the hash table "look" like a straight linked list
 * This should be used for things that require you to cycle through the
 * full list, ex. for finding ALL matching stuff.
 * : usage should be like :
 *
 *	for (nptr = next_userhost(cptr, NULL); nptr; nptr = 
 *	     next_userhost(cptr, nptr))
 *		YourCodeOnTheWhowasListStruct
 */
WhowasList *BX_next_userhost(WhowasWrapList *cptr, WhowasList *nptr)
{
	unsigned long hvalue = 0;
	if (!cptr) 
		/* No channel! */
		return NULL;
	else if (!nptr) 
	{
		/* wants to start the walk! */
		while ((WhowasList *) cptr->NickListTable[hvalue].list == NULL) 
		{
			hvalue++;
			if (hvalue >= WHOWASLIST_HASHSIZE)
				return NULL;
		}
		return (WhowasList *) cptr->NickListTable[hvalue].list;
	}
	else if (nptr->next) 
	{
		/* still returning a chain! */
		return nptr->next;
	}
	else if (!nptr->next) 
	{
		int hvalue;
		/* hit end of chain, go to next bucket */
		hvalue = hash_userhost_channel(nptr->nicklist->host, nptr->channel, WHOWASLIST_HASHSIZE) + 1;
		if (hvalue >= WHOWASLIST_HASHSIZE) 
		{
			/* end of list */
			return NULL;
		}
		else 
		{
			while ((WhowasList *) cptr->NickListTable[hvalue].list == NULL) 
			{
				hvalue++;
				if (hvalue >= WHOWASLIST_HASHSIZE)
					return NULL;
			}
			/* return head of next filled bucket */
                        return (WhowasList *) cptr->NickListTable[hvalue].list;
		}
	}
	else 
		/* shouldn't ever be here */
		say ("WHOWAS_HASH_ERROR: next_userhost");
	return NULL;
}

void show_whowas_hashtable(WhowasWrapList *cptr, char *list)
{
        int count, count2 = 1;
        WhowasList *ptr;
	
	say("WhoWas %s Cache Stats: %lu hits  %lu  links  %lu unlinks", list, cptr->total_hits, cptr->total_links, cptr->total_unlinks);
        for (count = 0; count < WHOWASLIST_HASHSIZE; count++) 
        {
		
		if (cptr->NickListTable[count].links == 0)
			continue;
                for (ptr = (WhowasList *) cptr->NickListTable[count].list; ptr; count2++, ptr = ptr->next) 
			put_it("%s", convert_output_format("%K[%W$[3]0%K] %Y$[10]1 %W$2%G!%c$3", "%d %s %s %s", count2, ptr->channel, ptr->nicklist->nick, ptr->nicklist->host));
        }
}

int show_wholeft_hashtable(WhowasWrapList *cptr, time_t ltime, int *total, int *hook, char *list)
{
        int count, count2;
        WhowasList *ptr;

        for (count = 0; count < WHOWASLIST_HASHSIZE; count++) 
        {
		
		if (cptr->NickListTable[count].links == 0)
			continue;
                for (ptr = (WhowasList *) cptr->NickListTable[count].list, count2 = 1; ptr; count2++, ptr = ptr->next) 
		{
			if (ptr->server1/* && ptr->server2*/)
			{
				if (!(*total)++ && (*hook = do_hook(WHOLEFT_HEADER_LIST, "%s %s %s %s %s %s", "Nick", "Host", "Channel", "Time", "Server", "Server")))
					put_it("%s", convert_output_format(fget_string_var(FORMAT_WHOLEFT_HEADER_FSET), NULL));
				if (do_hook(WHOLEFT_LIST, "%s %s %s %ld %s %s", ptr->nicklist->nick, ptr->nicklist->host, ptr->channel, ltime-ptr->time, ptr->server1?ptr->server1:"Unknown", ptr->server2?ptr->server2:"Unknown"))
					put_it("%s", convert_output_format(fget_string_var(FORMAT_WHOLEFT_USER_FSET), "%s %s %s %l %s", ptr->nicklist->nick, ptr->nicklist->host, ptr->channel, (long)ltime-ptr->time, ptr->server1?ptr->server1:empty_string));
			}
		}
        }
	if (*total)
		do_hook(WHOLEFT_FOOTER_LIST, "%s", "End of WhoLeft");
	return *hook;
}

int BX_remove_oldest_whowas_hashlist(WhowasWrapList *list, time_t timet, int count)
{
WhowasList *ptr;
int total = 0;
register unsigned long x;
	if (!count)
	{
		for (x = 0; x < WHOWASLIST_HASHSIZE; x++)
		{
			ptr = (WhowasList *) (&(list->NickListTable[x]))->list;
			if (!ptr || !ptr->nicklist)
				continue;
			while (ptr)
			{
				if ((ptr->time + timet) <= now)
				{
					if (!(ptr = find_userhost_channel(ptr->nicklist->host, ptr->channel, 1, list)))
						break;
					new_free(&(ptr->nicklist->ip));
					new_free(&(ptr->nicklist->nick));
					new_free(&(ptr->nicklist->host));
					new_free(&(ptr->nicklist->server));
					new_free((char **)&(ptr->nicklist));
					new_free(&(ptr->channel));
					new_free(&(ptr->server1));
					new_free(&(ptr->server2));
					new_free((char **)&ptr);
					total++;
					ptr = (WhowasList *) (&(list->NickListTable[x]))->list;
				} else ptr = ptr->next;
			}
		}
	}
	else
	{
		while((ptr = next_userhost(list, NULL)) && count)
		{
			x = hash_userhost_channel(ptr->nicklist->host, ptr->channel, WHOWASLIST_HASHSIZE);
			if (!(ptr = find_userhost_channel(ptr->nicklist->host, ptr->channel, 1, list)))
				break;
			if (ptr->nicklist)
			{
				new_free(&(ptr->nicklist->ip));
				new_free(&(ptr->nicklist->nick));
				new_free(&(ptr->nicklist->host));
				new_free(&(ptr->nicklist->server));
				new_free((char **)&(ptr->nicklist));
			}
			new_free(&(ptr->channel));
			new_free(&(ptr->server1));
			new_free(&(ptr->server2));
			new_free((char **)&ptr);
			total++; count--;			
		}
	}
	return total;
}

int cmp_host (List *a, List *b)
{
NickList *a1 = (NickList *)a, *b1 = (NickList *)b;
	return strcmp(a1->host, b1->host);
}

int cmp_time (List *a, List *b)
{
NickList *a1 = (NickList *)a, *b1 = (NickList *)b;
	if (a1->idle_time > b1->idle_time)
		return -1;
	if (a1->idle_time < b1->idle_time)
		return 1;
	return strcmp(a1->nick, b1->nick);
}


int cmp_ip (List *a, List *b)
{
NickList *a1 = (NickList *)a, *b1 = (NickList *)b;
unsigned long at, bt;
	if (!a1->ip && !b1->ip)
		return -1;
/*		return strcmp(a1->nick, b1->nick);*/
	if (!a1->ip)
		return -1;
	if (!b1->ip)
		return 1;
	at = inet_addr(a1->ip); bt = inet_addr(b1->ip);
	if (at < bt)
		return 1;
	if (at > bt)
		return -1;
	return strcmp(a1->nick, b1->nick);
}

int cmp_op (List *a, List *b)
{
NickList *a1 = (NickList *)a, *b1 = (NickList *)b;
int a_isop, b_isop;
	a_isop = nick_isop(a1);
	b_isop = nick_isop(b1);
	if ((!a_isop && !b_isop))
		return strcmp(a1->nick, b1->nick);
	if (!a_isop)
		return 1;
	if (!b_isop)
		return -1;
	return 0;
}

int cmp_voice (List *a, List *b)
{
NickList *a1 = (NickList *)a, *b1 = (NickList *)b;
int a_isvoice, b_isvoice;
	a_isvoice = nick_isvoice(a1);
	b_isvoice = nick_isvoice(b1);
	if ((!a_isvoice && !b_isvoice) || (a_isvoice && b_isvoice))
		return strcmp(a1->nick, b1->nick);
	if (!a_isvoice)
		return -1;
	if (!b_isvoice)
		return 1;
	return 0;
}

NickList *BX_sorted_nicklist(ChannelList *chan, int sort)
{
	NickList *tmp, *l = NULL, *list = NULL, *last = NULL;
	for (tmp = next_nicklist(chan, NULL); tmp; tmp = next_nicklist(chan, tmp))
	{
		l = (NickList *)new_malloc(sizeof(NickList));
		memcpy(l, tmp, sizeof(NickList));
		l->next = NULL;
		switch(sort)
		{
			case NICKSORT_HOST:
				add_to_list_ext((List **)&list, (List *)l, cmp_host);
				break;
			case NICKSORT_OP:
				add_to_list_ext((List **)&list, (List *)l, cmp_op);
				break;
			case NICKSORT_VOICE:
				add_to_list_ext((List **)&list, (List *)l, cmp_voice);
				break;
			case NICKSORT_TIME:
				add_to_list_ext((List **)&list, (List *)l, cmp_time);
				break;
			case NICKSORT_IP:
				add_to_list_ext((List **)&list, (List *)l, cmp_ip);
				break;
			case NICKSORT_NONE:
				if (last)
					last->next = l;
				else
					list = l;
				break;
			default:
			case NICKSORT_NORMAL:
				add_to_list((List **)&list, (List *)l);
				break;
		}
		last = l;
	}
	return list;
}

void BX_clear_sorted_nicklist(NickList **list)
{
	register NickList *t;
	while(*list)
	{
		t = (*list)->next;
		new_free((char **)&(*list));
		*list = t;
	}
}

Flooding *BX_add_name_to_floodlist(char *name, char *host, char *channel, HashEntry *list, unsigned int size)
{
	Flooding *nptr;
	unsigned long hvalue = hash_nickname(name, size);
	nptr = (Flooding *)new_malloc(sizeof(Flooding));
	nptr->next = (Flooding *) list[hvalue].list;
	nptr->name = m_strdup(name);
	nptr->host = m_strdup(host);
	list[hvalue].list = (void *) nptr;
	/* quick tally of nicks in chain in this array spot */
	list[hvalue].links++;
	/* keep stats on hits to this array spot */
	list[hvalue].hits++;
	return nptr;
}

Flooding *BX_find_name_in_floodlist(char *name, char *host, HashEntry *list, unsigned int size, int remove)
{
	HashEntry *location;
	register Flooding *tmp, *prev = NULL;
	unsigned long hvalue = hash_nickname(name, size);

	location = &(list[hvalue]);

	/* at this point, we found the array spot, now search
	 * as regular linked list, or as ircd likes to say...
	 * "We found the bucket, now search the chain"
	 */
	for (tmp = (Flooding *) location->list; tmp; prev = tmp, tmp = tmp->next) 
	{
		if (!my_stricmp(name, tmp->name)) 
		{
			if (remove != REMOVE_FROM_LIST)
				move_gen_link_to_top((List *)tmp, (List *)prev, location);
			else 
			{
				location->links--;
				remove_gen_link_from_list((List *)tmp, (List *)prev, location);
			}
			return tmp;
		}
	}
	return NULL;
}


--- NEW FILE: term.c ---
/*
 * term.c -- termios and termcap handlers
 *
 * Written By Matthew Green, based on window.c by Michael Sandrof
 * Copyright(c) 1993 Matthew Green
 * Significant modifications by Jeremy Nelson
 * Copyright 1997 EPIC Software Labs,
 * Significant additions by J. Kean Johnston
 * Copyright 1998 J. Kean Johnston, used with permission
 * Modifications Copyright Colten Edwards 1997-1998.
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
 */

#include "irc.h"
static char cvsrevision[] = "$Id: term.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(term_c)
#include "struct.h"
#include "screen.h"
#include "ircaux.h"
[...2570 lines suppressed...]
						if (par[0] < par[1] && par[1] < li)
						{
							top = par[0]-1;
							bottom = par[1];
                                                	term_CS_scroll(top, bottom, 1);
						}
						continue;
					}
				}
			        continue;
			default:
				vc_state = ESnormal;

        	}
	}
	return num;
}

#endif /* NOT IN WTERM_C */
#endif

--- NEW FILE: network.c ---
/*
 * network.c -- handles stuff dealing with connecting and name resolving
 *
 * Written by Jeremy Nelson in 1995
 * See the COPYRIGHT file or do /help ircii copyright
 */
#define SET_SOURCE_SOCKET

#include "irc.h"
static char cvsrevision[] = "$Id: network.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(network_c)
#include "struct.h"
#include "ircterm.h"

#include "ircaux.h"
#include "output.h"
#include "vars.h"

#include "struct.h"

#define MAIN_SOURCE
#include "modval.h"

#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif

#ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif
#ifdef PARANOID
/* NaiL^d0d: no hijack please, we need random bytes, in stdlib.h */
#include <stdlib.h>
#endif

extern char hostname[NAME_LEN+1];
extern int  use_socks;
char *socks_user = NULL;

#if !defined(WTERM_C) && !defined(STERM_C)

/*
 * Stuff pertaining to bouncing through socks proxies.
 *
 * written by Joshua J. Drake
 * on Nov. 4, 1998
 * last modified, nov. 14
 */
#define SOCKS_PORT	1080
#define SOCKS4_VERSION	4
#define SOCKS5_VERSION	5

/* auth types */
#define AUTH_NONE       0x00
#define AUTH_GSSAPI     0x01
#define AUTH_PASSWD     0x02
#define AUTH_CHAP       0x03

/* auth errors */
#define AUTH_OK        0
#define AUTH_FAIL      -1

/* commands */
#define SOCKS_CONNECT   1
#define SOCKS_BIND      2
#define SOCKS_UDP       3
#define SOCKS_PING      0x80
#define SOCKS_TRACER    0x81
#define SOCKS_ANY       0xff

/* errors */
#define SOCKS5_NOERR            0x00
#define SOCKS5_RESULT           0x00
#define SOCKS5_FAIL             0x01
#define SOCKS5_AUTHORIZE        0x02
#define SOCKS5_NETUNREACH       0x03
#define SOCKS5_HOSTUNREACH      0x04
#define SOCKS5_CONNREF          0x05
#define SOCKS5_TTLEXP           0x06
#define SOCKS5_BADCMND          0x07
#define SOCKS5_BADADDR          0x08

/* flags */
#define SOCKS5_FLAG_NONAME      0x01
#define SOCKS5_FLAG_VERBOSE     0x02
#define SOCKS5_IPV4ADDR         0x01
#define SOCKS5_HOSTNAME         0x03
#define SOCKS5_IPV6ADDR         0x04

char *socks4_error(char cd)
{
	switch (cd)
	{
		case 91:
			return "rejected or failed";
			break;
		case 92:
			return "no identd";
			break;
		case 93:
			return "identd response != username";
			break;
		default:
			return "Unknown error";
	}
}

char *socks5_error(char cd)
{
	switch (cd)
	{
		case SOCKS5_FAIL:
			return "Rejected or failed";
			break;
		case SOCKS5_AUTHORIZE:
			return "Connection not allowed by ruleset";
			break;
		case SOCKS5_NETUNREACH:
			return "Network unreachable";
			break;
		case SOCKS5_HOSTUNREACH:
			return "Host unreachable";
			break;
		case SOCKS5_CONNREF:
			return "Connection refused";
			break;
		case SOCKS5_TTLEXP:
			return "Time to live expired";
			break;
		case SOCKS5_BADCMND:
			return "Bad command";
			break;
		case SOCKS5_BADADDR:
			return "Bad address";
			break;
		default:
			return "Unknown error";
	}
}

/*
 * try to negotiate a SOCKS4 connection.
 *
 */
int socks4_connect(int s, int portnum, struct sockaddr_in *server)
{
struct _sock_connect {
	char version;
	char type;
	unsigned short port;
	unsigned long address;
	char username[NAME_LEN+1];
} sock4_connect;
char socksreq[10];
char *p;
int red;

	memset(&sock4_connect, 0, sizeof(sock4_connect));
	sock4_connect.version = SOCKS4_VERSION;
	sock4_connect.type = SOCKS_CONNECT;
	sock4_connect.port = server->sin_port;

	strncpy(sock4_connect.username, socks_user? socks_user: getenv("USER") ? getenv("USER") : username, NAME_LEN);
	p = inet_ntoa(server->sin_addr);
	sock4_connect.address = inet_addr(p);
	if ((red = write(s, &sock4_connect, 8 + strlen(sock4_connect.username) + 1)) == -1)
	{
		bitchsay("Cannot write to socks proxy: %s", strerror(errno));
		return 0;
	}
	alarm(get_int_var(CONNECT_TIMEOUT_VAR));
	if ((red = read(s, socksreq, 8)) == -1)
	{
		alarm(0);
		bitchsay("Cannot read from socks proxy: %s", strerror(errno));
		return 0;
	}
	alarm(0);
	if (socksreq[1] != 90)
	{
		bitchsay("Cannot connect to SOCKS4 proxy: %s", socks4_error(socksreq[1]));
		return 0;
	}
	return 1;
}

/*
 * try to negotiate a SOCKS5 connection. (with the socket/username, to the server)
 */
int socks5_connect(int s, int portnum, struct sockaddr_in *server)
{
struct _sock_connect {
	char version;
	char type;
	char authtype;
	char addrtype;
	unsigned long address;
	unsigned short port;
	char username[NAME_LEN+1];
} sock5_connect;
char tmpbuf[25], *p;
struct in_addr tmpAddr;
unsigned short tmpI;
int red;

	/* propose any authentication */
	memset(&sock5_connect, 0, sizeof(sock5_connect));
	sock5_connect.version = SOCKS5_VERSION;
	sock5_connect.type = SOCKS_CONNECT;
	sock5_connect.authtype = AUTH_NONE; /* AUTH_PASSWD, AUTH_GSSAPI, AUTH_CHAP */

	if ((red = write(s, &sock5_connect, 4)) == -1)
	{
		bitchsay("Cannot write to proxy: %s", strerror(errno));
		return 0;
	}

	memset(tmpbuf, 0, sizeof(tmpbuf));
	alarm(get_int_var(CONNECT_TIMEOUT_VAR));
	if ((red = read(s, tmpbuf, sizeof(tmpbuf)-1)) == -1)
	{
		alarm(0);
		bitchsay("Cannot use SOCKS5 proxy, read failed during auth: %s", strerror(errno));
		return 0;
	}
	alarm(0);
	/* report server desired authentication (if not none) */
	if (tmpbuf[1] != AUTH_NONE)
	{
		bitchsay("Cannot use SOCKS5 proxy, server wants type %x authentication.", tmpbuf[1]);
		return 0;
	}

	/* try to bounce to target */
	memset(&sock5_connect, 0, sizeof(sock5_connect));
	sock5_connect.version = SOCKS5_VERSION;
	sock5_connect.type = SOCKS_CONNECT;
	sock5_connect.addrtype = SOCKS5_IPV4ADDR;
	p = inet_ntoa(server->sin_addr);
	sock5_connect.address = inet_addr(p);
	sock5_connect.port = server->sin_port;

	if ((red = write(s, &sock5_connect, 10)) == -1)
	{
		bitchsay("Cannot write to the proxy: %s", strerror(errno));
		return 0;
	}
	memset(tmpbuf, 0, sizeof(tmpbuf)-1);
	alarm(get_int_var(CONNECT_TIMEOUT_VAR));
	if ((red = read(s, tmpbuf, sizeof(tmpbuf)-1)) == -1)
	{
		alarm(0);
		bitchsay("Cannot use SOCKS5 proxy, read failed during bounce: %s. Attempting SOCKS4", strerror(errno));
		return 0;
	}
	alarm(0);
	if (tmpbuf[0] != SOCKS5_VERSION)
	{
		bitchsay("This is not a SOCKS5 proxy.");
		return 0;
	}
	if (tmpbuf[1] != SOCKS5_NOERR)
	{
		bitchsay("Cannot use SOCKS5 proxy, server failed: %s", socks5_error(tmpbuf[1]));
		return 0;
	}
	/*
	 * read the rest of the response (depending on what type of address they use)
	 */
	alarm(get_int_var(CONNECT_TIMEOUT_VAR));
	switch (tmpbuf[3])
	{
		case 1:
			read(s, tmpbuf, 4);
			memcpy(&tmpAddr.s_addr, tmpbuf, 4);
			read(s, tmpbuf, 2);
			tmpbuf[3] = '\0';
			tmpI = atoi(tmpbuf);
			bitchsay("SOCKS5 bounce successful, your address will be: %s:%d", inet_ntoa(tmpAddr), ntohs(tmpI));
			break;
		case 3:
		{
			char buffer[256];
			read(s, tmpbuf, 1);
			tmpbuf[1] = '\0';
			tmpI = atoi(tmpbuf);
			read(s, tmpbuf, tmpI);
			tmpbuf[tmpI] = '\0';
			strncpy(buffer, tmpbuf, sizeof(buffer));
			read(s, tmpbuf, 2);
			tmpbuf[3] = '\0';
			tmpI = atoi(tmpbuf);
			bitchsay("SOCKS5 bounce successful, your address will be: %s:%d", buffer, ntohs(tmpI));
			break;
		}
		case 4:
			read(s, tmpbuf, 18);
			/* don't report address of ipv6 addresses. */
			bitchsay("SOCKS5 bounce successful. [ipv6]");
			break;
		default:
			bitchsay("error tmpbuf[3]: %x", tmpbuf[3]);
			alarm(0);
			return 0;
	}
	alarm(0);
	return 1;
}

int handle_socks(int fd, struct sockaddr_in addr, char *host, int portnum)
{
	struct sockaddr_in proxy;
	struct hostent *hp;
                
	memset(&proxy, 0, sizeof(proxy));
	if (!(hp = gethostbyname(host)))
	{
		bitchsay("Unable to resolve SOCKS proxy host address: %s", host);
		return -1;
	}
	bcopy(hp->h_addr, (char *)&proxy.sin_addr, hp->h_length);
	proxy.sin_family = AF_INET;
	proxy.sin_port = htons(portnum);
	alarm(get_int_var(CONNECT_TIMEOUT_VAR));
	if (connect(fd, (struct sockaddr *)&proxy, sizeof(proxy)) < 0)
	{
		alarm(0);
		bitchsay("Unable to connect to SOCKS5 proxy: %s", strerror(errno));
		close(fd);
		return -1;
	}
	alarm(0);
	if (!socks5_connect(fd, portnum, &addr))
	{
		close(fd);
		if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		{
			bitchsay("Unable to get socket: %s", strerror(errno));
			return -1;
		}
		alarm(get_int_var(CONNECT_TIMEOUT_VAR));
		if (connect(fd, (struct sockaddr *)&proxy, sizeof(proxy)) < 0)
		{
			alarm(0);
			bitchsay("Unable to connect to SOCKS4 proxy: %s", strerror(errno));
			return -1;
		}
		alarm(0);
		if (!socks4_connect(fd, portnum, &addr))
		{
			close(fd);
			return -1;
		}
	}
	return fd;
}
#endif


/*
 * connect_by_number:  Wheeeee. Yet another monster function i get to fix
 * for the sake of it being inadequate for extension.
 *
 * we now take four arguments:
 *
 *	- hostname - name of the host (pathname) to connect to (if applicable)
 *	- portnum - port number to connect to or listen on (0 if you dont care)
 *	- service -	0 - set up a listening socket
 *			1 - set up a connecting socket
 *	- protocol - 	0 - use the TCP protocol
 *			1 - use the UDP protocol
 *
 *
 * Returns:
 *	Non-negative number -- new file descriptor ready for use
 *	-1 -- could not open a new file descriptor or 
 *		an illegal value for the protocol was specified
 *	-2 -- call to bind() failed
 *	-3 -- call to listen() failed.
 *	-4 -- call to connect() failed
 *	-5 -- call to getsockname() failed
 *	-6 -- the name of the host could not be resolved
 *	-7 -- illegal or unsupported request
 *	-8 -- no socks access
 *
 *
 * Credit: I couldnt have put this together without the help of BSD4.4-lite
 * User SupplimenTary Document #20 (Inter-process Communications tutorial)
 */
int BX_connect_by_number(char *hostn, unsigned short *portnum, int service, int protocol, int nonblocking)
{
	int fd = -1;
	int is_unix = (hostn && *hostn == '/');
	int sock_type, proto_type;

#ifndef __OPENNT
	sock_type = (is_unix) ? AF_UNIX : AF_INET;
#else
	sock_type = AF_INET;
#endif

	proto_type = (protocol == PROTOCOL_TCP) ? SOCK_STREAM : SOCK_DGRAM;

	if ((fd = socket(sock_type, proto_type, 0)) < 0)
		return -1;

	set_socket_options (fd);

	/* Unix domain server */
#ifdef HAVE_SYS_UN_H
	if (is_unix)
	{
		struct sockaddr_un name;

		memset(&name, 0, sizeof(struct sockaddr_un));
		name.sun_family = AF_UNIX;
		strcpy(name.sun_path, hostn);
#ifdef HAVE_SUN_LEN
# ifdef SUN_LEN
		name.sun_len = SUN_LEN(&name);
# else
		name.sun_len = strlen(hostn) + 1;
# endif
#endif

		if (is_unix && (service == SERVICE_SERVER))
		{
			if (bind(fd, (struct sockaddr *)&name, strlen(name.sun_path) + sizeof(name.sun_family)))
				return close(fd), -2;
			if (protocol == PROTOCOL_TCP)
				if (listen(fd, 4) < 0)
					return close(fd), -3;
		}

		/* Unix domain client */
		else if (service == SERVICE_CLIENT)
		{
			alarm(get_int_var(CONNECT_TIMEOUT_VAR));
			if (connect (fd, (struct sockaddr *)&name, strlen(name.sun_path) + 2) < 0)
			{
				alarm(0);
				return close(fd), -4;
			}
			alarm(0);
		}
	}
	else
#endif

	/* Inet domain server */
	if (!is_unix && (service == SERVICE_SERVER))
	{
		int length;
#ifdef IP_PORTRANGE
		int ports;
#endif
		struct sockaddr_foobar name;
#ifdef IPV6
		struct in6_addr any = { IN6ADDR_ANY_INIT };

		memset(&name, 0, sizeof(struct sockaddr_foobar));
		name.sf_family = AF_INET6;
		memcpy(&name.sf_addr6, &any, sizeof(struct in6_addr));
#else
		memset(&name, 0, sizeof(struct sockaddr_foobar));
		name.sf_family = AF_INET;
		name.sf_addr.s_addr = htonl(INADDR_ANY);
#endif

		name.sf_port = htons(*portnum);
#ifdef PARANOID
		name.sf_port += (unsigned short)(rand() & 255);
#endif
		
#ifdef IP_PORTRANGE
		if (getenv("EPIC_USE_HIGHPORTS"))
		{
			ports = IP_PORTRANGE_HIGH;
			setsockopt(fd, IPPROTO_IP, IP_PORTRANGE, 
					(char *)&ports, sizeof(ports));
		}
#endif

		if (bind(fd, (struct sockaddr *)&name, sizeof(name)))
			return close(fd), -2;

		length = sizeof (name);
		if (getsockname(fd, (struct sockaddr *)&name, &length))
			return close(fd), -5;

		*portnum = ntohs(name.sf_port);

		if (protocol == PROTOCOL_TCP)
			if (listen(fd, 4) < 0)
				return close(fd), -3;
#ifdef NON_BLOCKING_CONNECTS
		if (nonblocking && set_non_blocking(fd) < 0)
			return close(fd), -4;
#endif
	}

	/* Inet domain client */
	else if (!is_unix && (service == SERVICE_CLIENT))
	{
		struct sockaddr_foobar server;
		struct hostent *hp;
#ifdef WINNT
		char buf[BIG_BUFFER_SIZE+1];
#endif		
#ifdef IPV6
		struct addrinfo hints, *res;
		struct sockaddr_foobar *sf = NULL;
#else
		struct sockaddr_in localaddr;
		if (LocalHostName)
		{
			memset(&localaddr, 0, sizeof(struct sockaddr_in));
			localaddr.sin_family = AF_INET;
			localaddr.sin_addr = LocalHostAddr.sf_addr;
			localaddr.sin_port = 0;
			if (bind(fd, (struct sockaddr *)&localaddr, sizeof(localaddr)))
				return close(fd), -2;
		}
#endif

		memset(&server, 0, sizeof(struct sockaddr_in));
#ifndef WINNT

#ifdef IPV6
                memset(&hints, 0, sizeof(hints));
                if (!getaddrinfo(hostn, NULL, &hints, &res) && res)
                {
                        sf = (struct sockaddr_foobar*) res->ai_addr;

                        close(fd);

                        proto_type = (protocol == PROTOCOL_TCP) ? SOCK_STREAM : SOCK_DGRAM;
                        if ((fd = socket(sf->sf_family, proto_type, 0)) < 0)
                                return -1;
                        set_socket_options (fd);

                        if ((server.sf_family = sf->sf_family) == AF_INET)
                                memcpy(&server.sf_addr, &sf->sf_addr, sizeof(struct in_addr));
                        else
                                memcpy(&server.sf_addr6, &sf->sf_addr6, sizeof(struct in6_addr));
                        server.sf_port = htons(*portnum);

                        memset(&hints, 0, sizeof(struct addrinfo));
                        hints.ai_family = res->ai_family;
                        freeaddrinfo(res);
 
                        if (LocalHostName && !getaddrinfo(LocalHostName, NULL, &hints, &res) && res)
                        {
                                if (bind(fd, (struct sockaddr *) res->ai_addr, sizeof(struct sockaddr_foobar)))
                                        return close(fd), -2;
                                freeaddrinfo(res);
                        }
                }
                else
                        return close(fd), -6;

#else
		if (isdigit((unsigned char)hostn[strlen(hostn)-1]))
			inet_aton(hostn, (struct in_addr *)&server.sf_addr);
		else
		{
			if (!(hp = gethostbyname(hostn)))
	  			return close(fd), -6;
			memcpy(&server.sf_addr, hp->h_addr, hp->h_length);
		}
		server.sf_family = AF_INET;
		server.sf_port = htons(*portnum);
#endif /* IPV6 */
		
#else
		/* for some odd reason resolv() fails on NT... */
/*		server = (*(struct sockaddr_in *) hostn);*/
		if (!hostn)
		{
			gethostname(buf, sizeof(buf));
			hostn = buf;
		}
		if ((server.sf_addr.s_addr = inet_addr(hostn)) == -1)
		{
			if ((hp = gethostbyname(hostn)) != NULL)
			{
				memset(&server, 0, sizeof(server));
				bcopy(hp->h_addr, (char *) &server.sf_addr,
					hp->h_length);
				server.sf_family = hp->h_addrtype;
			}
			else
				return (-2);
		}
		else
			server.sf_family = AF_INET;
		server.sf_port = (unsigned short) htons(*portnum);
#endif /* WINNT */

#ifdef NON_BLOCKING_CONNECTS
		if (!use_socks && nonblocking && set_non_blocking(fd) < 0)
			return close(fd), -4;
#endif

#if !defined(WTERM_C) && !defined(STERM_C) && !defined(IPV6)

		if (use_socks && get_string_var(SOCKS_HOST_VAR))
		{

			fd = handle_socks(fd, *((struct sockaddr_in*) &server), get_string_var(SOCKS_HOST_VAR), get_int_var(SOCKS_PORT_VAR));
			if (fd == -1)
				return -4;
			else
				return fd;
		}
#endif
		alarm(get_int_var(CONNECT_TIMEOUT_VAR));
		if (connect (fd, (struct sockaddr *)&server, sizeof(server)) < 0)
		{
			alarm(0);
#ifdef NON_BLOCKING_CONNECTS
			if (!nonblocking)
#endif
				return close(fd), -4;
		}
		alarm(0);
	}

	/* error */
	else
		return close(fd), -7;

	return fd;
}

int	lame_resolv (const char *hostname, struct sockaddr_foobar *buffer)
{
#ifdef IPV6
	struct addrinfo *res;
	if (getaddrinfo(hostname, NULL, NULL, &res) && res)
		return -1;

	memmove(buffer, res->ai_addr, res->ai_addrlen);
	return 0;
#else
	struct hostent 	*hp;

	if (!(hp = gethostbyname(hostname)))
		return -1;

	buffer->sf_family = AF_INET;
	memmove(&buffer->sf_addr, hp->h_addr, hp->h_length);
	return 0;
#endif	
}

#ifdef IPV6

extern struct sockaddr_foobar *BX_lookup_host (const char *host)
{
	static struct sockaddr_foobar sf;
	struct addrinfo *res;
	
	if (!getaddrinfo(host, NULL, NULL, &res) && res)
	{
		memcpy(&sf, res->ai_addr, sizeof(struct sockaddr_foobar));
		freeaddrinfo(res);

		return &sf;
	}

	return NULL;
}

extern char *BX_host_to_ip (const char *host)
{
	struct addrinfo *res;
	struct sockaddr_foobar *sf;
	static char ip[128];
	
	if (!getaddrinfo(host, NULL, NULL, &res) && res)
	{
		sf = (struct sockaddr_foobar*) res->ai_addr;
		inet_ntop(sf->sf_family, (sf->sf_family == AF_INET) ?
		  (void*) &sf->sf_addr : (void*) &sf->sf_addr6, ip, 128);
		freeaddrinfo(res);

		return ip;
	}
	else
		return empty_string;
}

extern char *BX_ip_to_host (const char *ip)
{
	static char host[128];
	struct sockaddr_foobar sf;
	
	if (inet_pton(AF_INET6, ip, &sf.sf_addr6))
		sf.sf_family = AF_INET6;
	else
	{
		inet_pton(AF_INET, ip, &sf.sf_addr);
		sf.sf_family = AF_INET;
	}
	
	if (getnameinfo((struct sockaddr*)&sf, sizeof(struct sockaddr_foobar), host, 128, NULL, 0, 0))
		strncpy(host, ip, 128);

	return host;
}

extern char *BX_one_to_another (const char *what)
{
	if (isdigit(what[strlen(what)-1]) || strchr(what, ':'))
		return ip_to_host (what);
	else
		return host_to_ip (what);
}

#else

extern struct sockaddr_foobar *BX_lookup_host (const char *host)
{
	struct hostent *he;
	static struct sockaddr_foobar sf;

	alarm(1);
	he = gethostbyname(host);
	alarm(0);
	if (he)
	{
		sf.sf_family = AF_INET;
		memcpy(&sf.sf_addr, he->h_addr, sizeof(struct in_addr));
		return &sf;
	}
	else
		return NULL;
}

extern char *BX_host_to_ip (const char *host)
{
	struct hostent *hep = gethostbyname(host);
	static char ip[30];

	return (hep ? sprintf(ip,"%u.%u.%u.%u",	hep->h_addr[0] & 0xff,
						hep->h_addr[1] & 0xff,
						hep->h_addr[2] & 0xff,
						hep->h_addr[3] & 0xff),
						ip : empty_string);
}

extern char *BX_ip_to_host (const char *ip)
{
	struct in_addr ia;
	struct hostent *he;
	static char host[101];
	
	ia.s_addr = inet_addr(ip);
	he = gethostbyaddr((char*) &ia, sizeof(struct in_addr), AF_INET);

	return (he ? strncpy(host, he->h_name, 100): empty_string);
}

extern char *BX_one_to_another (const char *what)
{

	if (!isdigit((unsigned char)what[strlen(what)-1]))
		return host_to_ip (what);
	else
		return ip_to_host (what);
}

#endif

/*
 * It is possible for a race condition to exist; such that select()
 * indicates that a listen()ing socket is able to recieve a new connection
 * and that a later accept() call will still block because the connection
 * has been closed in the interim.  This wrapper for accept() attempts to
 * defeat this by making the accept() call nonblocking.
 */
int	my_accept (int s, struct sockaddr *addr, int *addrlen)
{
	int	retval;
	set_non_blocking(s);
	retval = accept(s, addr, addrlen);
	set_blocking(s);
	return retval;
}



int BX_set_non_blocking(int fd)
{
#ifdef NON_BLOCKING_CONNECTS
	int	res;

#if defined(NBLOCK_POSIX)
	int nonb = 0;
	nonb |= O_NONBLOCK;
#elif defined(NBLOCK_BSD)
	int nonb = 0;
	nonb |= O_NDELAY;
#elif defined(NBLOCK_SYSV)
	res = 1;

	if (ioctl (fd, FIONBIO, &res) < 0)
		return -1;
#else
#error no idea how to set an fd to non-blocking
#endif
#if (defined(NBLOCK_POSIX) || defined(NBLOCK_BSD)) && !defined(NBLOCK_SYSV)
	if ((res = fcntl(fd, F_GETFL, 0)) == -1)
		return -1;
	else if (fcntl(fd, F_SETFL, res | nonb) == -1)
		return -1;
#endif
#endif
	return 0;
}

int BX_set_blocking(int fd)
{
#ifdef NON_BLOCKING_CONNECTS
	int	res;

#if defined(NBLOCK_POSIX)
	int nonb = 0;
	nonb |= O_NONBLOCK;
#elif defined(NBLOCK_BSD)
	int nonb = 0;
	nonb |= O_NDELAY;
#elif defined(NBLOCK_SYSV)
	res = 0;

	if (ioctl (fd, FIONBIO, &res) < 0)
		return -1;
#else
#error no idea how to return an fd blocking
#endif
#if (defined(NBLOCK_POSIX) || defined(NBLOCK_BSD)) && !defined(NBLOCK_SYSV)
	if ((res = fcntl(fd, F_GETFL, 0)) == -1)
		return -1;
	else if (fcntl(fd, F_SETFL, res &~ nonb) == -1)
		return -1;
#endif
#endif
	return 0;
}



--- NEW FILE: queue.c ---
/*
 *  queue.c - The queue command
 *  
 *  Queues allow for future batch processing
 *
 *  Syntax:  /QUEUE -DO -SHOW -LIST -NO_FLUSH -DELETE -FLUSH <name> {commands}
 *
 *  Written by Jeremy Nelson (nelson at cs.uwp.edu) (ESL)
 *
 *  Copyright (C) 1993.  See the copyright file and all that rot
 */

#include "irc.h"
static char cvsrevision[] = "$Id: queue.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(queue_c)
#include "struct.h"

#include "alias.h"
#include "commands.h"
#include "ircaux.h"
#include "queue.h"
#include "output.h"
#define MAIN_SOURCE
#include "modval.h"

extern char  *next_expr (char **, char);

struct CmdListT;
struct  QueueT {
        struct QueueT   *next;
        struct CmdListT *first;
        char     *name;
};
struct  CmdListT {
        struct CmdListT *next;
        char     *what;
};
typedef struct QueueT Queue;
typedef struct CmdListT CmdList;

static	Queue   *lookup_queue (Queue *, char *);
static	int     add_commands_to_queue (Queue *, char *, char *);
static	void    display_all_queues (Queue *);
static	CmdList *walk_commands (Queue *);
static	Queue   *make_new_queue (Queue *, char *);
static	int     delete_commands_from_queue (Queue *, int);
static	void    flush_queue (Queue *);
static	void    print_queue (Queue *);
static	int     num_entries (Queue *);
static	Queue   *remove_a_queue (Queue *);
static	Queue   *do_queue (Queue *, int);

BUILT_IN_COMMAND(queuecmd)
{
        Queue   *tmp;
	char	*arg 		= NULL,
		*name 		= NULL,
        	*startcmds 	= NULL,
                *cmds           = NULL;
	int	noflush 	= 0, 		runit 			= 0, 
		list 		= 0,
		flush 		= 0, 		remove_by_number 	= 0,
		commands 	= 1,            number                  = 0;
        static  Queue   *Queuelist;
	
	/* 
	 * If the queue list is empty, make an entry
	 * Some OS's barf on writing to static strings...
	 * I wish ultrix wasnt so darn forgiving...
	 */
	char this_sucks[4];
	strcpy(this_sucks,"Top");
	if (Queuelist == NULL)
                Queuelist = make_new_queue(NULL, this_sucks);

        if ((startcmds = strchr(args, '{')) == NULL)
                commands = 0;
        else
                *(startcmds-1) = '\0';

	while ((arg = upper(next_arg(args, &args))) != NULL) 
        {
		if (*arg == '-' || *arg == '/')
		{
			*arg++ = '\0';
			if (!strcmp(arg, "NO_FLUSH"))
				noflush = 1;
			else if (!strcmp(arg, "SHOW")) 
                        {
				display_all_queues(Queuelist);
                                return;
                        }
			else if (!strcmp(arg, "LIST"))
				list = 1;
			else if (!strcmp(arg, "DO"))
				runit = 1;
			else if (!strcmp(arg, "DELETE"))
				remove_by_number = 1;
			else if (!strcmp(arg, "FLUSH"))
				flush = 1;
		}
		else
		{
			if (name)
				number = atoi(arg);
			else
				name = arg;
		}
	}

        if (name == NULL)
                return;

	/* Find the queue based upon the previous queue */
        tmp = lookup_queue(Queuelist, name);

	/* if the next queue is empty, then we need to see if 
	   we should make it or output an error */
        if ((tmp->next) == NULL) 
        {
                if (commands)
                	tmp->next = make_new_queue(NULL, name);
		else 
                {
                	yell ("QUEUE: (%s) no such queue",name);
                	return;
		}
        }

	if (remove_by_number == 1) 
		if (delete_commands_from_queue(tmp->next,number))
                        tmp->next = remove_a_queue(tmp->next);

	if (list == 1) 
		print_queue(tmp->next);
	if (runit == 1) 
		tmp->next = do_queue(tmp->next, noflush);
	if (flush == 1) 
		tmp->next = remove_a_queue(tmp->next);

	if (startcmds) 
        {
		int booya;

		if ((cmds = next_expr(&startcmds, '{')) == NULL) 
                {
			yell ("QUEUE: missing closing brace");
			return;
		}
		booya = add_commands_to_queue (tmp->next, cmds, subargs);
		say ("QUEUED: %s now has %d entries",name, booya);
	}
}

/*
 * returns the queue BEFORE the queue we are looking for
 * returns the last queue if no match
 */
static Queue *	lookup_queue (Queue *queue, char *what)
{
	Queue	*tmp = queue;

        upper(what);

	while (tmp->next)
	{
		if (!strcmp(tmp->next->name, what))
			return tmp;
		else
                        if (tmp->next)
			        tmp = tmp->next;
                        else
                                break;
	}
        return tmp;
}

/* returns the last CmdList in a queue, useful for appending commands */
static CmdList *walk_commands (Queue *queue)
{
	CmdList *ctmp = queue->first;
	
        if (ctmp) 
        {
	        while (ctmp->next)
	                ctmp = ctmp->next;
	        return ctmp;
        }
        return NULL;
}

/*----------------------------------------------------------------*/
/* Make a new queue, link it in, and return it. */
static Queue *make_new_queue (Queue *afterqueue, char *name)
{
        Queue *tmp = (Queue *)new_malloc(sizeof(Queue));

        upper(name);

        tmp->next = afterqueue;
        tmp->first = NULL;
        tmp->name = NULL;
        malloc_strcpy(&tmp->name, name);
        return tmp;
}
        
/* add a command to a queue, at the end of the list */
/* expands the whole thing once and stores it */
static int	add_commands_to_queue (Queue *queue, char *what, char *subargs)
{
	CmdList *ctmp = walk_commands(queue);
	char	*list = NULL,
                *sa;
	int 	args_flag = 0;
	
        sa = subargs ? subargs : space;
	list = expand_alias(what,sa,&args_flag, NULL);
        if (!ctmp) 
        {
                queue->first = (CmdList *)new_malloc(sizeof(CmdList));
		ctmp = queue->first;
	}
        else 
        {
	        ctmp->next = (CmdList *)new_malloc(sizeof(CmdList));
		ctmp = ctmp->next;
	}
 	ctmp->what = NULL;
	malloc_strcpy(&ctmp->what, list);
	ctmp->next = NULL;
	new_free(&list);
	return num_entries(queue);
}


/* remove the Xth command from the queue */
static int	delete_commands_from_queue (Queue *queue, int which)
{
        CmdList *ctmp = queue->first;
        CmdList *blah;
        int x;

        if (which == 1)
                queue->first = ctmp->next;
        else 
        {
                for (x=1;x<which-1;x++) 
                {
                        if (ctmp->next) 
                                ctmp = ctmp->next;
                        else 
                                return 0;
                }
                blah = ctmp->next;
                ctmp->next = ctmp->next->next;
                ctmp = blah;
        }
        new_free(&ctmp->what);
        new_free((char **)&ctmp);
        if (queue->first == NULL)
                return 1;
        else
                return 0;
}

/*-------------------------------------------------------------------*/
/* flush a queue, deallocate the memory, and return the next in line */
static Queue	*remove_a_queue (Queue *queue)
{
	Queue *tmp;
        tmp = queue->next;
	flush_queue(queue);
	new_free((char **)&queue);
	return tmp;
}

/* walk through a queue, deallocating the entries */
static void	flush_queue (Queue *queue)
{
	CmdList *tmp, *tmp2;
	tmp = queue->first;

	while (tmp != NULL)
	{
		tmp2 = tmp;
		tmp = tmp2->next;
                if (tmp2->what != NULL)
		        new_free(&tmp2->what);
                if (tmp2)
	        	new_free((char **)&tmp2);
	}
}

/*------------------------------------------------------------------------*/
/* run the queue, and if noflush, then return the queue, else return the
   next queue */
static Queue  	*do_queue (Queue *queue, int noflush)
{
	CmdList	*tmp;
	
        tmp = queue->first;
        
        do
	{
		if (tmp->what != NULL)
			parse_line("QUEUE", tmp->what, empty_string, 0, 0, 1);
		tmp = tmp->next;
	}
        while (tmp != NULL);

	if (!noflush) 
		return remove_a_queue(queue);
	else
                return queue;
}

/* ---------------------------------------------------------------------- */
/* output the contents of all the queues to the screen */
static void    display_all_queues (Queue *queue)
{
        Queue *tmp = queue->next;
        while (tmp) 
        {
                print_queue(tmp);
                if (tmp->next == NULL)
                        return;
                else
                        tmp = tmp->next;
        }
        yell("QUEUE: No more queues");
}

/* output the contents of a queue to the screen */
static void	print_queue (Queue *queue)
{
	CmdList *tmp;
	int 	x = 0;
	
        tmp = queue->first;
	while (tmp != NULL) 
        {
		if (tmp->what)
			say ("<%s:%2d> %s",queue->name,++x,tmp->what);
		tmp = tmp->next;
	}
        say ("<%s> End of queue",queue->name);
}

/* return the number of entries in a queue */
static int	num_entries (Queue *queue)
{
	int x = 1;
	CmdList *tmp;

        if ((tmp = queue->first) == NULL) 
                return 0;
	while (tmp->next) 
        {
                x++;
		tmp = tmp->next;
	}
	return x;
}

--- NEW FILE: alist.c ---
/*
 * alist.c -- resizeable arrays.
 * Written by Jeremy Nelson
 * Copyright 1997 EPIC Software Labs
 *
 * This file presumes a good deal of chicanery.  Specifically, it assumes
 * that your compiler will allocate disparate structures congruently as 
 * long as the members match as to their type and location.  This is
 * critically important for how this code works, and all hell will break
 * loose if your compiler doesnt do this.  Every compiler i know of does
 * it, which is why im assuming it, even though im not allowed to assume it.
 *
 * This file is hideous.  Ill kill each and every one of you who made
 * me do this. ;-)
 */

#define _cs_alist_hash_
#define _ci_alist_hash_
#include "irc.h"
static char cvsrevision[] = "$Id: alist.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(alist_c)
#include "alist.h"
#include "ircaux.h"
#include "output.h"
#define MAIN_SOURCE
#include "modval.h"

u_32int_t	bin_ints = 0;
u_32int_t	lin_ints = 0;
u_32int_t	bin_chars = 0;
u_32int_t	lin_chars = 0;
u_32int_t	alist_searches = 0;
u_32int_t	char_searches = 0;


#define ARRAY_ITEM(array, loc) ((Array_item *) ((array) -> list [ (loc) ]))
#define LARRAY_ITEM(array, loc) (((array) -> list [ (loc) ]))

/* Function decls */
static void check_array_size (Array *list);
void move_array_items (Array *list, int start, int end, int dir);

/*
 * Returns an entry that has been displaced, if any.
 */
Array_item *BX_add_to_array (Array *array, Array_item *item)
{
	int count;
	int location = 0;
	Array_item *ret = NULL;
	u_32int_t       mask;   /* Dummy var */

	if (array->hash == HASH_INSENSITIVE)
		item->hash = ci_alist_hash(item->name, &mask);
	else
		item->hash = cs_alist_hash(item->name, &mask);

	check_array_size(array);
	if (array->max)
	{
		find_array_item(array, item->name, &count, &location);
		if (count < 0)
		{
			ret = ARRAY_ITEM(array, location);
			array->max--;
		}
		else
			move_array_items(array, location, array->max, 1);
	}

	array->list[location] = item;
	array->max++;
	return ret;
}

/*
 * Returns the entry that has been removed, if any.
 */
Array_item *BX_remove_from_array (Array *array, char *name)
{
	int count, location = 0;

	if (array->max)
	{
		find_array_item(array, name, &count, &location);
		if (count >= 0)
			return NULL;

		return array_pop(array, location);
	}
	return NULL;	/* Cant delete whats not there */
}

/* Remove the 'which'th item from the given array */
Array_item *BX_array_pop (Array *array, int which)
{
	Array_item *ret = NULL;

	if (which < 0 || which >= array->max)
		return NULL;

	ret = ARRAY_ITEM(array, which);
	move_array_items(array, which + 1, array->max, -1);
	array->max--;
	return ret;
}
  
/*
 * Returns the entry that has been removed, if any.
 */
Array_item *BX_remove_all_from_array (Array *array, char *name)
{
	int count, location = 0;
	Array_item *ret = NULL;

	if (array->max)
	{
		find_array_item(array, name, &count, &location);
		if (count == 0)
			return NULL;
		ret = ARRAY_ITEM(array, location);
		move_array_items(array, location + 1, array->max, -1);
		array->max--;
		return ret;
	}
	return NULL;	/* Cant delete whats not there */
}

Array_item *BX_array_lookup (Array *array, char *name, int wild, int delete)
{
	int count, location;

	if (delete)
		return remove_from_array(array, name);
	else
		return find_array_item(array, name, &count, &location);
}

static void check_array_size (Array *array)
{
	if (array->total_max == 0)
		array->total_max = 6;		/* Good size to start with */
	else if (array->max == array->total_max-1)
		array->total_max *= 2;
	else if (array->max * 3 < array->total_max)
		array->total_max /= 2;
	else
		return;

/*yell("Resizing...");*/
	RESIZE(array->list, Array_item *, array->total_max);
}

/*
 * Move ``start'' through ``end'' array elements ``dir'' places up
 * in the array.  If ``dir'' is negative, move them down in the array.
 * Fill in the vacated spots with NULLs.
 */
void move_array_items (Array *array, int start, int end, int dir)
{
	int i;

	if (dir > 0)
	{
		for (i = end; i >= start; i--)
			LARRAY_ITEM(array, i + dir) = ARRAY_ITEM(array, i);
		for (i = dir; i > 0; i--)
			LARRAY_ITEM(array, start + i - 1) = NULL;
	}
	else if (dir < 0)
	{
		for (i = start; i <= end; i++)
			LARRAY_ITEM(array, i + dir) = ARRAY_ITEM(array, i);
		for (i = end - dir + 1; i <= end; i++)
			LARRAY_ITEM(array, i) = NULL;
	}
}


/*
 * This is just a generalization of the old function  ``find_command''
 *
 * You give it an alist_item and a name, and it gives you the number of
 * items that are completed by that name, and where you could find/put that
 * item in the list.  It returns the alist_item most appropriate.
 *
 * If ``cnt'' is less than -1, then there was one exact match and one or
 *	more ambiguous matches in addition.  The exact match's location 
 *	is put into ``loc'' and its entry is returned.  The ambigous matches
 *	are (of course) immediately subsequent to it.
 *
 * If ``cnt'' is -1, then there was one exact match.  Its location is
 *	put into ``loc'' and its entry is returned.
 *
 * If ``cnt'' is zero, then there were no matches for ``name'', but ``loc''
 * 	is set to the location in the array in which you could place the
 * 	specified name in the sorted list.
 *
 * If ``cnt'' is one, then there was one command that non-ambiguously 
 * 	completes ``name''
 *
 * If ``cnt'' is greater than one, then there was exactly ``cnt'' number
 *	of entries that completed ``name'', but they are all ambiguous.
 *	The entry that is lowest alphabetically is returned, and its
 *	location is put into ``loc''.
 */
Array_item *BX_find_array_item (Array *set, char *name, int *cnt, int *loc)
{
	size_t	len = strlen(name);
	int	c = 0, 
		pos = 0, 
		min, 
		max;
	u_32int_t mask, hash;

	if (set->hash == HASH_INSENSITIVE)
		hash = ci_alist_hash(name, &mask);
	else
		hash = cs_alist_hash(name, &mask);
	
	*cnt = 0;
	if (!set->list || !set->max)
	{
		*loc = 0;
		return NULL;
	}

	alist_searches++;
	max = set->max - 1;
	min = 0;
	
	while (max >= min)
	{
		bin_ints++;
		pos = (max - min) / 2 + min;
		c = (hash & mask) - (ARRAY_ITEM(set, pos)->hash & mask);
		if (c == 0)
			break;
		else if (c < 0)
			max = pos - 1;
		else
			min = pos + 1;
	}

	/*
	 * If we can't find a symbol that qualifies, then we can just drop
	 * out here.  This is good because a "pass" (lookup for a symbol that
	 * does not exist) requires only cheap integer comparisons.
	 */
	if (c != 0)
	{
		if (c > 0)
			*loc = pos + 1;
		else
			*loc = pos;
		return NULL;
	}

	/*
	 * Now we've found some symbol that has the same first four letters.
	 * Expand the min/max range to include all of the symbols that have
	 * the same first four letters...
	 */
	min = max = pos;
	while ((min > 0) && (hash & mask) == (ARRAY_ITEM(set, min)->hash & mask))
		min--, lin_ints++;
	while ((max < set->max - 1) && (hash &mask) == (ARRAY_ITEM(set, max)->hash & mask))
		max++, lin_ints++;

	char_searches++;

	/*
	 * Then do a full blown binary search on the smaller range
	 */
	while (max >= min)
	{
		bin_chars++;
		pos = (max - min) / 2 + min;
		c = set->func(name, ARRAY_ITEM(set, pos)->name, len);
		if (c == 0)
			break;
		else if (c < 0)
			max = pos - 1;
		else
			min = pos + 1;
	}


	if (c != 0)
	{
		if (c > 0)
			*loc = pos + 1;
		else
			*loc = pos;
		return NULL;
	}

	/*
	 * If we've gotten this far, then we've found at least
	 * one appropriate entry.  So we set *cnt to one
	 */
	*cnt = 1;

	/*
	 * So we know that 'pos' is a match.  So we start 'min'
	 * at one less than 'pos' and walk downard until we find
	 * an entry that is NOT matched.
	 */
	min = pos - 1;
	while (min >= 0 && !set->func(name, ARRAY_ITEM(set, min)->name, len))
		(*cnt)++, min--, lin_chars++;
	min++;

	/*
	 * Repeat the same ordeal, except this time we walk upwards
	 * from 'pos' until we dont find a match.
	 */
	max = pos + 1;
	while (max < set->max && !set->func(name, ARRAY_ITEM(set, max)->name, len))
		(*cnt)++, max++, lin_chars++;

	/*
	 * Alphabetically, the string that would be identical to
	 * 'name' would be the first item in a string of items that
	 * all began with 'name'.  That is to say, if there is an
	 * exact match, its sitting under item 'min'.  So we check
	 * for that and whack the count appropriately.
	 */
	if (strlen(ARRAY_ITEM(set, min)->name) == len)
		*cnt *= -1;

	/*
	 * Then we tell the caller where the lowest match is,
	 * in case they want to insert here.
	 */
	if (loc)
		*loc = min;

	/*
	 * Then we return the first item that matches.
	 */
	return ARRAY_ITEM(set, min);
}

 #define FIXED_ITEM(list, pos, size) (*(Array_item *) (list + ( pos * size )))

 /*
 * This is useful for finding items in a fixed array (eg, those lists that
 * are a simple fixed length arrays of 1st level structs.)
 *
 * This code is identical to find_array_item except ``list'' is a 1st
 * level array instead of a 2nd level array.
 */
void * BX_find_fixed_array_item (void *list, size_t size, int howmany, char *name, int *cnt, int *loc)
{
	int	len = strlen(name),
		min = 0,
		max = howmany,
		old_pos = -1,
		pos,
		c;

	*cnt = 0;

	while (1)
	{
		pos = (max + min) / 2;
		if (pos == old_pos)
		{
			*loc = pos;
			return NULL;
		}
		old_pos = pos;

		c = strncmp(name, FIXED_ITEM(list, pos, size).name, len);
		if (c == 0)
			break;
		else if (c > 0)
			min = pos;
		else
			max = pos;
	}
	
	/*
	 * If we've gotten this far, then we've found at least
	 * one appropriate entry.  So we set *cnt to one
	 */
	*cnt = 1;

	/*
	 * So we know that 'pos' is a match.  So we start 'min'
	 * at one less than 'pos' and walk downard until we find
	 * an entry that is NOT matched.
	 */
	min = pos - 1;
	while (min >= 0 && !strncmp(name, FIXED_ITEM(list, min, size).name, len))
		(*cnt)++, min--;
	min++;

	/*
	 * Repeat the same ordeal, except this time we walk upwards
	 * from 'pos' until we dont find a match.
	 */
	max = pos + 1;
	while ((max < howmany) && !strncmp(name, FIXED_ITEM(list, max, size).name, len))
		(*cnt)++, max++;

	/*
	 * Alphabetically, the string that would be identical to
	 * 'name' would be the first item in a string of items that
	 * all began with 'name'.  That is to say, if there is an
	 * exact match, its sitting under item 'min'.  So we check
	 * for that and whack the count appropriately.
	 */
	if (strlen(FIXED_ITEM(list, min, size).name) == len)
		*cnt *= -1;

	/*
	 * Then we tell the caller where the lowest match is,
	 * in case they want to insert here.
	 */
	if (loc)
		*loc = min;

	/*
	 * Then we return the first item that matches.
	 */
	return (void *)&FIXED_ITEM(list, min, size);
}



--- NEW FILE: hebrew.c ---
/*
 * hebrew.c 
 * by crisk
 * 
 * MS-Windows like Hebrew process routine
 *
 * This first started as an ircII script.. the function was 
 * ported almost 1 to 1 from the script, so you can excuse
 * the extreme mess.
 * 
 */

#include "irc.h"
#include "ircaux.h"
#define MAIN_SOURCE
#include "modval.h"

#ifdef WANT_HEBREW
#include <stdio.h>
#include <string.h>

unsigned char *heb_reverse(char translate, unsigned char *str)
{
   unsigned char tmp1,tmp2;
   int ind,len,transind;
   unsigned char *transloc;
   
   unsigned char *transfrom = "()[]{}";
   unsigned char *transto   = ")(][}{";
   
   len = strlen(str)-1; 
   
   for (ind=0;ind<((len+1)/2);ind++)
     {
	tmp1 = *(str+ind);
	tmp2 = *(str+len-ind);
	
	if (translate)
	  {
	     transloc = index(transfrom,tmp1);
	     if (transloc != NULL) 
	       {
		  transind = transloc-transfrom;
		  tmp1 = *(transto+transind);
	       }
	   
	     transloc = index(transfrom,tmp2);
	     if (transloc != NULL) 
	       {
		  transind = transloc-transfrom;
		  tmp2 = *(transto+transind);
	       }
	  }
	*(str+ind) = tmp2;
	*(str+len-ind) = tmp1;
     }
   
   if (translate)
   if (len % 2) 
     {
	ind = (len % 2)+1;
	transloc = index(transfrom,*(str+ind));
	if (transloc != NULL) 
	  {
	     transind = transloc-transfrom;
	     *(str+ind) = *(transto+transind);
	  }
     }	
   
   return str;
}

unsigned char *hebrew_process(unsigned char *str)
{
   
#define hebrew_isheb(x) ((x >= 224) && (x <= 256))
#define hebrew_iseng(x) (((x >= 'a') && (x <= 'z')) || ((x >= 'A') && (x <= 'Z')))   
#define hebrew_isnum(x) ((x >= '0') && (x <= '9'))   
  
   unsigned char *engspecial = "";
   unsigned char *numrelated = "+-/.,";
   
   int len;
   
   unsigned char *result;
   unsigned char let;
   unsigned char *tmpstr; 
   unsigned char *tmpbuf;
   int pos;
   int p;
   int mode;
   int pnum;
   int pnumplus;
   int ppp;
   int j,j2;
   
   if (!str || !*str)
   	return empty_string;
   len = strlen(str);
   
   result = (unsigned char *)new_malloc(len+2);
   tmpstr = (unsigned char *)new_malloc(len*2);
   tmpbuf = (unsigned char *)new_malloc(len*2);
   
   pos = 0;
   p   = 0;
   pnum = 0;
   mode = 0;
   
   while (pos < len)
     {
	let = str[pos];
	pos++;
	tmpstr[0] = let;
	tmpstr[1] = '\0';

	if ((hebrew_iseng(let)) || (index(engspecial,let) != NULL))
	  {
	     
	     strcat(tmpstr,tmpbuf);
	     
	     strcat(tmpstr,result);
	     strcpy(result,tmpstr);
	     tmpbuf[0] = '\0';
	     tmpstr[1] = '\0';
	     
	     p = 0;
	     mode = 1;
	     pnum = 0;
	  } else if (hebrew_isheb(let))
	  {
	     if (mode)
	       {
		  strcat(tmpbuf,result);
		  strcpy(result,tmpbuf);
	       }
	     else
	       {
		  tmpbuf = heb_reverse(1,tmpbuf);
		  strcat(tmpbuf,tmpstr);
		  strcpy(tmpstr,tmpbuf);
	       }
	     
	     mode = 0;
	     
	     for (j=0;j<p;j++)
	       *(tmpbuf+j) = *(result+j);
	     
	     for (j2=0;j2<strlen(tmpstr);j2++)
	       *(tmpbuf+j+j2) = *(tmpstr+j2);
	     
	     for (j=j;j<=strlen(result);j++)
	       *(tmpbuf+j+j2) = *(result+j);
	     
	     *(tmpbuf+j+j2) = '\0';
	     strcpy(result,tmpbuf);
	     tmpbuf[0] = '\0';
	     
	     p += strlen(tmpstr);
	     pnum = 0;
	  }
	else if (hebrew_isnum(let))
	  {
	     if (!mode)
	       {
		  tmpbuf = heb_reverse(1,tmpbuf);
		  pnumplus = 0;
		  
		  if (tmpbuf[0] != '\0')
		    {
		       if ((strlen(tmpbuf) == 1) && (index(numrelated,tmpbuf[0])))
			 {
			    strcat(tmpstr,tmpbuf);
			    tmpbuf[0] = '\0';
			    pnumplus = 1;
			 }
		       else
			 {
			    pnum = 0;
			 }
		    }
		  
		  strcat(tmpbuf,tmpstr);
		  strcpy(tmpstr,tmpbuf);

		  ppp = p-pnum;
		  
		  for (j=0;j<ppp;j++)
		    *(tmpbuf+j) = *(result+j);
		  
		  for (j2=0;j2<strlen(tmpstr);j2++)
		    *(tmpbuf+j+j2) = *(tmpstr+j2);
	     
		  for (j=j;j<=strlen(result);j++)
		    *(tmpbuf+j+j2) = *(result+j);
	
		  *(tmpbuf+j+j2) = '\0';
		  
		  strcpy(result,tmpbuf);
		  
		  tmpbuf[0] = '\0';
		  
		  p += strlen(tmpstr);
		  pnum++;
		  pnum += pnumplus; 
	       }
	     else
	       {
		  strcat(tmpstr,tmpbuf);
		  strcat(tmpstr,result);
		  strcpy(result,tmpstr);
		  tmpbuf[0]='\0';
		  p = 0;
	       }
	  }
	else
	  {
	     strcat(tmpstr,tmpbuf);
	     strcpy(tmpbuf,tmpstr);
	  }
     }

   mode = 1;                    /* MS-Hebrew behavior thing */
   
   if (mode)
     {
	strcat(tmpbuf,result);
	strcpy(result,tmpbuf);
     }
   else
     {
	tmpbuf = heb_reverse(1,tmpbuf);
	strcpy(tmpstr,tmpbuf);
	
	for (j=0;j<p;j++)
	  *(tmpbuf+j) = *(result+j);
	
	for (j2=0;j2<strlen(tmpstr);j2++)
	  *(tmpbuf+j+j2) = *(tmpstr+j2);
	
	for (j=j;j<=strlen(result);j++)
	  *(tmpbuf+j+j2) = *(result+j);
	
        strcpy(result,tmpbuf);
     }
   
   result = heb_reverse(0,result);		  

   strcpy(str,result);
   
   new_free(&result);
   new_free(&tmpstr);
   new_free(&tmpbuf);
   
   return str;
   
}
#endif


--- NEW FILE: struct.c ---
/* 
 * Written for BitchX by Colten Edwards (c) Feb 1999
 */

#include "irc.h"
static char cvsrevision[] = "$Id: struct.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(struct_c)
#include "struct.h"
#include "hash.h"
#include "hash2.h"
#include "misc.h"
#include "names.h"
#include "window.h"
#include "server.h"
#define MAIN_SOURCE
#include "modval.h"

#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

extern char *after_expando(char *, int, int *);

/* the types of IrcVariables (repeated in vars.h) */
#define BOOL_TYPE_VAR		0
#define CHAR_TYPE_VAR		1
#define INT_TYPE_VAR		2
#define STR_TYPE_VAR		3
#define SPECIAL_TYPE_VAR	4

#define VAR_READ_WRITE		0
#define VAR_READ_ONLY		1

static char *struct_name[] = {"WINDOW", "CHANNEL", "NICK", "DCC", "CSET", "USERLIST", "SHITLIST", "BANS", "EXEMPTBANS", "SERVER", "" };

#define WINDOW_LOOKUP		0
#define CHANNEL_LOOKUP		1
#define NICKLIST_LOOKUP 	2
#define DCC_LOOKUP		3
#define CSET_LOOKUP		4
#define USERLIST_LOOKUP		5
#define SHITLIST_LOOKUP		6
#define BANS_LOOKUP		7
#define EXEMPT_LOOKUP		8
#define SERVER_LOOKUP		9

typedef struct _lookup_struct
{
	char *code;
	int offset;
	int type;
	int readwrite;
} LookupStruct;

static LookupStruct server_struct[] = {
	{ "NAME",		offsetof(Server, name), STR_TYPE_VAR, VAR_READ_ONLY },
	{ "ITSNAME",		offsetof(Server, itsname), STR_TYPE_VAR, VAR_READ_ONLY },
	{ "PASSWORD",		offsetof(Server, password), STR_TYPE_VAR, VAR_READ_WRITE },
	{ "SNETWORK",		offsetof(Server, snetwork), STR_TYPE_VAR, VAR_READ_WRITE },
	{ "COOKIE",		offsetof(Server, cookie), STR_TYPE_VAR, VAR_READ_ONLY },
	{ "PORT",		offsetof(Server, port), INT_TYPE_VAR, VAR_READ_ONLY },

	{ "NICKNAME",		offsetof(Server, nickname), STR_TYPE_VAR, VAR_READ_ONLY },
	{ "USERHOST",		offsetof(Server, userhost), STR_TYPE_VAR, VAR_READ_WRITE },
	{ "NICKNAME_PENDING",	offsetof(Server, nickname_pending), INT_TYPE_VAR, VAR_READ_ONLY },
	{ "ORIGNICK_PENDING",	offsetof(Server, orignick_pending), INT_TYPE_VAR, VAR_READ_ONLY },

	{ "AWAY",		offsetof(Server, away), STR_TYPE_VAR, VAR_READ_WRITE },
	{ "AWAYTIME",		offsetof(Server, awaytime), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "OPERATOR",		offsetof(Server, operator), BOOL_TYPE_VAR, VAR_READ_ONLY },
	{ "SERVER2_8",		offsetof(Server, server2_8), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "VERSION",		offsetof(Server, version), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "VERSION_STRING",	offsetof(Server, version_string), STR_TYPE_VAR, VAR_READ_ONLY },

	{ "UMODES",		offsetof(Server, umodes), STR_TYPE_VAR, VAR_READ_ONLY },
	{ "UMODE",		offsetof(Server, umode),  CHAR_TYPE_VAR, VAR_READ_ONLY },

	{ "CONNECTED",		offsetof(Server, connected), BOOL_TYPE_VAR, VAR_READ_ONLY },

	{ "WRITE",		offsetof(Server, write), INT_TYPE_VAR, VAR_READ_ONLY },
	{ "READ",		offsetof(Server, read), INT_TYPE_VAR, VAR_READ_ONLY },
	{ "EOF",		offsetof(Server, eof), INT_TYPE_VAR, VAR_READ_ONLY },


	{ "CHANNEL",		offsetof(Server, chan_list), SPECIAL_TYPE_VAR, VAR_READ_ONLY },
	{ "ORIGNICK",		offsetof(Server, orignick), STR_TYPE_VAR, VAR_READ_WRITE },
	{ "LAG",		offsetof(Server, lag),	INT_TYPE_VAR, VAR_READ_ONLY },
	{ NULL,			0,				0, 0 }
};

static LookupStruct channel_struct[] = {
	{ "CHANNEL",		offsetof(ChannelList, channel), STR_TYPE_VAR, VAR_READ_ONLY },
	{ "SERVER",		offsetof(ChannelList, server), INT_TYPE_VAR, VAR_READ_ONLY  },
	{ "MODE",		offsetof(ChannelList, s_mode), STR_TYPE_VAR, VAR_READ_ONLY  },
	{ "TOPIC",		offsetof(ChannelList, topic), STR_TYPE_VAR, VAR_READ_WRITE  },
	{ "TOPIC_LOCK",		offsetof(ChannelList, topic_lock), INT_TYPE_VAR, VAR_READ_WRITE  },
		
	{ "LIMIT",		offsetof(ChannelList, limit), INT_TYPE_VAR, VAR_READ_ONLY  },
	{ "KEY",		offsetof(ChannelList, key), STR_TYPE_VAR, VAR_READ_ONLY  },
	{ "CHOP",		offsetof(ChannelList, chop), INT_TYPE_VAR, VAR_READ_ONLY  },
	{ "HOP",		offsetof(ChannelList, hop), INT_TYPE_VAR, VAR_READ_ONLY  },
	{ "VOICE",		offsetof(ChannelList, voice), INT_TYPE_VAR, VAR_READ_ONLY  },
	{ "BOUND",		offsetof(ChannelList, bound), INT_TYPE_VAR, VAR_READ_ONLY  },
	{ "CHANPASS",		offsetof(ChannelList, chanpass), STR_TYPE_VAR, VAR_READ_ONLY  },
	{ "CONNECTED",		offsetof(ChannelList, connected), INT_TYPE_VAR, VAR_READ_ONLY  },
	{ "REFNUM",		offsetof(ChannelList, refnum), INT_TYPE_VAR, VAR_READ_ONLY  },
	{ "WINDOW",		offsetof(ChannelList, window), SPECIAL_TYPE_VAR, VAR_READ_ONLY },

	{ "NICK",		offsetof(ChannelList, NickListTable), SPECIAL_TYPE_VAR, VAR_READ_ONLY },

	{ "MAXIDLE",		offsetof(ChannelList, max_idle), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "TOG_LIMIT",		offsetof(ChannelList, tog_limit), BOOL_TYPE_VAR, VAR_READ_WRITE  },
	{ "CHECK_IDLE",		offsetof(ChannelList, check_idle), BOOL_TYPE_VAR, VAR_READ_WRITE  },
	{ "DO_SCAN",		offsetof(ChannelList, do_scan), INT_TYPE_VAR, VAR_READ_ONLY  },
#if 0
	struct timeval	channel_create;		/* time for channel creation */
	struct timeval	join_time;		/* time of last join */
#endif

	{ "STATS_OPS",		offsetof(ChannelList, stats_ops), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_DOPS",		offsetof(ChannelList, stats_dops), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_BANS",		offsetof(ChannelList, stats_bans), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_UNBANS",	offsetof(ChannelList, stats_unbans), INT_TYPE_VAR, VAR_READ_WRITE  },

	{ "STATS_SOPS",		offsetof(ChannelList, stats_sops), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_SDOPS",	offsetof(ChannelList, stats_sdops), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_SHOPS",	offsetof(ChannelList, stats_shops), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_SDEHOPS",	offsetof(ChannelList, stats_sdehops), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_SEBANS",	offsetof(ChannelList, stats_sebans), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_SUNEBANS",	offsetof(ChannelList, stats_sunebans), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_SBANS",	offsetof(ChannelList, stats_sbans), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_SUNBANS",	offsetof(ChannelList, stats_sunbans), INT_TYPE_VAR, VAR_READ_WRITE  },

	{ "STATS_TOPICS",	offsetof(ChannelList, stats_topics), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_KICKS",	offsetof(ChannelList, stats_kicks), INT_TYPE_VAR, VAR_READ_WRITE  },
	{ "STATS_PUBS",		offsetof(ChannelList, stats_pubs), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STATS_PARTS",	offsetof(ChannelList, stats_parts), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STATS_SIGNOFFS",	offsetof(ChannelList, stats_signoffs), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STATS_JOINS",	offsetof(ChannelList, stats_joins), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STATS_EBANS",	offsetof(ChannelList, stats_ebans), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STATS_UNEBANS",	offsetof(ChannelList, stats_unebans), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STATS_CHANPASS",	offsetof(ChannelList, stats_chanpass), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STATS_HOPS",		offsetof(ChannelList, stats_hops), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STATS_DHOPS",	offsetof(ChannelList, stats_dhops), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "CSET",		offsetof(ChannelList, csets), SPECIAL_TYPE_VAR, VAR_READ_ONLY },

	{ "MSGLOG",		offsetof(ChannelList, msglog_on), BOOL_TYPE_VAR , VAR_READ_WRITE },
	{ "MSGLOG_FILE",	offsetof(ChannelList, logfile), STR_TYPE_VAR , VAR_READ_WRITE },

	{ "TOTALNICKS",		offsetof(ChannelList, totalnicks), INT_TYPE_VAR , VAR_READ_ONLY },
	{ "MAXNICKS",		offsetof(ChannelList, maxnicks), INT_TYPE_VAR , VAR_READ_ONLY },
	{ "MAXNICKSTIME",	offsetof(ChannelList, maxnickstime), INT_TYPE_VAR , VAR_READ_ONLY },
	{ "TOTALBANS",		offsetof(ChannelList, totalbans), INT_TYPE_VAR , VAR_READ_ONLY },
	{ "MAXBANS",		offsetof(ChannelList, maxbans), INT_TYPE_VAR , VAR_READ_ONLY },
	{ "MAXBANSTIME",	offsetof(ChannelList, maxbanstime), INT_TYPE_VAR , VAR_READ_ONLY },
	{ "BANS",		offsetof(ChannelList, bans), SPECIAL_TYPE_VAR, VAR_READ_ONLY },
	{ "EXEMPTBANS",		offsetof(ChannelList, exemptbans), SPECIAL_TYPE_VAR, VAR_READ_ONLY },
	{ NULL,			0,				0, 0 }
};

static LookupStruct user_struct[] = {
	{ "NICK",		offsetof(UserList, nick),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "HOST",		offsetof(UserList, host),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "COMMENT",		offsetof(UserList, comment),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "CHANNELS",		offsetof(UserList, channels),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "PASSWORD",		offsetof(UserList, password),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "FLAGS",		offsetof(UserList, flags),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "TIME",		offsetof(UserList, time),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ NULL,			0,				0, 0 }
};

static LookupStruct shit_struct[] = {
	{ "FILTER",		offsetof(ShitList, filter),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "LEVEL",		offsetof(ShitList, level),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "CHANNELS",		offsetof(ShitList, channels),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "REASON",		offsetof(ShitList, reason),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "TIME",		offsetof(ShitList, time),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ NULL,			0,				0, 0 }
};

static LookupStruct bans_struct[] = {
	{ "BAN",		offsetof(BanList, ban),	STR_TYPE_VAR, VAR_READ_ONLY },
	{ "SETBY",		offsetof(BanList, setby), STR_TYPE_VAR, VAR_READ_WRITE },
	{ "SENT_UNBAN",		offsetof(BanList, sent_unban),INT_TYPE_VAR, VAR_READ_WRITE },
	{ "SENT_UNBAN_TIME",	offsetof(BanList, sent_unban_time),INT_TYPE_VAR, VAR_READ_WRITE },
	{ "TIME",		offsetof(BanList, time), INT_TYPE_VAR, VAR_READ_ONLY },
	{ "COUNT",		offsetof(BanList, count), INT_TYPE_VAR, VAR_READ_ONLY },
	{ NULL,			0,				0, 0 }
};

static LookupStruct dcc_struct[] = {
	{ "USER",		offsetof(DCC_int, user),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "USERHOST",		offsetof(DCC_int, userhost),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "SERVER",		offsetof(DCC_int, server),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "ENCRYPT",		offsetof(DCC_int, encrypt),	STR_TYPE_VAR , VAR_READ_ONLY },
	{ "FILENAME",		offsetof(DCC_int, filename),	STR_TYPE_VAR , VAR_READ_ONLY },
	{ "OTHERNAME",		offsetof(DCC_int, othername),	STR_TYPE_VAR , VAR_READ_ONLY },
	{ "BYTES_READ",		offsetof(DCC_int, bytes_read),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "BYTES_SENT",		offsetof(DCC_int, bytes_sent),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "START_OFFSET",	offsetof(DCC_int, transfer_orders.byteoffset), INT_TYPE_VAR , VAR_READ_ONLY },
	{ "FILESIZE",		offsetof(DCC_int, filesize),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "PACKETS",		offsetof(DCC_int, packets),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "BLOCKSIZE",		offsetof(DCC_int, blocksize),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "DCC_FAST",		offsetof(DCC_int, dcc_fast),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "REMPORT",		offsetof(DCC_int, remport),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "LOCALPORT",		offsetof(DCC_int, localport),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "DCCNUM",		offsetof(DCC_int, dccnum),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ NULL,			0, 				0, 0}
};

static LookupStruct win_struct[] = {
	{ "NAME",		offsetof(Window, name),		STR_TYPE_VAR , VAR_READ_ONLY },
	{ "REFNUM",		offsetof(Window, refnum),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "SERVER",		offsetof(Window, server),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "TOP",		offsetof(Window, top),		INT_TYPE_VAR , VAR_READ_ONLY },
	{ "BOTTOM",		offsetof(Window, bottom),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "CURSOR",		offsetof(Window, cursor),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "LINE_CNT",		offsetof(Window, line_cnt),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "SCROLL",		offsetof(Window, noscroll),	BOOL_TYPE_VAR , VAR_READ_WRITE },
	{ "SCRATCH",		offsetof(Window, scratch_line), BOOL_TYPE_VAR , VAR_READ_WRITE },	
	{ "COLUMNS",		offsetof(Window, columns),	INT_TYPE_VAR , VAR_READ_ONLY },
	{ "NOTIFY_LEVEL",	offsetof(Window, notify_level), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "WINDOW_LEVEL",	offsetof(Window, window_level), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "CURRENT_CHANNEL",	offsetof(Window, current_channel),STR_TYPE_VAR , VAR_READ_ONLY },
	{ "WAITING_CHANNEL",	offsetof(Window, waiting_channel),STR_TYPE_VAR , VAR_READ_ONLY },
	{ "BIND_CHANNEL",	offsetof(Window, bind_channel), STR_TYPE_VAR , VAR_READ_ONLY },
	{ "QUERY_NICK",		offsetof(Window, query_nick),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "QUERY_HOST",		offsetof(Window, query_host),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "QUERY_CMD",		offsetof(Window, query_cmd),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "LOG",		offsetof(Window, log),		BOOL_TYPE_VAR , VAR_READ_WRITE },
	{ "LOGFILE",		offsetof(Window, logfile),	STR_TYPE_VAR , VAR_READ_WRITE },
	{ "LASTLOG_LEVEL",	offsetof(Window, lastlog_level),INT_TYPE_VAR , VAR_READ_WRITE },
	{ "LASTLOG_SIZE",	offsetof(Window, lastlog_size), INT_TYPE_VAR , VAR_READ_WRITE },
	{ "LASTLOG_MAX",	offsetof(Window, lastlog_max),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "HOLD_MODE",		offsetof(Window, hold_mode),	BOOL_TYPE_VAR , VAR_READ_WRITE },
	{ "MANGLER",		offsetof(Window, mangler),	BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "PROMPT",		offsetof(Window, prompt),	STR_TYPE_VAR, VAR_READ_WRITE },
	{ NULL,			0, 				0, 0}
};			

static LookupStruct nicklist_struct[] = {
	{ "NICK",		offsetof(NickList, nick),	STR_TYPE_VAR , VAR_READ_ONLY }, 
	{ "HOST",		offsetof(NickList, host),	STR_TYPE_VAR , VAR_READ_ONLY }, 
	{ "IP",			offsetof(NickList, ip),		STR_TYPE_VAR , VAR_READ_WRITE }, 
	{ "SERVER",		offsetof(NickList, server),	STR_TYPE_VAR , VAR_READ_ONLY }, 
	{ "IP_COUNT",		offsetof(NickList, ip_count),	INT_TYPE_VAR , VAR_READ_ONLY },

	{ "USERLIST",		offsetof(NickList, userlist),	SPECIAL_TYPE_VAR , VAR_READ_ONLY },
	{ "SHITLIST",		offsetof(NickList, shitlist),	SPECIAL_TYPE_VAR , VAR_READ_ONLY },

	{ "FLAGS",		offsetof(NickList, flags),	INT_TYPE_VAR , VAR_READ_ONLY },

	{ "IDLE_TIME",		offsetof(NickList, idle_time),	INT_TYPE_VAR , VAR_READ_WRITE },

	{ "FLOODCOUNT",		offsetof(NickList, floodcount),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "FLOODTIME",		offsetof(NickList, floodtime),	INT_TYPE_VAR , VAR_READ_WRITE },

	{ "NICKCOUNT",		offsetof(NickList, nickcount),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "NICKTIME",		offsetof(NickList, nicktime),	INT_TYPE_VAR , VAR_READ_WRITE },

	{ "KICKCOUNT",		offsetof(NickList, kickcount),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "KICKTIME",		offsetof(NickList, kicktime),	INT_TYPE_VAR , VAR_READ_WRITE },

	{ "JOINCOUNT",		offsetof(NickList, joincount),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "JOINTIME",		offsetof(NickList, jointime),	INT_TYPE_VAR , VAR_READ_WRITE },


	{ "DOPCOUNT",		offsetof(NickList, dopcount),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "DOPTIME",		offsetof(NickList, doptime),	INT_TYPE_VAR , VAR_READ_WRITE },

	{ "KICKCOUNT",		offsetof(NickList, bancount),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "KICKTIME",		offsetof(NickList, bantime),	INT_TYPE_VAR , VAR_READ_WRITE },

	{ "CREATED",		offsetof(NickList, created),	INT_TYPE_VAR , VAR_READ_ONLY },


	{ "STAT_KICKS", 	offsetof(NickList, stat_kicks),INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STAT_DOPS",		offsetof(NickList, stat_dops),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STAT_OPS",		offsetof(NickList, stat_ops),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STAT_HOPS",		offsetof(NickList, stat_hops),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STAT_DHOPS",		offsetof(NickList, stat_dhops),INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STAT_EBAN",		offsetof(NickList, stat_eban),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STAT_UNEBAN",	offsetof(NickList, stat_uneban),INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STAT_BANS",		offsetof(NickList, stat_bans),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STAT_UNBANS",	offsetof(NickList, stat_unbans),INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STAT_NICKS",		offsetof(NickList, stat_nicks),INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STAT_PUB",		offsetof(NickList, stat_pub),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "STAT_TOPICS",	offsetof(NickList, stat_topics),INT_TYPE_VAR , VAR_READ_WRITE },

	{ "SENT_REOP",		offsetof(NickList, sent_reop),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "SENT_REOP_TIME",	offsetof(NickList, sent_reop_time),INT_TYPE_VAR , VAR_READ_WRITE },
	{ "SENT_VOICE",		offsetof(NickList, sent_voice),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "SENT_VOICE_TIME",	offsetof(NickList, sent_voice_time),INT_TYPE_VAR , VAR_READ_WRITE },
	{ "SENT_DEOP",		offsetof(NickList, sent_deop),	INT_TYPE_VAR , VAR_READ_WRITE },
	{ "SENT_DEOP_TIME",	offsetof(NickList, sent_deop_time),INT_TYPE_VAR , VAR_READ_WRITE },
	{ "NEED_USERHOST",	offsetof(NickList, need_userhost), INT_TYPE_VAR , VAR_READ_ONLY },
	{ NULL,			0, 				0, 0}
};

static LookupStruct cset_struct[] = {
	{ "AINV",		offsetof(CSetList, set_ainv), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "ANNOY_KICK",		offsetof(CSetList, set_annoy_kick), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "AOP",		offsetof(CSetList, set_aop), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "AUTO_JOIN_ON_INVITE",offsetof(CSetList, set_auto_join_on_invite), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "AUTO_LIMIT",		offsetof(CSetList, set_auto_limit), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "AUTO_REJOIN",	offsetof(CSetList, set_auto_rejoin), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "BANTIME",		offsetof(CSetList, set_bantime), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "BITCH",		offsetof(CSetList, bitch_mode), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "CHANMODE",		offsetof(CSetList, chanmode), STR_TYPE_VAR, VAR_READ_WRITE },

	{ "CHANNEL_LOG",	offsetof(CSetList, channel_log), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "CHANNEL_LOG_FILE",	offsetof(CSetList, channel_log_file), STR_TYPE_VAR, VAR_READ_WRITE },
	{ "CHANNEL_LOG_LEVEL",	offsetof(CSetList, log_level), INT_TYPE_VAR, VAR_READ_WRITE },

	{ "COMPRESS_MODES",	offsetof(CSetList, compress_modes), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "CTCP_FLOOD_BAN",	offsetof(CSetList, set_ctcp_flood_ban), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "DEOPFLOOD",		offsetof(CSetList, set_deopflood), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "DEOPFLOOD_TIME",	offsetof(CSetList, set_deopflood_time), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "DEOP_ON_DEOPFLOOD",	offsetof(CSetList, set_deop_on_deopflood), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "DEOP_ON_KICKFLOOD",	offsetof(CSetList, set_deop_on_kickflood), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "HACKING",		offsetof(CSetList, set_hacking), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "JOINFLOOD",		offsetof(CSetList, set_joinflood), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "JOINFLOOD_TIME",	offsetof(CSetList, set_joinflood_time), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "KICKFLOOD",		offsetof(CSetList, set_kickflood), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "KICKFLOOD_TIME",	offsetof(CSetList, set_kickflood_time), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "KICK_IF_BANNED",	offsetof(CSetList, set_kick_if_banned), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "KICK_ON_DEOPFLOOD",	offsetof(CSetList, set_kick_on_deopflood), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "KICK_ON_JOINFLOOD",	offsetof(CSetList, set_kick_on_joinflood), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "KICK_ON_KICKFLOOD",	offsetof(CSetList, set_kick_on_kickflood), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "KICK_ON_NICKFLOOD",	offsetof(CSetList, set_kick_on_nickflood), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "KICK_ON_PUBFLOOD",	offsetof(CSetList, set_kick_on_pubflood), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "KICK_OPS",		offsetof(CSetList, set_kick_ops), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "LAMEIDENT",		offsetof(CSetList, set_lame_ident), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "LAMELIST",		offsetof(CSetList, set_lamelist), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "NICKFLOOD",		offsetof(CSetList, set_nickflood), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "NICKFLOOD_TIME",	offsetof(CSetList, set_nickflood_time), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "PUBFLOOD",		offsetof(CSetList, set_pubflood), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "PUBFLOOD_IGNORE_TIME",offsetof(CSetList, set_pubflood_ignore), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "PUBFLOOD_TIME",	offsetof(CSetList, set_pubflood_time), INT_TYPE_VAR, VAR_READ_WRITE },
	{ "SHITLIST",		offsetof(CSetList, set_shitlist), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "USERLIST",		offsetof(CSetList, set_userlist), BOOL_TYPE_VAR, VAR_READ_WRITE },
	{ "CHANNEL",		offsetof(CSetList, channel), STR_TYPE_VAR, VAR_READ_ONLY },
	{ NULL,			0, 				0, 0}
};

int find_structure(char *name)
{
int i;
	for (i = 0; *struct_name[i]; i++)
		if (!my_stricmp(struct_name[i], name))
			return i;	
	return -1;
}

int setup_structure(char *name, char *which, Window **win, DCC_int **dcc, ChannelList **chan, NickList **nick, int *server)
{
	int i;
	int serv = -1;
	i = find_structure(name);
	switch (i)
	{
		case WINDOW_LOOKUP:
		{
			*win = get_window_by_desc(which);
			if (!*win)
				*win = current_window;
			return WINDOW_LOOKUP;
		}
		case NICKLIST_LOOKUP:
		{
			char *ch = NULL;
			if (!*chan)
			{
				if (!(ch = get_current_channel_by_refnum(0)))
					return -1;
				if (!(*chan = lookup_channel(ch, current_window->server, 0)))
					return -1;
			}
			if (!ch && !*chan)
				return -1;
			*nick = find_nicklist_in_channellist(which, *chan, 0);
			return NICKLIST_LOOKUP;
		}
		case DCC_LOOKUP:
		{
			SocketList *s;
			DCC_int *n;
			int l = my_atol(which);
			s = get_socket(l);
			if (!s || !(n = (DCC_int *)s->info))
				break;
			*dcc = n;
			return DCC_LOOKUP;
		}
		case CHANNEL_LOOKUP:
		{
			char *ch = which;
			if (!*ch)
			{
				if (!(ch = get_current_channel_by_refnum(0)))
					return -1;
			}
			if (is_channel(ch))
				*chan = lookup_channel(ch, current_window->server, 0);
			else
			{
				if ((ch = make_channel(ch)))
					*chan = lookup_channel(ch, current_window->server, 0);
			}
			return CHANNEL_LOOKUP;
		}
		case SERVER_LOOKUP:
			if (*which && ((serv = my_atol(which)) != -1))
				*server = serv;
			return SERVER_LOOKUP;
		default:
			break;
	}
	return -1;
}


static inline int get_offset_int(void *tmp, int offset)
{
	int val = *(int *)((void *)tmp + offset);
	return val;
}

static inline char *get_offset_str(void *tmp, int offset)
{
	char *s = *(char **) ((void *)tmp + offset);
	return s;
}

static inline char *get_offset_char(void *tmp, int offset)
{
	char *s = (char *) ((void *)tmp + offset);
	return s;
}

static inline void set_offset_int(void *tmp, int offset, int val)
{
	int *ptr = (int *)((void *)tmp + offset);
	*ptr = val;
}

static inline void set_offset_str(void *tmp, int offset, char *val)
{
	char **ptr = (char **) ((void *)tmp + offset);
	if (val && *val)
		malloc_strcpy(ptr, val);
	else
		new_free(ptr);
}

static inline void set_offset_char(void *tmp, int offset, char *val)
{
	char *ptr = (char *) ((void *)tmp + offset);
	if (val && *val)
		strcpy(ptr, val);
	else
		*ptr = 0;
}

int lookup_code(LookupStruct *t, char *name)
{
	int i;
	for (i = 0; t[i].code; i++)
	{
		if (!my_stricmp(t[i].code, name))
			break;
	}
	if (!t[i].code)
		return -1;
	return i;
}

char *return_structure(LookupStruct *name, void *user)
{
int j;
char *ret = NULL;
char *s;
	if (!user)
		return NULL;
	for (j = 0; name[j].code; j++)
	{
		if (name[j].type == INT_TYPE_VAR)
			m_s3cat(&ret, ",", ltoa(get_offset_int(user, name[j].offset)));
		else if (name[j].type == STR_TYPE_VAR)
		{
			s = get_offset_str(user, name[j].offset);
			m_s3cat(&ret, ",", s ? s : empty_string);
		}
		else if (name[j].type == BOOL_TYPE_VAR)
			m_s3cat(&ret, ",", on_off(get_offset_int(user, name[j].offset)));
		else if (name[j].type == CHAR_TYPE_VAR)
			m_s3cat(&ret, ",", get_offset_char(user, name[j].offset));
		else if (name[j].type == SPECIAL_TYPE_VAR)
		{
			s = LOCAL_COPY(name[j].code);
			lower(s);
			m_s3cat(&ret, ",", "<");
			malloc_strcat(&ret, s);
			malloc_strcat(&ret, ">");
		}
	}
	return ret;
}

char *lookup_structure_item(int idx, LookupStruct *name, void *user, char *arg)
{
	if (!user || (idx == -1))
		return NULL;
	switch (name[idx].type)
	{
		case STR_TYPE_VAR:
			if (arg && !name[idx].readwrite)
				set_offset_str(user, name[idx].offset, arg);
			return m_strdup(get_offset_str(user, name[idx].offset));
		case CHAR_TYPE_VAR:
			if (arg && !name[idx].readwrite)
				set_offset_char(user, name[idx].offset, arg);
			return m_strdup(get_offset_char(user, name[idx].offset));
		case INT_TYPE_VAR:
			if (arg && *arg && !name[idx].readwrite)
				set_offset_int(user, name[idx].offset, my_atol(arg));
			return m_strdup(ltoa(get_offset_int(user, name[idx].offset)));
		case BOOL_TYPE_VAR:
			if (arg && *arg && !name[idx].readwrite)
			{
				int val = 0;
				if (!my_stricmp(arg, "on"))
					val = 1;
				else if (!my_stricmp(arg, "off"))
					val = 0;
				else
					val = my_atol(arg);
				set_offset_int(user, name[idx].offset, val);
			}
			return m_strdup(on_off(get_offset_int(user, name[idx].offset)));
		default:
			break;
	}
	return NULL;
}

char *lookup_structure_member(int type, char *name, char *rest, NickList *n, ChannelList *ch, Window *w, DCC_int *dcc, int serv)
{
LookupStruct *tmp = NULL;
int i = -1;
char *ret = NULL;
void *user = NULL;
int code = -1;
	switch (type)
	{
		case WINDOW_LOOKUP:
			tmp = win_struct;
			user = w;
			break;
		case NICKLIST_LOOKUP:
			tmp = nicklist_struct;
			user = n;
			break;
		case DCC_LOOKUP:
			tmp = dcc_struct;
			user = dcc;
			break;
		case CHANNEL_LOOKUP:
			tmp = channel_struct;
			user = ch;
			break;
		case CSET_LOOKUP:
			tmp = cset_struct;
			if (ch)
				user = ch->csets;
			break;
		case SERVER_LOOKUP:
			tmp = server_struct;
			if (serv != -1 && serv < server_list_size())
			{
				user = (void *)(get_server_list());
				user += (sizeof(Server) * serv);
				ch = ((Server *)user)->chan_list;
			}
		default:
			break;
	}
	if (!tmp || !user) 
		return NULL;
	if (!name || !*name)
		return return_structure(tmp, user);
 	if ((i = lookup_code(tmp, name)) == -1)
		return NULL;
 	switch(tmp[i].type)
	{
		case INT_TYPE_VAR:
		case STR_TYPE_VAR:
		case BOOL_TYPE_VAR:
		case CHAR_TYPE_VAR:
		{
			char *lparen;
			if (rest && !*rest)
				rest = NULL;
#if 0
			if ((lparen = strchr(rest+1, '[')))
			{
				if ((rparen = MatchingBracket(lparen+1, '[', ']')))
					*rparen++ = 0;
				*lparen++ = 0;
				if (!*lparen)
					lparen = NULL;
			} else if (*rest)
#endif
				lparen = rest;
			ret = lookup_structure_item(i, tmp, user, lparen);
			break;
		}
		case SPECIAL_TYPE_VAR:
		{
			code = find_structure(name);
			switch (code)
			{
				case NICKLIST_LOOKUP: /* this is a special case */
					tmp = nicklist_struct;
					n = sorted_nicklist(ch, NICKSORT_NONE);
					for (; n; n = n->next)
						if (!rest || !*rest)
							m_s3cat(&ret, ",", n->nick);
						else if (*rest == '>' &&  *(rest+1) && !my_stricmp(rest+1, n->nick))
							ret = return_structure(tmp, n); 
					clear_sorted_nicklist(&n);
					return ret;
					break;
				case USERLIST_LOOKUP:
					tmp = user_struct;
					if (n)
						user = n->userlist;
					break;
				case SHITLIST_LOOKUP:
					tmp = shit_struct;
					if (n)
						user = n->shitlist;
					break;
				case CSET_LOOKUP:
					tmp = cset_struct;
					if (ch)
						user = ch->csets;
					break;
				case WINDOW_LOOKUP:
					tmp = win_struct;
					if (ch)
						user = ch->window;
					break;
				case EXEMPT_LOOKUP:
					tmp = bans_struct;
					if (ch)
						user = ch->exemptbans;
					break;
				case BANS_LOOKUP:
				{
					BanList *b;
					tmp = bans_struct;
					if (ch)
						user = ch->bans;
					for (b = ch->bans; b; b= b->next)
						if (!rest || !*rest)
							m_s3cat(&ret, ",", b->ban);
						else if (*rest == '>' &&  *(rest+1) && !my_stricmp(rest+1, b->ban))
							ret = return_structure(tmp, b);
					return ret;
					break;
				}
				case CHANNEL_LOOKUP:
					tmp = channel_struct;
					ch = ((Server *)user)->chan_list;
					for (; ch; ch = ch->next)
						if (!rest || !*rest)
							m_s3cat(&ret, ",", ch->channel);
						else if (*rest == '>' &&  *(rest+1) && !my_stricmp(rest+1, ch->channel))
							ret = return_structure(tmp, ch);
					return ret;
					break;
			}
			if (rest && *rest == '>' && *(rest + 1))
			{
				char *lparen, *rparen;
				if ((lparen = strchr(rest+1, '[')))
				{
					if ((rparen = MatchingBracket(lparen+1, '[', ']')))
						*rparen++ = 0;
					*lparen++ = 0;
				} else if (rest && !*rest)
					rest = NULL;
				i = lookup_code(tmp, rest+1);
				ret = lookup_structure_item(i, tmp, user, lparen);
			}
			else
				ret = return_structure(tmp, user);
		}
		default:
			break;
	}
	return ret;
}


char *call_structure_internal(char *name, char *args, char *rest, char *rest1)
{
	Window		*win	= NULL; 
	ChannelList	*chan	= NULL;
	DCC_int		*dcc	= NULL;
	NickList	*nick	= NULL;
	char		*ret	= NULL;
	int		type =  -1;
	int		server = -1;
	char		*lparen, *rparen;
	type = setup_structure(name, args, &win, &dcc, &chan, &nick, &server);
	if ((lparen = strchr(rest, '[')))
	{
		if ((rparen = MatchingBracket(lparen+1, '[', ']')))
			*rparen++ = 0;
		*lparen++ = 0;
		rest1 = lparen;
	}
	if (type != -1)
		ret = lookup_structure_member(type, rest, rest1, nick, chan, win ? win : current_window, dcc, server);
	return ret;
}

--- NEW FILE: translat.c ---
/*
 * translat.c:  Stuff for handling character translation tables
 * and a digraph entry facility.  Support an international IRC!
 *
 * I listen to Sex Pistols, so I assume everyone in this world,
 * and more specifically, all servers, are using ISO 8859/1
 * (Latin-1).  And in case of doubt, please consult Jarkko 'Wiz'
 * Oikarinen's document "Internet Relay Chat Protocol" (doc/Comms
 * in the ircd package), paragraph 2.2.  Besides, all of the sane
 * world has already converted to this set.  (X-Windows, Digital,
 * MS-Windows, etc.)
 * If someone please would forward me some documentation on other
 * international sets, like 8859/2 - 8859/10 etc, please do so!
 * Moreover, feedback on the tables in the definition files would
 * be greatly appreciated!
 * Another idea, to be implemented some beautiful day, would be
 * to add transliteration of the Kanji/Katakana sets used in
 * the far east.  8-)
 * Tomten <tomten at solace.hsh.se> / <tomten at lysator.liu.se>
 */

#include "irc.h"
static char cvsrevision[] = "$Id: translat.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(translat_c)
#include "struct.h"

#if defined(TRANSLATE) || defined(HEBREW)
#include "vars.h"
#include "translat.h"
#include "ircaux.h"
#include "window.h"
#include "screen.h"
#include "output.h"
#include "hebrew.h"
#define MAIN_SOURCE
#include "modval.h"


#ifndef TRANSLATION_PATH
/* we have this here as a safety feature if for some reason TRANSLATION_PATH
 * is not defined.
 */
#define TRANSLATION_PATH "/usr/local/lib/bx/translation"
#endif


static	unsigned char	my_getarg (char **);

/* Globals */
unsigned char	transToClient[256];    /* Server to client translation. */
unsigned char	transFromClient[256];  /* Client to server translation. */
char	translation = 0;	/* 0 for transparent (no) translation. */
char	digraph_changed = 0;

/*
 * dig_table_lo[] and dig_table_hi[] contain the character pair that
 * will result in the digraph in dig_table_di[].  To avoid searching
 * both tables, I take the lower character of the pair, and only
 * search dig_table_lo[].  Thus, dig_table_lo[] must always contain
 * the lower character of the pair.
 *
 * The digraph tables are based on those in the excellent editor Elvis,
 * with some additions for those, like me, who are used to VT320 or
 * VT420 terminals.
 */

#define	DiLo(x)	x,
#define	DiHi(x)
#define	DiDi(x)

/*
 * Digraph tables.  Note that, when adding a new digraph, the character
 * of the pair with the lowest value, *must* be in the DiLo column.
 * The higher of the pair goes in DiHi, and the digraph itself in DiDi.
 */

unsigned char	dig_table_lo[DIG_TABLE_SIZE] =
{
#include "digraph.inc"
	0
};


#undef	DiLo
#undef	DiHi
#undef	DiDi
#define	DiLo(x)
#define	DiHi(x)	x,
#define	DiDi(x)

unsigned char	dig_table_hi[DIG_TABLE_SIZE] =
{
#include "digraph.inc"
	0
};


#undef	DiLo
#undef	DiHi
#undef	DiDi
#define	DiLo(x)
#define	DiHi(x)
#define	DiDi(x)	x,

unsigned char	dig_table_di[DIG_TABLE_SIZE] =
{
#include "digraph.inc"
	0
};


/*
 * enter_digraph:  The BIND function ENTER_DIGRAPH.
 */
void	enter_digraph(char key, char *ptr)
{
	last_input_screen->digraph_hit = 1;  /* Just stuff away first character. */
}

/*
 * get_digraph:  Called by edit_char() when a digraph entry is activated.
 * Looks up a digraph given char c1 and the global char
 * last_input_screen->digraph_hit.
 */
unsigned char	get_digraph(unsigned char c1)
{
	int	i = 0;
	unsigned char	c,
		c2 = last_input_screen->digraph_first;

	last_input_screen->digraph_hit = 0;
	if (c1 > c2)	/* Make sure we have the lowest one in c1. */
		c = c1,	c1 = c2, c2 = c;
	while (dig_table_lo[i])
	{	/* Find digraph and return it. */
		if ((dig_table_lo[i] == c1) && (dig_table_hi[i] == c2))
			return dig_table_di[i];
		i++;
	}
	return 0;		/* Failed lookup. */
}


/*
 * set_translation:  Called when the TRANSLATION variable is SET.
 * Attempts to load a new translation table.
 */
void set_translation(Window *win, char *tablename, int unused)
{
	FILE	*table;
	unsigned char	temp_table[512];
	char	*filename = NULL, *s;
	int	inputs[8];
	int	j,
		c = 0;
	char	buffer[BIG_BUFFER_SIZE + 1];

	if (!tablename)
	{
		translation = 0;
		return;
	}
	for (s = tablename; *s; s++)
	{
		if (isspace((unsigned char)*s))
		{
			*s = '\0';
			break;
		}			
	}
	
	tablename = upper(tablename);

	/* Check for transparent mode; ISO-8859/1, Latin-1 */
	if (!strcmp("LATIN_1", tablename))
	{
		translation = 0;
		return;
	}

	/* Else try loading the translation table from disk. */
	malloc_strcpy(&filename, TRANSLATION_PATH "/");
	malloc_strcat(&filename, tablename);
	if ( !(table = uzfopen(&filename, ".", 0)) )
	{
		say("Cannot open character table definition \"%s\" !",
			tablename);
		set_string_var(TRANSLATION_VAR, NULL);
		new_free(&filename);
		return;
	}

	/* Any problems in the translation tables between hosts are
	 * almost certain to be caused here.
	 * many scanf implementations do not work as defined. In particular,
	 * scanf should ignore white space including new lines (many stop
	 * at the new line character, hence the fgets and sscanf workaround),
	 * many fail to read 0xab as a hexadecimal number (failing on the
	 * x) despite the 0x being defined as optionally existing on input,
	 * and others zero out all the output variables if there is trailing
	 * non white space in the format string which doesn't appear on the
	 * input. Overall, the standard I/O libraries have a tendancy not
	 * to be very standard.
	 */

	while (fgets(buffer, 80, table))
	{
		sscanf(buffer, "0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x",
		    inputs+0, inputs+1, inputs+2, inputs+3,
		    inputs+4, inputs+5, inputs+6, inputs+7);
		for (j = 0; j<8; j++)
			temp_table[c++] = (unsigned char) inputs[j];
	}
	fclose(table);
	new_free(&filename);
	if (c == 512)
	{
		for (c = 0; c <= 255; c++)
		{
			transToClient[c] = temp_table[c];
			transFromClient[c] = temp_table[c | 256];
		}
#if 0
		for (c = 0; c <= 255; c++)
			transToClient[c] = c;
#endif

		translation = 1;
	}
	else
	{
		say("Error loading translation table \"%s\" !", tablename);
		set_string_var(TRANSLATION_VAR, NULL);
	}
}

/*
 * digraph:  The /DIGRAPH command with facilities.
 * This routine is *NOT* finished yet.
 */

BUILT_IN_COMMAND(digraph)
{
	char	*arg;
	u_char	c1,
		c2 = '\0',
		c3 = '\0';
	int	len,
		i;

	if ((arg = next_arg(args, &args)) && (*arg == '-'))
	{
		arg++;
		if ((len = strlen(arg)) == 0)
		{
			say("Unknown or missing flag.");
			return;
		}
		if (my_strnicmp(arg, "add", len) == 0)
		{
			/*
			 * Add a digraph to the table.
			 * I *know*.  This *is* a kludge.
			 */
			if ((i = strlen((char *)dig_table_lo)) ==
					DIG_TABLE_SIZE - 1)
				say("Sorry, digraph table full.");
			else
			{
				while ((c1 = my_getarg(&args)) &&
				    (c2 = my_getarg(&args)) &&
				    (c3 = my_getarg(&args)))
				{
					/* Pass c1 to get_digraph() */
					last_input_screen->digraph_first = c1;	    
					if (get_digraph(c2) == 0)
					{
						dig_table_di[i] = c3;
						/* Make sure c1 <= c2 */
						if (c1 > c2)
						    c3 = c1, c1 = c2, c2 = c3;
						dig_table_lo[i] = c1;
						dig_table_hi[i] = c2;
						i++;
						dig_table_lo[i] =
							dig_table_hi[i] =
					 		dig_table_di[i] =
							(unsigned char) 0;
						digraph_changed = 1;
						say("Digraph added to table.");
					}
					else
					{
				say("Digraph already defined in table.");
				break;
					}
				}
				if (!c2 || !c3)
					say("Unknown or missing argument.");
			}

		}
		else if (my_strnicmp(arg, "remove", len) == 0)
		{

			/* Remove a digraph from the table. */
			if ((i = strlen((char *)dig_table_lo)) == 0)
				say("Digraph table is already empty.");
			else
			{
				if ((c1 = my_getarg(&args)) &&
						(c2 = my_getarg(&args)))
				{
					i = 0;
					if (c1 > c2)
						c3 = c1, c1 = c2, c2 = c3;
					while (dig_table_lo[i])
					{
						if ((dig_table_lo[i] == c1) &&
						    (dig_table_hi[i] == c2))
					/*
					 * strcpy() is not guaranteed for
					 * overlapping copying, but this one
					 * is high -> low. Ought to be fixed.
					 */
	/* re-indent this block - phone, jan 1993. */
				{
					strcpy(((char *)dig_table_lo + i),
					    (char *)(dig_table_lo + (i + 1)));
					strcpy((char *)(dig_table_hi + i),
					    (char *)(dig_table_hi + (i + 1)));
					strcpy((char *)(dig_table_di + i),
					    (char *)(dig_table_di + (i + 1)));
					digraph_changed = 1;
					put_it("Digraph removed from table.");
					return;
				}
	/* much better */
						i++;
					}
					say("Digraph not found.");
				}
			}
		}
		else if (my_strnicmp(arg, "clear", len) == 0)
		{

			/* Clear digraph table. */
			dig_table_lo[0] = dig_table_hi[0] = dig_table_di[0] =
				(unsigned char) 0;
			digraph_changed = 1;
			say("Digraph table cleared.");

		}
		else
			say("Unknown flag.");
	}
	else
	{

		/* Display digraph table. */
		char	buffer1[8];
		char	buffer2[192];

		say("Digraph table:");
		buffer2[0] = (char) 0;
		i = 0;
		while(dig_table_lo[i])
		{
			sprintf(buffer1, "%c%c %c   ", dig_table_lo[i],
			    dig_table_hi[i], dig_table_di[i]);
			strcat(buffer2, buffer1);
			if ((++i % 10) == 0)
			{
				put_it(buffer2);
				buffer2[0] = (char) 0;
			}
		}
		if (buffer2[0])
			put_it(buffer2);
		sprintf(buffer2, "%d digraphs listed.", i);
		say(buffer2);
	}
}

static	unsigned char my_getarg(char **args)
{
	unsigned char *arg;

	arg = (unsigned char *)next_arg(*args, args);
	if (!args || !*args || !arg)
		return '\0';
	/* Don't trust isdigit() with 8 bits. */
	if ((*arg <= '9') && (*arg >= '0'))
	{
		unsigned char i = *arg & 0x0f;
		while ( *(++arg) )
			i = (i * 10) + (*arg & 0x0f);
		return i;
	}
	else if ( (*arg == '!') && (*(arg + 1)) )
		return *(arg + 1) | 0x80;
	return *arg;
}

void	save_digraphs(FILE *fp)
{
	if (digraph_changed)
	{

		int	i = 0;
		char	*command = "\nDIGRAPH -ADD ";

		fprintf(fp, "DIGRAPH -CLEAR");
		fprintf(fp, command);
		while(1)
		{
			fprintf(fp, "%d %d %d  ", dig_table_lo[i],
				dig_table_hi[i], dig_table_di[i]);
			if (!dig_table_lo[++i])
				break;
			if (!(i % 5))
				fprintf(fp, command);
		}
		fputc('\n', fp);

	}
}

#endif

--- NEW FILE: cset.c ---
/************
 *  cset.c  *
 ************
 *
 * My code for creating all the "Channel Sets" on a per channel
 * basis. We manage them here, and then just insert/remove/return them 
 * when a channel calls for them.
 * Code for creating and modifying "Window Sets" on a per window
 * basis. 
 *
 * Note: Notice the shameless use of typecasting to get these functions
 *	 to work as painlessly as possible.
 *
 * Written by Scott H Kilau
 * Modified by Colten D Edwards for /wset
 *
 * Copyright(c) 1997
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
[...1269 lines suppressed...]
			for (nick = next_nicklist(chan, NULL); nick; nick = next_nicklist(chan, nick))
				count++;
			set_channel_limit(chan, chan->limit, cs->set_auto_limit, count);
		} else
			set_channel_limit(chan, chan->limit, 0, 0);
	}
}

#ifdef GUI
CSetArray *return_cset_var(int nummer)
{
   return &cset_array[nummer];
}

WSetArray *return_wset_var(int nummer)
{
   return &wset_array[nummer];
}
#endif


--- NEW FILE: files.c ---
/*
 * files.c -- allows you to read/write files. Wow.
 *
 * (C) 1995 Jeremy Nelson (ESL)
 * See the COPYRIGHT file for more information
 */

#include "irc.h"
static char cvsrevision[] = "$Id: files.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(files_c)
#include "ircaux.h"
#define MAIN_SOURCE
#include "modval.h"

/* Here's the plan.
 *  You want to open a file.. you can READ it or you can WRITE it.
 *    unix files can be read/written, but its not the way you expect.
 *    so we will only alllow one or the other.  If you try to write to
 *    read only file, it punts, and if you try to read a writable file,
 *    you get a null.
 *
 * New functions: open(FILENAME <type>)
 *			<type> is 0 for read, 1 for write, 0 is default.
 *			Returns fd of opened file, -1 on error
 *		  read (fd)
 *			Returns line for given fd, as long as fd is
 *			opened via the open() call, -1 on error
 *		  write (fd text)
 *			Writes the text to the file pointed to by fd.
 *			Returns the number of bytes written, -1 on error
 *		  close (fd)
 *			closes file for given fd
 *			Returns 0 on OK, -1 on error
 *		  eof (fd)
 *			Returns 1 if fd is at EOF, 0 if not. -1 on error
 */

struct FILE___ {
	FILE *file;
	struct FILE___ *next;
};
typedef struct FILE___ File;

static File *FtopEntry = NULL;

File *new_file (void)
{
	File *tmp = FtopEntry;
	File *tmpfile = (File *)new_malloc(sizeof(File));

	if (FtopEntry == NULL)
		FtopEntry = tmpfile;
	else
	{
		while (tmp->next)
			tmp = tmp->next;
		tmp->next = tmpfile;
	}
	return tmpfile;
}

void remove_file (File *file)
{
	File *tmp = FtopEntry;

	if (file == FtopEntry)
		FtopEntry = file->next;
	else
	{
		while (tmp->next && tmp->next != file)
			tmp = tmp->next;
		if (tmp->next)
			tmp->next = tmp->next->next;
	}
	fclose(file->file);
	new_free((char **)&file);
}

	
int open_file_for_read (char *filename)
{
	char *dummy_filename = NULL;
	FILE *file;

	malloc_strcpy(&dummy_filename, filename);
	file = uzfopen(&dummy_filename, ".", 0);
	new_free(&dummy_filename);
	if (file)
	{
		File *nfs = new_file();
		nfs->file = file;
		nfs->next = NULL;
		return fileno(file);
	}
	else
		return -1;
}

int open_file_for_write (char *filename)
{
	/* patch by Scott H Kilau so expand_twiddle works */
	char *expand = NULL;
	FILE *file;

	if (!(expand = expand_twiddle(filename)))
		malloc_strcpy(&expand, filename);
	file = fopen(expand, "a");
	new_free(&expand);
	if (file)
	{
		File *nfs = new_file();
		nfs->file = file;
		nfs->next = NULL;
		return fileno(file);
	}
	else 
		return -1;
}

int open_file_for_bwrite (char *filename)
{
	/* patch by Scott H Kilau so expand_twiddle works */
	char *expand = NULL;
	FILE *file;

	if (!(expand = expand_twiddle(filename)))
		malloc_strcpy(&expand, filename);
	file = fopen(expand, "wb");
	new_free(&expand);
	if (file)
	{
		File *nfs = new_file();
		nfs->file = file;
		nfs->next = NULL;
		return fileno(file);
	}
	else 
		return -1;
}

File *lookup_file (int fd)
{
	File *ptr = FtopEntry;

	while (ptr)
	{
		if (fileno(ptr->file) == fd)
			return ptr;
		else
			ptr = ptr -> next;
	}
	return NULL;
}

int file_write (int fd, char *stuff)
{
	File *ptr = lookup_file(fd);
	int ret;
	if (!ptr)
		return -1;
	ret = fprintf(ptr->file, "%s\n", stuff);
	fflush(ptr->file);
	return ret;
}

int file_writeb (int fd, char *stuff)
{
	File *ptr = lookup_file(fd);
	int ret;
	if (!ptr)
		return -1;
	ret = fwrite(stuff, 1, strlen(stuff), ptr->file);
	fflush(ptr->file);
	return ret;
}

char *file_read (int fd)
{
	File *ptr = lookup_file(fd);
	if (!ptr)
		return m_strdup(empty_string);
	else
	{
		char blah[10240];
		if ((fgets(blah, 10239, ptr->file)))
			chop(blah, 1);
		else
			blah[0] = 0;
		return m_strdup(blah);
	}
}

char *file_readb (int fd, int numb)
{
	File *ptr = lookup_file(fd);
	if (!ptr)
		return m_strdup(empty_string);
	else
	{
		char *blah = (char *)new_malloc(numb+1);
		if ((fread(blah, 1, numb, ptr->file)))
			blah[numb] = 0;
		else
			blah[0] = 0;
		return blah;
	}
}


int	file_eof (int fd)
{
	File *ptr = lookup_file (fd);
	if (!ptr)
		return -1;
	else
		return feof(ptr->file);
}

int	file_close (int fd)
{
	File *ptr = lookup_file (fd);
	if (!ptr)
		return -1;
	else
		remove_file (ptr);
	return 0;
}

/* 
** by: Walter Bright via Usenet C newsgroup
**
** modified by: Bob Stout based on a recommendation by Ray Gardner
**
** There is no point in going to asm to get high speed file copies. Since it
** is inherently disk-bound, there is no sense (unless tiny code size is
** the goal). Here's a C version that you'll find is as fast as any asm code
** for files larger than a few bytes (the trick is to use large disk buffers):
*/

int file_copy(int from,int to)
{
	int bufsiz;
	if (from < 0)
		return 1;
	if (to < 0)
		return 1;
		
	if (!fork())
	{
	        /* Use the largest buffer we can get    */
		for (bufsiz = 0x4000; bufsiz >= 128; bufsiz >>= 1)
		{
			register char *buffer;
		
			buffer = (char *) malloc(bufsiz);
			if (buffer)
			{
				while (1)
				{
					register int n;

					n = read(from,buffer,bufsiz);
					if (n == -1)                /* if error             */
						break;
					if (n == 0)                 /* if end of file       */
					{   
						free(buffer);
						_exit(0);
					}
					if (n != write(to,buffer,(unsigned) n))
						break;
				}
				free(buffer);
				break;
			}
		}
		_exit(1);
	}
	return 0;
}

int	file_valid (int fd)
{
	if (lookup_file(fd))
		return 1;
	return 0;
}
  

--- NEW FILE: ircsig.c ---
/*
 * ircsig.c: has a `my_signal()' that uses sigaction().
 *
 * written by matthew green, 1993.
 *
 * i stole bits of this from w. richard stevens' `advanced programming
 * in the unix environment' -mrg
 */


#include "irc.h"
#include "irc_std.h"

sigfunc * my_signal(int sig_no, sigfunc *sig_handler, int misc_flags)
{
	/*
	 * misc_flags is unused currently.  it's planned to be used
	 * to use some of the doovier bits of sigaction(), if at
	 * some point we need them, -mrg
	 */

	struct sigaction sa = {{ 0 }}, osa = {{ 0 }};

	if (sig_no < 0)
		return NULL;

	sa.sa_handler = sig_handler;

	sigemptyset(&sa.sa_mask);
	sigaddset(&sa.sa_mask, sig_no);

	/* this is ugly, but the `correct' way.  i hate c. -mrg */
	sa.sa_flags = 0;
#if defined(SA_RESTART) || defined(SA_INTERRUPT)
	if (SIGALRM == sig_no || SIGINT == sig_no)
	{
# if defined(SA_INTERRUPT)
		sa.sa_flags |= SA_INTERRUPT;
# endif /* SA_INTERRUPT */
	}
	else
	{
# if defined(SA_RESTART)
		sa.sa_flags |= SA_RESTART;
# endif /* SA_RESTART */
	}
#endif /* SA_RESTART || SA_INTERRUPT */

	if (0 > sigaction(sig_no, &sa, &osa))
		return ((sigfunc *)SIG_ERR);

	return ((sigfunc *)osa.sa_handler);
}

--- NEW FILE: notice.c ---
/*
 * notice.c: special stuff for parsing NOTICEs
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1991
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#include "irc.h"
static char cvsrevision[] = "$Id: notice.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(notice_c)
#include "struct.h"

#include "commands.h"
#include "who.h"
#include "ctcp.h"
#include "window.h"
[...1193 lines suppressed...]
			return;
		}
 		put_it("%s", convert_output_format("$G %BOper%bView%n is already %K[%W$0%K]", "%s", on_off(get_int_var(OV_VAR))));
		hide = old_hide;
		{
			char buffer[BIG_BUFFER_SIZE] = "~/.BitchX/operview.log";
			Window *tmp = get_window_by_name("oper_view");
			if (tmp)
			{
				tmp->log = log;
				do_log(tmp->log, buffer, &tmp->log_fp); 
				if (!tmp->log_fp)
					tmp->log = 0;
				tmp->mangler = operlog_line_mangler;
			}
		}
		
	}
}
#endif

--- NEW FILE: readlog.c ---
/* 
 * Copyright Colten Edwards 1996
 */
 
#include "irc.h"
static char cvsrevision[] = "$Id: readlog.c,v 1.1.1.2 2003/05/17 07:00:23 root Exp $";
CVS_REVISION(readlog_c)
#include "struct.h"

#include <sys/stat.h>

#include "ircterm.h"
#include "server.h"
#include "vars.h"
#include "ircaux.h"
#include "input.h"
#include "window.h"
#include "screen.h"
#include "output.h"
#include "status.h"
#include "misc.h"
#define MAIN_SOURCE
#include "modval.h"
#ifdef WANT_HEBREW
#include "hebrew.h"
#endif

FILE * msg_fp = NULL;

static  Window  *msg_window = NULL;
static  int     finished_msg_paging = 0;
static  Screen  *msg_screen = NULL;
static  int     use_msg_window = 0;
static	void log_prompt (char *name, char *line);
static	void set_msg_screen (Screen *);
static	char	*(*read_log_func) (char *, int, FILE *);
	void	log_put_it(const char *topic, const char *format, ...);
	
BUILT_IN_COMMAND(remove_log)
{
	char *expand;
	char *filename = NULL;
	int old_display = window_display;

	int  reset_logptr = 0;

	if ((get_string_var(MSGLOGFILE_VAR) == NULL) || (get_string_var(CTOOLZ_DIR_VAR) == NULL))
		return;
	malloc_sprintf(&filename, "%s/%s", get_string_var(CTOOLZ_DIR_VAR), get_string_var(MSGLOGFILE_VAR));
	expand = expand_twiddle(filename);
	new_free(&filename);
	window_display = 0;	
	reset_logptr = logmsg(LOG_CURRENT, NULL, 3, NULL);
	log_toggle(0, NULL);
	window_display = old_display;
	if (unlink(expand)) 
	{
		bitchsay("Error unlinking: %s", expand);
		new_free(&expand);
		return;
	}
	window_display = 0;
	set_int_var(MSGCOUNT_VAR, 0);
	if (reset_logptr)
		log_toggle(1, NULL);
	window_display = old_display;
	bitchsay("Removed %s.", expand);
	status_update(1);
	new_free(&expand);
}

static int in_read_log = 0;

BUILT_IN_COMMAND(readlog)
{
	char *expand;
	struct	stat	stat_buf;
	char	*filename = NULL;
	char buffer[BIG_BUFFER_SIZE + 1];
	
	read_log_func = fgets;
	if (!get_string_var(MSGLOGFILE_VAR))
		if (!args || (args && !*args))
			return;
	if (msg_window)
		return;
	
	if (command)
	{
		in_read_log = 1;
		if (my_stricmp(command, "MORE"))
		{
			if (args && !my_strnicmp(args, "-resume", 2))
			{
				next_arg(args, &args);
				read_log_func = (char *(*)(char *, int, FILE *))global[RFGETS];
			}
		}
	}		
	if (args && *args)
		malloc_sprintf(&filename, "%s", args);
	else
		malloc_sprintf(&filename, "%s/%s", get_string_var(CTOOLZ_DIR_VAR), get_string_var(MSGLOGFILE_VAR));

	expand = expand_twiddle(filename);
	new_free(&filename);
	stat(expand, &stat_buf);
	strcpy(buffer, expand);
	
	if (stat_buf.st_mode & S_IFDIR)
		return;

	if ((msg_fp = fopen(expand, "r")) == NULL)
	{
		log_put_it(expand, "%s Error Opening Log file %s", thing_ansi, expand);
		new_free(&expand);
		msg_fp = NULL;
		return;
	}
	if (read_log_func == (char *(*)(char *, int, FILE *))global[RFGETS])
		fseek(msg_fp, 0, SEEK_END);
	new_free(&expand);
	msg_window = current_window;
	msg_screen = current_window->screen;
	log_prompt(buffer, NULL);
}

/*
 * show_help:  show's either a page of text from a help_fp, or the whole
 * thing, depending on the value of HELP_PAGER_VAR.  If it gets to the end,
 * (in either case it will eventally), it closes the file, and returns 0
 * to indicate this.
 */ 
static	int show_log(Window *window, char *name)
{
	Window	*old_window;
	int	rows = 0;
	char	line[500];

	if (window)
	{
		old_window = current_window;
		current_window = window;
	}
	else
	{
		old_window = NULL;
		window = current_window;
	}
	if (get_int_var(HELP_PAGER_VAR))
		rows = window->display_size - (window->double_status + 2);
	while (--rows)
	{
 		if ((*read_log_func)(line, 499, msg_fp))
		{
			if (*(line + strlen(line) - 1) == '\n')
			*(line + strlen(line) - 1) = (char) 0;
			#ifdef WANT_HEBREW
			if (get_int_var(HEBREW_TOGGLE_VAR))
				hebrew_process(line);
			#endif
			log_put_it(name, "%s", line);
		}
		else
		{
			if (msg_fp) fclose(msg_fp);
			set_msg_screen(NULL);
			msg_fp = NULL;
			return (0);
		}
	}
	return (1);
}

void remove_away_log(char *stuff, char *line)
{
	if ((line && toupper(*line) == 'Y'))
		remove_log(NULL, NULL, NULL, NULL);
	in_read_log = 0;
}
                         
static	void set_msg_screen(Screen *screen)
{
	msg_screen = screen;
	if (!msg_screen && msg_window)
	{
		if (use_msg_window)
		{
			int display = window_display;

			window_display = 0;
			delete_window(msg_window);
			window_display = display;
		}
		msg_window = NULL;
		update_all_windows();
	}
}

/*
 * log_prompt: The main procedure called to display the log file
 * currently being accessed.  Using add_wait_prompt(), it sets it
 * self up to be recalled when the next page is asked for.   If
 * called when we have finished paging the log file, we exit, as
 * there is nothing left to show.  If line is 'q' or 'Q', exit the
 * log pager, clean up, etc..  If all is cool for now, we call
 * show_help, and either if its finished, exit, or prompt for the
 * next page.   
 */

static	void log_prompt(char *name, char *line)
{

	if (line && *line && (toupper(*line) == 'Q'))
	{
		finished_msg_paging = 1;
		if (msg_fp) 
			fclose(msg_fp);
		msg_fp = NULL;
		set_msg_screen(NULL);
		if (!in_read_log)
			add_wait_prompt("Delete msg log [y/N]? ", remove_away_log, empty_string, WAIT_PROMPT_LINE,1);
		return;
	}

	if (show_log(msg_window, name))
	{
		add_wait_prompt("*** Hit any key for more, 'q' to quit ***",
			log_prompt, name, WAIT_PROMPT_KEY,1);
	}
	else 
	{
		if (msg_fp) 
			fclose(msg_fp);
		set_msg_screen(NULL);
		msg_fp = NULL;
		if (!in_read_log)
			add_wait_prompt("Delete msg log [y/N]? ", remove_away_log, empty_string, WAIT_PROMPT_LINE,1);
	}
}


--- NEW FILE: parse.c ---
/*
 * parse.c: handles messages from the server.   Believe it or not.  I
 * certainly wouldn't if I were you. 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 * Modified Colten Edwards 1997
 */

#include "irc.h"
static char cvsrevision[] = "$Id: parse.c,v 1.1.1.2 2003/06/11 07:00:43 root Exp $";
CVS_REVISION(parse_c)
#include "struct.h"

#include "alias.h"
#include "server.h"
#include "names.h"
#include "vars.h"
[...1889 lines suppressed...]
	 * a live client to see if this is a reasonable order.
	 */
	if ((numeric = atoi(comm)))
		numbered_command(from, numeric, ArgList);
	else
	{
		retval = (protocol_command *)find_fixed_array_item(
			(void *)rfc1459, sizeof(protocol_command), 
			num_protocol_cmds + 1, comm, &cnt, &loc);

		if (cnt < 0 && rfc1459[loc].inbound_handler)
			rfc1459[loc].inbound_handler(from, ArgList);
		else
			rfc1459_odd(from, comm, ArgList);
		rfc1459[loc].bytes += len;
		rfc1459[loc].count++;
	}
	FromUserHost = empty_string;
	from_server = -1;
}

--- NEW FILE: reg.c ---
/*
 * The original was spagetti. I have replaced Michael's code with some of
 * my own which is a thousand times more readable and can also handle '%',
 * which substitutes anything except a space. This should enable people
 * to position things better based on argument. I have also added '?', which
 * substitutes to any single character. And of course it still handles '*'.
 * this should be more efficient than the previous version too.
 *
 * Thus this whole file becomes:
 *
 * Written By Troy Rollo
 * Copyright(c) 1992
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: reg.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(reg_c)
#include "ircaux.h"
#include "output.h"
#define MAIN_SOURCE
#include "modval.h"

/*
 * The following #define is here because we *know* its behaviour.
 * The behaviour of toupper tends to be undefined when it's given
 * a non lower case letter.
 * All the systems supported by IRCII should be ASCII
 */
#define	mkupper(c)	(((c) >= 'a' && (c) <= 'z') ? ((c) - 'a' + 'A') : c)

#if 0
int old_match(const char *pattern, const char *string)
{
	char	type = 0;

	while (*string && *pattern && *pattern != '*' && *pattern != '%')
	{
		if (*pattern == '\\' && pattern[1])
		{
			if (!*++pattern || !(mkupper(*pattern) == mkupper(*string)))
				return 0;
			else
				pattern++, string++, total_explicit++;
			continue;	/* Erf! try $match(\\* *) */
		}

		if (*pattern == '?')
			pattern++, string++;
		else if (mkupper(*pattern) == mkupper(*string))
			pattern++, string++, total_explicit++;
		else
			break;
	}
	if (*pattern == '*' || *pattern == '%')
	{
		type = (*pattern++);
		while (*string)
		{
			if (old_match(pattern, string))
				return 1;
			else if (type == '*' || *string != ' ')
				string++;
			else
				break;
		}
	}

	/* Slurp up any trailing *'s or %'s... */
	if (!*string && (type == '*' || type == '%'))
		while (*pattern && (*pattern == '*' || *pattern == '%'))
			pattern++;

	if (!*string && !*pattern)
		return 1;

	return 0;
}
#endif

int new_match (const unsigned char *pattern, const unsigned char *string)
{
	int		count = 1;
	int 		asterisk = 0;
	int		percent = 0;
	const char	*last_asterisk_point = NULL;
	const char	*last_percent_point = NULL;
	int		last_asterisk_count = 0;
	int		last_percent_count = 0;
	const char	*after_wildcard = NULL;
	int		sanity = 0;
	const char	*old_pattern = pattern, *old_string = string;
	if (x_debug & DEBUG_REGEX_DEBUG)
		yell("Matching [%s] against [%s]", pattern, string);

	for (;;)
	{
		if (sanity++ > 100000)
		{
			yell("Infinite loop in match! pattern = [%s] string = [%s]", old_pattern, old_string);
			return 0;
		}

		/*
		 * If the last character in the pattern was a *, then
		 * we walk the string until we find the next instance int
		 * string, of the character that was after the *.
		 * If we get to the end of string, then obviously there
		 * is no match.  A * at the end of the pattern is handled
		 * especially, so we dont need to consider that.
		 */
		if (asterisk)
		{
			/*
			 * More pattern, no source.  Obviously this
			 * asterisk isnt going to cut it.  Try again.
			 * This replaces an 'always failure' case. 
			 * In 99% of the cases, we will try again and it
			 * will fail anyhow, but 1% of the cases it would
			 * have succeeded, so we need that retry.
			 */
			if (!*string)
				return 0;

			/*
			 * XXXX Skip over any backslashes...
			 */
			if (*pattern == '\\')
			{
				pattern++;
				if (tolower(*string) != tolower(*pattern))
					continue;
			}

			/*
			 * If the character in the pattern immediately
			 * following the asterisk is a qmark, then we
			 * save where we're at and we allow the ? to be
			 * matched.  If we find it doesnt work later on,
			 * then we will come back to here and try again.
			 *     OR 
			 * We've found the character we're looking for!
			 * Save some state information about how to recover
			 * if we dont match
			 */
			else if (*pattern == '?' || 
				(tolower(*string) == tolower(*pattern)))
			{
				asterisk = 0;
				last_asterisk_point = string;
				last_asterisk_count = count;
			}

			/*
			 * This is not the character we're looking for.
			 */
			else
				string++;

			continue;
		}

		/*
		 * Ok.  If we're dealing with a percent, but not a asterisk,
		 * then we need to look for the character after the percent.
		 * BUT, if we find a space, then we stop anyways.
		 */
		if (percent)
		{
			/*
			 * Ran out of string.  If there is more to the 
			 * pattern, then we failed.  Otherwise if the %
			 * was at the end of the pattern, we havent found
			 * a space, so it succeeds!
			 */
			if (!*string)
			{
				if (*pattern)
					return 0;
				else
					return count;
			}

			/*
			 * XXXX Skip over any backslashes...
			 */
			if (*pattern == '\\')
			{
				pattern++;
				if (tolower(*string) != tolower(*pattern))
					continue;
			}

			/*
			 * If we find a space, then we stop looking at the
			 * percent.  We're definitely done with it.  We also
			 * go back to normal parsing mode, presumably with
			 * the space after the %.
			 */
			if (*string == ' ')
			{
				percent = 0;
				last_percent_point = NULL;
			}
   
			/*
			 * If this is not the char we're looking for, then
			 * keep looking.
			 */
			else if (tolower(*string) != tolower(*pattern))
				string++;

			/*
			 * We found it!  Huzzah!
			 */
			else
			{
				percent = 0;
				last_percent_point = string;
				last_percent_count = count;
			}

			continue;
		}


		/*
		 * Ok.  So at this point, we know we're not handling an
		 * outstanding asterisk or percent request.  So we look
		 * to see what the next char is in the pattern and deal
		 * with it.
		 */
		switch (*pattern)
		{

		/*
		 * If its an asterisk, then we just keep some info about
		 * where we're at. 
		 */
		case ('*') : case ('%') :
		{
			asterisk = 0, percent = 0;
			do
			{
				if (*pattern == '*')
					asterisk = 1;
				pattern++;
			}
			while (*pattern == '*' || *pattern == '%');

			after_wildcard = pattern;
			if (asterisk)
			{
				last_asterisk_point = string;
				last_asterisk_count = count;
			}
			else
			{
				percent = 1;
				last_percent_point = string;
				last_percent_count = count;
			}

			/*
			 * If there's nothing in the pattern after the
			 * asterisk, then it slurps up the rest of string,
			 * and we're definitely done!
			 */
			if (asterisk && !*pattern)
				return count;

			break;
		}

		/*
		 * If its a question mark, then we have to slurp up one 
		 * character from the pattern and the string.
		 */
		case ('?') :
		{
			pattern++;

			/*
			 * If there is nothing left in string, then we
			 * definitely fail.
			 */
			if (!*string)
				return 0;
			string++;
			break;
		}

		/*
		 * De-quote any \'s in the pattern.
		 */
		case ('\\') :
		{
			/*
			 * ircII says that a single \ at the end of a pattern
			 * is defined as a failure. (must quote SOMETHING)
			 */
			pattern++;
			if (!*pattern)
				return 0;

			/*
			 * Check to see if the dequoted character and
			 * the next string character are the same.
			 */
			if (tolower(*pattern) != tolower(*string))
				return 0;

			count++, string++, pattern++;
			break;
		}

		/*
		 * If there is nothing left in the pattern and string,
		 * then we've definitely succeeded.  Return the number of
		 * non-wildcard characters.
		 */
		default:
		{
			if (!*pattern && !*string)
				return count;

			/*
			 * There are regular characters next in the pattern 
			 * and string.  Are they the same?  If they are, walk 
			 * past them and go to the next character.
			 */
			if (tolower(*pattern) == tolower(*string))
			{
				count++, pattern++, string++;
			}

			/*
			 * The two characters are not the same.  If we're 
			 * currently trying to match a wildcard, go back to 
			 * where we started after the wildcard and try looking
			 * again from there.  If we are not currently matching
			 * a wildcard, then the entire match definitely fails.
			 */
			else if (last_asterisk_point)
			{
                                asterisk = 1;
                                string = last_asterisk_point + 1;
                                pattern = after_wildcard;
                                count = last_asterisk_count;
			}
			else if (last_percent_point)
			{
                                percent = 1;
                                string = last_percent_point + 1;
                                pattern = after_wildcard;
                                count = last_percent_count;
			}
			else
				return 0;

			break;
		}
		}
	}

	return 0;
}

/*
 * wild_match: calculate the "value" of str when matched against pattern.
 * The "value" of a string is always zero if it is not matched by the pattern.
 * In all cases where the string is matched by the pattern, then the "value"
 * of the match is 1 plus the number of non-wildcard characters in "str".
 *
 * \\[ and \\] handling done by Jeremy Nelson
 */
int BX_wild_match (register const unsigned char *p, register const unsigned char *str)
{
	/*
	 * Is there a \[ in the pattern to be expanded? 
	 * 
	 * This stuff here just reduces the \[ \] set into a series of
	 * one-simpler patterns and then recurses over the options.
	 */
	if (strstr(p, "\\["))
	{
		char *pattern, *ptr, *ptr2, *arg, *placeholder;
		int nest = 0;

		/*
		 * Only make the copy if we're going to be tearing it apart.
		 */
		pattern = LOCAL_COPY(p);

		/*
		 * We will have to null this out, but not until we've used it
		 */
		placeholder = ptr = ptr2 = strstr(pattern, "\\[");

		/*
		 * Look for the matching \].
		 */
		do
		{
			switch (ptr[1]) 
			{
					/* step over it and add to nest */
				case '[' :  ptr2 = ptr + 2 ;
					    nest++;
					    break;
					/* step over it and remove nest */
				case ']' :  ptr2 = ptr + 2;
					    nest--;
					    break;
				default:
					    ptr2 = ptr + 2;
					    break;
			}
		}
		while (nest && (ptr = strchr(ptr2, '\\')));

		/*
		 * Right now, we know that ptr points to a \] or to a NULL.
		 * Remember that '&&' short circuits and that ptr will
		 * not be set to NULL if (nest) is zero.
		 */
		if (ptr)
		{
			int best_total = 0;

			*ptr = 0;
			ptr += 2;
			*placeholder = 0;
			placeholder += 2;

			/* 
			 * grab words ("" sets or space words) one at a time
			 * and attempt to match all of them.  The best value
			 * matched is the one used.
			 */
			while ((arg = new_next_arg(placeholder, &placeholder)))
			{
				int tmpval;
				char my_buff[BIG_BUFFER_SIZE + 1];

				strlcpy(my_buff, pattern, BIG_BUFFER_SIZE);
				strlcat(my_buff, arg, BIG_BUFFER_SIZE);
				strlcat(my_buff, ptr, BIG_BUFFER_SIZE);

				/*
				 * The total_explicit we return is whatever
				 * sub-pattern has the highest total_explicit
				 */
				if ((tmpval = wild_match(my_buff, str)))
				{
					if (tmpval > best_total)
						best_total = tmpval;
				}
			}

			return best_total; /* end of expansion section */
		}

		/*
		 * Possibly an unmatched \[ \] set.  Just wing it.
		 */
		else
			return new_match(pattern, str);
	}

	/*
	 * Trivial case -- No \[ \] sets, just do the match.
	 */
	else
		return new_match(p, str);
}


--- NEW FILE: cdrom.c ---
/*
 * cdrom.c: This file handles all the CDROM routines, in BitchX
 *
 * Written by Tom Zickel
 * a.k.a. IceBreak on the irc
 *
 * Copyright(c) 1996
 * Modified Colten Edwards aka panasync.
 *
 */

/* Version 0.2 22/07/97 written by IceBreak (ice_break at hotmail.com) */
/* 0.2: Finnally I rewrote the playing method inside the cdrom player to work
 *      with msf instead of lba, and frames instead of tracks, this should fix
 *      all the compability problems that the cdroms player had with some
 *      cdroms. I also fixed some bugs and better problem messages.
 * FreeBSD support added by Eric A. Griff aka setjmp!eagriff@*.global2000.net
 *
*/

#include "irc.h"
static char cvsrevision[] = "$Id: cdrom.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(cdrom_c)
#include "struct.h"

#include "ircaux.h"
#include "cdrom.h"
#include "output.h"
#include "misc.h"
#include "vars.h"
#define MAIN_SOURCE
#include "modval.h"

#define cparse(s) convert_output_format(s, NULL, NULL)

static int drive = 0;

static char cdrom_prompt[]="%gC%Gd%gROM%w";

#ifndef __FreeBSD__
static struct cdrom_tochdr hdr;
static struct cdrom_ti ti;
#else
static struct ioc_toc_header hdr;
#endif

static struct cdrom_etocentry TocEntry[101];

int cd_init(char *);

#ifndef __FreeBSD__
void play_chunk(int start, int end)
{
	struct cdrom_msf msf;

	end--;
	if (start >= end)
		start = end-1;

	msf.cdmsf_min0 = start / (60*75);
	msf.cdmsf_sec0 = (start % (60*75)) / 75;
	msf.cdmsf_frame0 = start % 75;
	msf.cdmsf_min1 = end / (60*75);
	msf.cdmsf_sec1 = (end % (60*75)) / 75;
	msf.cdmsf_frame1 = end % 75;

	if (ioctl(drive, CDROMSTART))
	{
		put_it("%s: Could not start the cdrom",cparse(cdrom_prompt));
		return;
	}
	if (ioctl(drive, CDROMPLAYMSF, &msf))
	{
		put_it("%s: Could not play the track",cparse(cdrom_prompt));
		return;
	}
}
#endif

static int check_cdrom_str(void)
{
char *str;
	if ((str = get_string_var(CD_DEVICE_VAR)))
	{
		if (!drive)
			cd_init(str);
		return 1;
	}
	put_it("%s: /SET CD_DEVICE  - The name of the CDROM device",cparse(cdrom_prompt));
	return 0;
}

int cd_init(char *dev)
{
#if 0
unsigned char msf_ent[3];
unsigned char msf1_ent[3];
#endif
int i, pos;

	if (((drive = open(dev, 0)) < 0))
	{
		if (errno == EACCES)
			put_it("%s: you dont have access to the cdrom driver",cparse(cdrom_prompt));
		return (-1);
	}

	if (ioctl(drive, CDROMREADTOCHDR, &hdr))
	{
		put_it("%s: Can't get TocHeader",cparse(cdrom_prompt));
		return (-2);
	}

#ifndef __FreeBSD__
	for (i=1;i<=hdr.cdth_trk1+1;i++)
	{
		if (i!=hdr.cdth_trk1+1) 
			TocEntry[i].cdte_track = i;
		else 
			TocEntry[i].cdte_track = CDROM_LEADOUT;
		TocEntry[i].cdte_format = CDROM_MSF;
		if (ioctl(drive,CDROMREADTOCENTRY,&TocEntry[i]))
			put_it("%s: Can't get TocEntry #%d",cparse(cdrom_prompt), i);
		else
		{
			TocEntry[i].avoid=TocEntry[i].cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
			TocEntry[i].m_length = TocEntry[i].cdte_addr.msf.minute * 60 + TocEntry[i].cdte_addr.msf.second;
			TocEntry[i].m_start = TocEntry[i].m_length * 75 + TocEntry[i].cdte_addr.msf.frame;
		}
	}

	pos = TocEntry[1].m_length;

	for (i=1;i<=hdr.cdth_trk1+1;i++)
	{
		TocEntry[i].m_length = TocEntry[i+1].m_length - pos;
		pos = TocEntry[i+1].m_length;
		if (TocEntry[i].avoid)
			TocEntry[i].m_length = (TocEntry[i+1].m_start - TocEntry[i+1].m_start) *2;
	}
	return (hdr.cdth_trk1);
#else
	for (i = hdr.starting_track; i <= hdr.ending_track; i++)
	{
		TocEntry[i].avoid=0;
		TocEntry[i].m_start=1;
		TocEntry[i].m_length=1;
	}
	return (hdr.ending_track);
#endif
}

static int check_mount(char *device)
{
#ifndef __FreeBSD__
FILE *fp;
struct mntent *mnt;

	if ((fp = setmntent(MOUNTED, "r")) == NULL)
		return 0;
	
	while ((mnt = getmntent (fp)) != NULL)
	{
		if (!strcmp (mnt->mnt_type, "iso9660") && !strcmp (mnt->mnt_fsname, device))
		{
			endmntent(fp);
			return 0;
		}
	}
	endmntent (fp);
#else
struct statfs *mntinfo;
int i,count;

	if(!(count=getmntinfo(&mntinfo,MNT_WAIT|MOUNT_CD9660)))
		return 0;
	
	for(i=0; i<count; i++)
		if(strstr(mntinfo[i].f_mntfromname,device))
			return 0;
#endif
	return 1;
}

void set_cd_device(Window *win, char *str, int blah)
{
	if (drive) 
  		close(drive);
	if (!str || !check_mount(str))
	{
		put_it("%s: ERROR: CDROM is already mounted, please unmount, and try again",cparse(cdrom_prompt));
		set_string_var(CD_DEVICE_VAR,NULL);
		return;
	}

	if (cd_init(str) < 0)
	{
		put_it("%s: ERROR: Could not initalize the CDROM, check if a disk is inside",cparse(cdrom_prompt));
		set_string_var(CD_DEVICE_VAR,NULL);
		return;
	}
	put_it("%s: CDROM device is now set to - %s",cparse(cdrom_prompt),str);
	set_string_var(CD_DEVICE_VAR,str);
}

BUILT_IN_COMMAND(cd_stop)
{
	if (!check_cdrom_str())
		return;

	if (ioctl(drive,CDROMSTOP))
		put_it("%s: Couldnt stop the cdrom",cparse(cdrom_prompt));
	else
		put_it("%s: Stopped playing cdrom",cparse(cdrom_prompt));
}

BUILT_IN_COMMAND(cd_eject)
{
	if (!check_cdrom_str())
		return;

	if (!drive)
		return;
	if (ioctl(drive,CDROMEJECT))
		put_it("%s: Couldnt eject the cdrom tray",cparse(cdrom_prompt));
	else
		put_it("%s: Ejected cdrom tray",cparse(cdrom_prompt));
	close(drive);
	drive=0;
}

BUILT_IN_COMMAND(cd_play)
{
int tn;
char *trackn;

#ifndef __FreeBSD__
unsigned char first, last;
struct cdrom_tochdr tocHdr;
#else
struct ioc_play_track cdrom_play_args;
int result;
#endif	

	if (!check_cdrom_str() || !drive)
		return;
	
	if (args && *args)
	{
		trackn = next_arg(args, &args);
		tn = atoi(trackn);

#ifndef __FreeBSD__
	        if (ioctl(drive,CDROMREADTOCHDR,&tocHdr))
	        {
	        	put_it("%s: Couldnt get cdrom heder",cparse(cdrom_prompt));
	        	return;
	        }

	        first = tocHdr.cdth_trk0;
	        last = tocHdr.cdth_trk1;
	        ti.cdti_trk0=tn;

	        if (ti.cdti_trk0<first) 
	        	ti.cdti_trk0=first;
	        if (ti.cdti_trk0>last) 
	        	ti.cdti_trk0=last;

	        ti.cdti_ind0=0;
	        ti.cdti_trk1=last;
	        ti.cdti_ind1=0;
#else
		if (tn < hdr.starting_track)
			tn=hdr.starting_track;
		if (tn > hdr.ending_track)
 			tn=hdr.ending_track;
#endif

	        if (TocEntry[tn].avoid==0)
	        {
#ifndef __FreeBSD__
			play_chunk(TocEntry[tn].m_start,TocEntry[last+1].m_start - 1);
#else
			cdrom_play_args.start_track=tn;
			cdrom_play_args.start_index=1;
			cdrom_play_args.end_track=hdr.ending_track;
			cdrom_play_args.end_index=1;
			(void)ioctl(drive,CDIOCPLAYTRACKS,&cdrom_play_args);
#endif
		        put_it("%s: Playing track number #%d",cparse(cdrom_prompt),tn);
	        }
	        else
	        	put_it("%s: Cannot play track #%d (Might be data track)",cparse(cdrom_prompt),tn);
	}
        else
	        put_it("%s: Usage: /cdplay <track number>",cparse(cdrom_prompt));

}

BUILT_IN_COMMAND(cd_list)
{
int i;

	if (!check_cdrom_str())
		return;
#ifndef __FreeBSD__
	for (i=1;i<=hdr.cdth_trk1;i++)
#else
	for (i = hdr.starting_track; i < hdr.ending_track; i++)
#endif
	{
		put_it("%s: Track #%02d: %02d:%02d:%02d %02d:%02d:%02d",
			cparse(cdrom_prompt),
			i,
			TocEntry[i].m_length / (60*75),
			(TocEntry[i].m_length % (60*75)) / 75,
			TocEntry[i].m_length % 75,
			TocEntry[i].m_start / (60*75),
			(TocEntry[i].m_start % (60*75)) /75,
			TocEntry[i].m_start % 75
			);
	}
}

BUILT_IN_COMMAND(cd_volume)
{
char *left, *right;
#ifndef __FreeBSD__
struct cdrom_volctrl volctrl;
#else
struct ioc_vol volctrl;
#endif

	if (!check_cdrom_str())
		return;

	if (args && *args)
	{
		left=next_arg(args, &args);
		right=next_arg(args, &args);
		ioctl(drive, CDROMVOLREAD, &volctrl);
		if (left && *left)
#ifndef __FreeBSD__
			volctrl.channel0 = atoi(left);
#else
			volctrl.vol[0] = atoi(left);
#endif
		if (right && *right)
#ifndef __FreeBSD__
			volctrl.channel1 = atoi(right);
#else
			volctrl.vol[1] = atoi(right);
#endif
		if (ioctl(drive,CDROMVOLCTRL,&volctrl))
			put_it("%s: Couldnt set cdrom volume",cparse(cdrom_prompt));
		else
			put_it("%s: CDROM Volume is now <%d> <%d>",cparse(cdrom_prompt),
#ifndef __FreeBSD__			
			volctrl.channel0,volctrl.channel1);
#else
			volctrl.vol[0],volctrl.vol[1]);
#endif
	}
	else
		put_it("%s: Usage: /cdvol <left> <right>",cparse(cdrom_prompt));
}

BUILT_IN_COMMAND(cd_pause)
{
static int cpause = 0;
	if (!check_cdrom_str())
		return;
	if (ioctl(drive, !cpause?CDROMPAUSE:CDROMRESUME))
		put_it("%s: Couldnt pause/resume your cdrom",cparse(cdrom_prompt));
	else
		put_it("%s: %s",cparse(cdrom_prompt),!cpause?"Your cdrom has been paused":"Your cdrom has been resumed");
	cpause ^= 1;
}

BUILT_IN_COMMAND(cd_help)
{
	put_it("%s: CDPLAY            - Play a CDROM Track Number",cparse(cdrom_prompt));
	put_it("%s: CDSTOP            - Make the CDROM Stop playing",cparse(cdrom_prompt));
	put_it("%s: CDEJECT           - Eject the CDROM Tray",cparse(cdrom_prompt));
	put_it("%s: CDVOL             - Set's the CDROM Volume",cparse(cdrom_prompt));
	put_it("%s: CDLIST            - List of CDROM tracks",cparse(cdrom_prompt));
	put_it("%s: CDPAUSE           - Pause/resume the CDROM",cparse(cdrom_prompt));
}

#if 0
char	*status_cdrom(window)
	Window	*window;
{
	static	char	text[BIG_BUFFER_SIZE];

	if ((drive) && (get_string_var(CD_DEVICE_VAR)))
	{
		rc=ioctl(drive,CDROMSUBCHNL,&subchnl);
		strcpy(text,"[CD:");
		if (subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY)
		{
			strcat(text,"P");
			strcat(text,":");
			strcat(text,ltoa((long)subchnl.cdsc_trk));
		} else {
			strcat(text,"S");
		}
		strcat(text,"]");
		return(text);
	}
	else
		return (empty_string);
}
#endif

--- NEW FILE: alloca.c ---
/* alloca.c -- allocate automatically reclaimed memory
   (Mostly) portable public-domain implementation -- D A Gwyn

   This implementation of the PWB library alloca function,
   which is used to allocate space off the run-time stack so
   that it is automatically reclaimed upon procedure exit,
   was inspired by discussions with J. Q. Johnson of Cornell.
   J.Otto Tennant <jot at cray.com> contributed the Cray support.

   There are some preprocessor constants that can
   be defined when compiling for your specific system, for
   improved efficiency; however, the defaults should be okay.

   The general concept of this implementation is to keep
   track of all alloca-allocated blocks, and reclaim any
   that are found to be deeper in the stack than the current
   invocation.  This heuristic does not reclaim storage as
   soon as it becomes invalid, but it will do so eventually.

   As a special case, alloca(0) reclaims storage without
   allocating any.  It is a good idea to use alloca(0) in
   your main control loop, etc. to force garbage collection.  */

/* If compiling with GCC 2, this file's not needed.  */
#if !defined (__GNUC__) || __GNUC__ < 2

/* If someone has defined alloca as a macro,
   there must be some other way alloca is supposed to work.  */
#ifndef alloca

/* If your stack is a linked list of frames, you have to
   provide an "address metric" ADDRESS_FUNCTION macro.  */

#if defined (CRAY) && defined (CRAY_STACKSEG_END)
long i00afunc ();
#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
#else
#define ADDRESS_FUNCTION(arg) &(arg)
#endif

typedef void *pointer;

#define	NULL	0

/* Different portions of Emacs need to call different versions of
   malloc.  The Emacs executable needs alloca to call xmalloc, because
   ordinary malloc isn't protected from input signals.  On the other
   hand, the utilities in lib-src need alloca to call malloc; some of
   them are very simple, and don't have an xmalloc routine.

   Non-Emacs programs expect this to call use xmalloc.

   Callers below should use malloc.  */

#define malloc xmalloc
extern pointer malloc ();

/* Define STACK_DIRECTION if you know the direction of stack
   growth for your system; otherwise it will be automatically
   deduced at run-time.

   STACK_DIRECTION > 0 => grows toward higher addresses
   STACK_DIRECTION < 0 => grows toward lower addresses
   STACK_DIRECTION = 0 => direction of growth unknown  */

#ifndef STACK_DIRECTION
#define	STACK_DIRECTION	0	/* Direction unknown.  */
#endif

#if STACK_DIRECTION != 0

#define	STACK_DIR	STACK_DIRECTION	/* Known at compile-time.  */

#else /* STACK_DIRECTION == 0; need run-time code.  */

static int stack_dir;		/* 1 or -1 once known.  */
#define	STACK_DIR	stack_dir

static void
find_stack_direction ()
{
  static char *addr = NULL;	/* Address of first `dummy', once known.  */
  auto char dummy;		/* To get stack address.  */

  if (addr == NULL)
    {				/* Initial entry.  */
      addr = ADDRESS_FUNCTION (dummy);

      find_stack_direction ();	/* Recurse once.  */
    }
  else
    {
      /* Second entry.  */
      if (ADDRESS_FUNCTION (dummy) > addr)
	stack_dir = 1;		/* Stack grew upward.  */
      else
	stack_dir = -1;		/* Stack grew downward.  */
    }
}

#endif /* STACK_DIRECTION == 0 */

/* An "alloca header" is used to:
   (a) chain together all alloca'ed blocks;
   (b) keep track of stack depth.

   It is very important that sizeof(header) agree with malloc
   alignment chunk size.  The following default should work okay.  */

#ifndef	ALIGN_SIZE
#define	ALIGN_SIZE	sizeof(double)
#endif

typedef union hdr
{
  char align[ALIGN_SIZE];	/* To force sizeof(header).  */
  struct
    {
      union hdr *next;		/* For chaining headers.  */
      char *deep;		/* For stack depth measure.  */
    } h;
} header;

static header *last_alloca_header = NULL;	/* -> last alloca header.  */

/* Return a pointer to at least SIZE bytes of storage,
   which will be automatically reclaimed upon exit from
   the procedure that called alloca.  Originally, this space
   was supposed to be taken from the current stack frame of the
   caller, but that method cannot be made to work for some
   implementations of C, for example under Gould's UTX/32.  */

pointer
alloca (size)
     unsigned size;
{
  auto char probe;		/* Probes stack depth: */
  register char *depth = ADDRESS_FUNCTION (probe);

#if STACK_DIRECTION == 0
  if (STACK_DIR == 0)		/* Unknown growth direction.  */
    find_stack_direction ();
#endif

  /* Reclaim garbage, defined as all alloca'd storage that
     was allocated from deeper in the stack than currently. */

  {
    register header *hp;	/* Traverses linked list.  */

    for (hp = last_alloca_header; hp != NULL;)
      if ((STACK_DIR > 0 && hp->h.deep > depth)
	  || (STACK_DIR < 0 && hp->h.deep < depth))
	{
	  register header *np = hp->h.next;

	  free ((pointer) hp);	/* Collect garbage.  */

	  hp = np;		/* -> next header.  */
	}
      else
	break;			/* Rest are not deeper.  */

    last_alloca_header = hp;	/* -> last valid storage.  */
  }

  if (size == 0)
    return NULL;		/* No allocation required.  */

  /* Allocate combined header + user data storage.  */

  {
    register pointer new = malloc (sizeof (header) + size);
    /* Address of header.  */

    ((header *) new)->h.next = last_alloca_header;
    ((header *) new)->h.deep = depth;

    last_alloca_header = (header *) new;

    /* User storage begins just after header.  */

    return (pointer) ((char *) new + sizeof (header));
  }
}

#if defined (CRAY) && defined (CRAY_STACKSEG_END)

#ifdef DEBUG_I00AFUNC
#include <stdio.h>
#endif

#ifndef CRAY_STACK
#define CRAY_STACK
#ifndef CRAY2
/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
struct stack_control_header
  {
    long shgrow:32;		/* Number of times stack has grown.  */
    long shaseg:32;		/* Size of increments to stack.  */
    long shhwm:32;		/* High water mark of stack.  */
    long shsize:32;		/* Current size of stack (all segments).  */
  };

/* The stack segment linkage control information occurs at
   the high-address end of a stack segment.  (The stack
   grows from low addresses to high addresses.)  The initial
   part of the stack segment linkage control information is
   0200 (octal) words.  This provides for register storage
   for the routine which overflows the stack.  */

struct stack_segment_linkage
  {
    long ss[0200];		/* 0200 overflow words.  */
    long sssize:32;		/* Number of words in this segment.  */
    long ssbase:32;		/* Offset to stack base.  */
    long:32;
    long sspseg:32;		/* Offset to linkage control of previous
				   segment of stack.  */
    long:32;
    long sstcpt:32;		/* Pointer to task common address block.  */
    long sscsnm;		/* Private control structure number for
				   microtasking.  */
    long ssusr1;		/* Reserved for user.  */
    long ssusr2;		/* Reserved for user.  */
    long sstpid;		/* Process ID for pid based multi-tasking.  */
    long ssgvup;		/* Pointer to multitasking thread giveup.  */
    long sscray[7];		/* Reserved for Cray Research.  */
    long ssa0;
    long ssa1;
    long ssa2;
    long ssa3;
    long ssa4;
    long ssa5;
    long ssa6;
    long ssa7;
    long sss0;
    long sss1;
    long sss2;
    long sss3;
    long sss4;
    long sss5;
    long sss6;
    long sss7;
  };

#else /* CRAY2 */
/* The following structure defines the vector of words
   returned by the STKSTAT library routine.  */
struct stk_stat
  {
    long now;			/* Current total stack size.  */
    long maxc;			/* Amount of contiguous space which would
				   be required to satisfy the maximum
				   stack demand to date.  */
    long high_water;		/* Stack high-water mark.  */
    long overflows;		/* Number of stack overflow ($STKOFEN) calls.  */
    long hits;			/* Number of internal buffer hits.  */
    long extends;		/* Number of block extensions.  */
    long stko_mallocs;		/* Block allocations by $STKOFEN.  */
    long underflows;		/* Number of stack underflow calls ($STKRETN).  */
    long stko_free;		/* Number of deallocations by $STKRETN.  */
    long stkm_free;		/* Number of deallocations by $STKMRET.  */
    long segments;		/* Current number of stack segments.  */
    long maxs;			/* Maximum number of stack segments so far.  */
    long pad_size;		/* Stack pad size.  */
    long current_address;	/* Current stack segment address.  */
    long current_size;		/* Current stack segment size.  This
				   number is actually corrupted by STKSTAT to
				   include the fifteen word trailer area.  */
    long initial_address;	/* Address of initial segment.  */
    long initial_size;		/* Size of initial segment.  */
  };

/* The following structure describes the data structure which trails
   any stack segment.  I think that the description in 'asdef' is
   out of date.  I only describe the parts that I am sure about.  */

struct stk_trailer
  {
    long this_address;		/* Address of this block.  */
    long this_size;		/* Size of this block (does not include
				   this trailer).  */
    long unknown2;
    long unknown3;
    long link;			/* Address of trailer block of previous
				   segment.  */
    long unknown5;
    long unknown6;
    long unknown7;
    long unknown8;
    long unknown9;
    long unknown10;
    long unknown11;
    long unknown12;
    long unknown13;
    long unknown14;
  };

#endif /* CRAY2 */
#endif /* not CRAY_STACK */

#ifdef CRAY2
/* Determine a "stack measure" for an arbitrary ADDRESS.
   I doubt that "lint" will like this much. */

static long
i00afunc (long *address)
{
  struct stk_stat status;
  struct stk_trailer *trailer;
  long *block, size;
  long result = 0;

  /* We want to iterate through all of the segments.  The first
     step is to get the stack status structure.  We could do this
     more quickly and more directly, perhaps, by referencing the
     $LM00 common block, but I know that this works.  */

  STKSTAT (&status);

  /* Set up the iteration.  */

  trailer = (struct stk_trailer *) (status.current_address
				    + status.current_size
				    - 15);

  /* There must be at least one stack segment.  Therefore it is
     a fatal error if "trailer" is null.  */

  if (trailer == 0)
    abort ();

  /* Discard segments that do not contain our argument address.  */

  while (trailer != 0)
    {
      block = (long *) trailer->this_address;
      size = trailer->this_size;
      if (block == 0 || size == 0)
	abort ();
      trailer = (struct stk_trailer *) trailer->link;
      if ((block <= address) && (address < (block + size)))
	break;
    }

  /* Set the result to the offset in this segment and add the sizes
     of all predecessor segments.  */

  result = address - block;

  if (trailer == 0)
    {
      return result;
    }

  do
    {
      if (trailer->this_size <= 0)
	abort ();
      result += trailer->this_size;
      trailer = (struct stk_trailer *) trailer->link;
    }
  while (trailer != 0);

  /* We are done.  Note that if you present a bogus address (one
     not in any segment), you will get a different number back, formed
     from subtracting the address of the first block.  This is probably
     not what you want.  */

  return (result);
}

#else /* not CRAY2 */
/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
   Determine the number of the cell within the stack,
   given the address of the cell.  The purpose of this
   routine is to linearize, in some sense, stack addresses
   for alloca.  */

static long
i00afunc (long address)
{
  long stkl = 0;

  long size, pseg, this_segment, stack;
  long result = 0;

  struct stack_segment_linkage *ssptr;

  /* Register B67 contains the address of the end of the
     current stack segment.  If you (as a subprogram) store
     your registers on the stack and find that you are past
     the contents of B67, you have overflowed the segment.

     B67 also points to the stack segment linkage control
     area, which is what we are really interested in.  */

  stkl = CRAY_STACKSEG_END ();
  ssptr = (struct stack_segment_linkage *) stkl;

  /* If one subtracts 'size' from the end of the segment,
     one has the address of the first word of the segment.

     If this is not the first segment, 'pseg' will be
     nonzero.  */

  pseg = ssptr->sspseg;
  size = ssptr->sssize;

  this_segment = stkl - size;

  /* It is possible that calling this routine itself caused
     a stack overflow.  Discard stack segments which do not
     contain the target address.  */

  while (!(this_segment <= address && address <= stkl))
    {
#ifdef DEBUG_I00AFUNC
      fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
#endif
      if (pseg == 0)
	break;
      stkl = stkl - pseg;
      ssptr = (struct stack_segment_linkage *) stkl;
      size = ssptr->sssize;
      pseg = ssptr->sspseg;
      this_segment = stkl - size;
    }

  result = address - this_segment;

  /* If you subtract pseg from the current end of the stack,
     you get the address of the previous stack segment's end.
     This seems a little convoluted to me, but I'll bet you save
     a cycle somewhere.  */

  while (pseg != 0)
    {
#ifdef DEBUG_I00AFUNC
      fprintf (stderr, "%011o %011o\n", pseg, size);
#endif
      stkl = stkl - pseg;
      ssptr = (struct stack_segment_linkage *) stkl;
      size = ssptr->sssize;
      pseg = ssptr->sspseg;
      result += size;
    }
  return (result);
}

#endif /* not CRAY2 */
#endif /* CRAY */

#endif /* no alloca */
#endif /* not GCC version 2 */

--- NEW FILE: lastlog.c ---
/*
 * lastlog.c: handles the lastlog features of irc. 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: lastlog.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(lastlog_c)
#include "struct.h"

#include "lastlog.h"
#include "window.h"
#include "screen.h"
#include "vars.h"
#include "ircaux.h"
#include "output.h"
#include "misc.h"
#include "hook.h"
#include "status.h"
#define MAIN_SOURCE
#include "modval.h"

extern	int	grab_http (char *, char *, char *);
/*
 * lastlog_level: current bitmap setting of which things should be stored in
 * the lastlog.  The LOG_MSG, LOG_NOTICE, etc., defines tell more about this 
 */
static	unsigned long	lastlog_level;
static	unsigned long	notify_level;

static	unsigned long	msglog_level = 0;
	unsigned long	beep_on_level = 0;
	unsigned long	new_server_lastlog_level = 0;
	unsigned long	current_window_level = 0;
	
	
FILE	*logptr = NULL;

/*
 * msg_level: the mask for the current message level.  What?  Did he really
 * say that?  This is set in the set_lastlog_msg_level() routine as it
 * compared to the lastlog_level variable to see if what ever is being added
 * should actually be added 
 */
static	unsigned long	msg_level = LOG_CRAP;

static	char	*levels[] =
{
	"CRAP",		"PUBLIC",	"MSGS",		"NOTICES",
	"WALLS",	"WALLOPS",	"NOTES",	"OPNOTES",
	"SNOTES",	"ACTIONS",	"DCC",		"CTCP",
	"USERLOG1",	"USERLOG2",	"USERLOG3",	"USERLOG4",
	"USERLOG5",	"BEEP",		"TCL",		"SEND_MSG", 
	"KILL",		"MODEUSER",	"MODECHAN",	"KICK", 
	"KICKUSER",	"PARTS",	"INVITES",	"JOIN",
	"TOPIC",	"HELP",		"NOTIFY",	"DEBUG"
};

#define NUMBER_OF_LEVELS (sizeof(levels) / sizeof(char *))


void reset_hold_mode(Window *win)
{
	win->hold_mode = win->save_hold_mode;
	win->save_hold_mode = win->in_more = 0;
}

/* set_lastlog_msg_level: sets the message level for recording in the lastlog */
unsigned long BX_set_lastlog_msg_level(unsigned long level)
{
	unsigned long	old;

	old = msg_level;
	msg_level = level;
	return (old);
}

/*
 * bits_to_lastlog_level: converts the bitmap of lastlog levels into a nice
 * string format.  Note that this uses the global buffer, so watch out 
 */
char	* bits_to_lastlog_level(unsigned long level)
{
	static	char	buffer[481]; /* this *should* be enough for this */
	int	i;
unsigned long	p;

	if (level == LOG_ALL)
		strcpy(buffer, "ALL");
	else if (level == 0)
		strcpy(buffer, "NONE");
	else
	{
		*buffer = '\0';
		for (i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p <<= 1)
		{
			if (level & p)
			{
				if (*buffer)
					strmcat(buffer, space, 480);
				strmcat(buffer, levels[i],480);
			}
		}
	}
	return (buffer);
}

unsigned long parse_lastlog_level(char *str, int display)
{
	char	*ptr,
		*rest;
	int	len,
		i;
unsigned long	p,
		level;
	int	neg;

	level = 0;
	while ((str = next_arg(str, &rest)) != NULL)
	{
		while (str)
		{
			if ((ptr = strchr(str, ',')) != NULL)
				*ptr++ = '\0';
			if ((len = strlen(str)) != 0)
			{
				if (my_strnicmp(str, "ALL", len) == 0)
					level = LOG_ALL;
				else if (my_strnicmp(str, "NONE", len) == 0)
					level = 0;
				else
				{
					if (*str == '-')
					{
						str++; len--;
						neg = 1;
					}
					else
						neg = 0;
					for (i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p <<= 1)
					{
						if (!my_strnicmp(str, levels[i], len))
						{
							if (neg)
								level &= (LOG_ALL ^ p);
							else
								level |= p;
							break;
						}
					}
					if (i == NUMBER_OF_LEVELS)
					{
						if (display)
							say("Unknown lastlog level: %s", str);
						return LOG_ALL;
					}
				}
			}
			str = ptr;
		}
		str = rest;
	}
	return (level);
}

/*
 * set_lastlog_level: called whenever a "SET LASTLOG_LEVEL" is done.  It
 * parses the settings and sets the lastlog_level variable appropriately.  It
 * also rewrites the LASTLOG_LEVEL variable to make it look nice 
 */
void set_lastlog_level(Window *win, char *str, int unused)
{
	lastlog_level = parse_lastlog_level(str, 1);
	set_string_var(LASTLOG_LEVEL_VAR, bits_to_lastlog_level(lastlog_level));
	current_window->lastlog_level = lastlog_level;
}

/*
 * set_msglog_level: called whenever a "SET MSGLOG_LEVEL" is done.  It
 * parses the settings and sets the msglog_level variable appropriately.  It
 * also rewrites the MSGLOG_LEVEL variable to make it look nice 
 */
void set_msglog_level(Window *win, char *str, int unused)
{
	msglog_level = parse_lastlog_level(str, 1);
	set_string_var(MSGLOG_LEVEL_VAR, bits_to_lastlog_level(msglog_level));
}

void	set_new_server_lastlog_level (Window *win, char *str, int unused)
{
	new_server_lastlog_level = parse_lastlog_level(str, 1);
	set_string_var(NEW_SERVER_LASTLOG_LEVEL_VAR, 
			bits_to_lastlog_level(new_server_lastlog_level));
}

void remove_from_lastlog(Window *window)
{
	Lastlog *tmp, *end_holder;

	if (window->lastlog_tail)
	{
		end_holder = window->lastlog_tail;
		tmp = window->lastlog_tail->prev;
		window->lastlog_tail = tmp;
		if (tmp)
			tmp->next = NULL;
		else
			window->lastlog_head = window->lastlog_tail;
		window->lastlog_size--;
		new_free(&end_holder->msg);
		new_free((char **)&end_holder);
	}
	else
		window->lastlog_size = 0;
}

/*
 * set_lastlog_size: sets up a lastlog buffer of size given.  If the lastlog
 * has gotten larger than it was before, all previous lastlog entry remain.
 * If it get smaller, some are deleted from the end. 
 */
void set_lastlog_size(Window *win_unused, char *unused, int size)
{
	int	i,
		diff;
	Window	*win = NULL;

	while ((traverse_all_windows(&win)))
	{
		if (win->lastlog_size > size)
		{
			diff = win->lastlog_size - size;
			for (i = 0; i < diff; i++)
				remove_from_lastlog(win);
		}
		win->lastlog_max = size;
	}
}

void free_lastlog(Window *win)
{
Lastlog *ptr;
	for (ptr = win->lastlog_head; ptr;)
	{
		Lastlog *next = ptr->next;
		new_free(&ptr->msg);
		new_free(&ptr);
		ptr = next;
	}
	win->lastlog_head = NULL;
	win->lastlog_tail = NULL;
	win->lastlog_size = 0;
}
/*
 * lastlog: the /LASTLOG command.  Displays the lastlog to the screen. If
 * args contains a valid integer, only that many lastlog entries are shown
 * (if the value is less than lastlog_size), otherwise the entire lastlog is
 * displayed 
 */
BUILT_IN_COMMAND(lastlog)
{
	int	cnt,
		from = 0,
		p,
		i,
		level = 0,
		msg_level,
		len,
		mask = 0,
		header = 1,
		lines = 0,
		reverse = 0,
		time_log = 1,
		remove = 0;

	Lastlog *start_pos;
	char	*match = NULL,
		*arg;
	char	*file_open[] = { "wt", "at" };
	int	file_open_type = 0;
	char	*blah = NULL;
	FILE	*fp = NULL;
	
	reset_display_target();
	cnt = current_window->lastlog_size;

	while ((arg = new_next_arg(args, &args)) != NULL)
	{
		if (*arg == '-')
		{
			arg++;
			if (!(len = strlen(arg)))
			{
				header = 0;
				continue;
			}
			else if (!my_strnicmp(arg, "MAX", len))
			{
				char *ptr = NULL;
				ptr = new_next_arg(args, &args);
				if (ptr)
					lines = atoi(ptr);
				if (lines < 0)
					lines = 0;
			}
			else if (!my_strnicmp(arg, "LITERAL", len))
			{
				if (match)
				{
					say("Second -LITERAL argument ignored");
					(void) new_next_arg(args, &args);
					continue;
				}
				if ((match = new_next_arg(args, &args)) != NULL)
					continue;
				say("Need pattern for -LITERAL");
				return;
			}
			else if (!my_strnicmp(arg, "REVERSE", len))
				reverse = 1;
			else if (!my_strnicmp(arg, "TIME", len))
				time_log = 0;
			else if (!my_strnicmp(arg, "BEEP", len))
			{
				if (match)
				{
					say("-BEEP is exclusive; ignored");
					continue;
				}
				else
					match = "\007";
			}
			else if (!my_strnicmp(arg, "CLEAR", len))
			{
				free_lastlog(current_window);
				say("Cleared lastlog");
				return;
			}
			else if (!my_strnicmp(arg, "APPEND", len))
				file_open_type = 1;
			else if (!my_strnicmp(arg, "FILE", len))
			{
#ifdef PUBLIC_ACCESS
				bitchsay("This command has been disabled on a public access system");
				return;
#else
				if (args && *args)
				{
					char *filename;
					filename = next_arg(args, &args);
					if (!(fp = fopen(filename, file_open[file_open_type])))
					{
						bitchsay("cannot open file %s", filename);
						return;
					}
				} 
				else
				{
					bitchsay("Filename needed for save");
					return;
				}
#endif
			}
			else if (!my_strnicmp(arg, "MORE", len))
			{
				current_window->save_hold_mode = current_window->hold_mode;
				current_window->in_more = 1;
				reset_line_cnt(current_window, NULL, 1);
			}
			else
			{
				if (*arg == '-')
					remove = 1, arg++;
				else
					remove = 0;
					
				/*
				 * Which can be combined with -ALL, which 
				 * turns on all levels.  Use --MSGS or
				 * whatever to turn off ones you dont want.
				 */
				if (!my_strnicmp(arg, "ALL", len))
				{
					if (remove)
						mask = 0;
					else
						mask = LOG_ALL;
					continue;	/* Go to next arg */
				}

				/*
				 * Find the lastlog level in our list.
				 */
				for (i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p *= 2)
				{
					if (!my_strnicmp(levels[i], arg, len))
					{
						if (remove)
							mask &= ~p;
						else
							mask |= p;
						break;
					}
				}

				if (i == NUMBER_OF_LEVELS)
				{
					bitchsay("Unknown flag: %s", arg);
					reset_display_target();
					return;
				}
			}
		}
		else
		{
			if (level == 0)
			{
				if (match || isdigit((unsigned char)*arg))
				{
					cnt = atoi(arg);
					level++;
				}
				else
					match = arg;
			}
			else if (level == 1)
			{
				from = atoi(arg);
				level++;
			}
		}
	}
	start_pos = current_window->lastlog_head;
	level = current_window->lastlog_level;
	msg_level = set_lastlog_msg_level(0);

	if (!reverse)
	{
		for (i = 0; (i < from) && start_pos; start_pos = start_pos->next)
			if (!mask || (mask & start_pos->level))
				i++;

		for (i = 0; (i < cnt) && start_pos; start_pos = start_pos->next)
			if (!mask || (mask & start_pos->level))
				i++;

		start_pos = (start_pos) ? start_pos->prev : current_window->lastlog_tail;
	} else
		start_pos = current_window->lastlog_head;
		
	/* Let's not get confused here, display a seperator.. -lynx */
	strip_ansi_in_echo = 0;
	if (header && !fp)
		say("Lastlog:");

	if (match)
	{

		blah = (char *) alloca(strlen(match)+4);
		sprintf(blah, "*%s*", match);
	}
	for (i = 0; (i < cnt) && start_pos; start_pos = (reverse ? start_pos->next : start_pos->prev))
	{
		if (!mask || (mask & start_pos->level))
		{
			i++;
			if (!match || wild_match(blah, start_pos->msg))
			{
				char *s = !get_int_var(LASTLOG_ANSI_VAR)?stripansicodes(start_pos->msg):start_pos->msg;

				if (!fp)
				{
					put_it("%s", !time_log ? s : convert_output_format(fget_string_var(FORMAT_LASTLOG_FSET), "%l %s", start_pos->time, s));
					grab_http("*", "*", start_pos->msg);
				} 
				else
				{
					if (time_log)
					{
						s = convert_output_format(fget_string_var(FORMAT_LASTLOG_FSET), "%l %s", start_pos->time, s);
						chop(s, 3);
					}
					fprintf(fp, "%s\n", s);
				}
				if (lines == 0)
					continue;
				else if (lines == 1)
					break;
				lines--;
			}
		}
	}
	if (header && !fp)
		say("End of Lastlog");
	if (fp)
		fclose(fp);
	strip_ansi_in_echo = 1;
	current_window->lastlog_level = level;
	set_lastlog_msg_level(msg_level);
}

Lastlog *get_lastlog_current_head(Window *win)
{
	return win->lastlog_head;
}

/*
 * add_to_lastlog: adds the line to the lastlog.  If the LASTLOG_CONVERSATION
 * variable is on, then only those lines that are user messages (private
 * messages, channel messages, wall's, and any outgoing messages) are
 * recorded, otherwise, everything is recorded 
 */
void add_to_lastlog(Window *window, const char *line)
{
	Lastlog *new;

	if (window == NULL)
		window = current_window;
	if (window->lastlog_level & msg_level)
	{
		/* no nulls or empty lines (they contain "> ") */
		if (line && (strlen(line) > 2))
		{
			new = (Lastlog *) new_malloc(sizeof(Lastlog));
			new->next = window->lastlog_head;
			new->prev = NULL;
			new->level = msg_level;
			new->msg = NULL;
			new->time = now;
			new->msg = m_strdup(line);

			if (window->lastlog_head)
				window->lastlog_head->prev = new;
			window->lastlog_head = new;

			if (window->lastlog_tail == NULL)
				window->lastlog_tail = window->lastlog_head;

			if (window->lastlog_size++ >= window->lastlog_max)
				remove_from_lastlog(window);
		}
	}
}

unsigned long real_notify_level(void)
{
	return (notify_level);
}

unsigned long real_lastlog_level(void)
{
	return (lastlog_level);
}

void set_notify_level(Window *win, char *str, int unused)
{
	notify_level = parse_lastlog_level(str, 1);
	set_string_var(NOTIFY_LEVEL_VAR, bits_to_lastlog_level(notify_level));
	current_window->notify_level = notify_level;
}

int logmsg(unsigned long log_type, char *from, int flag, char *format, ...)
{
#ifdef PUBLIC_ACCESS
	return 0;
#else
	char *timestr;
	time_t t;
	char *filename = NULL;
	char *expand = NULL;
	char *type = NULL;
	unsigned char **lines = NULL;
	char msglog_buffer[BIG_BUFFER_SIZE+1];			
	
	
	if (!get_string_var(MSGLOGFILE_VAR) || !get_string_var(CTOOLZ_DIR_VAR))
		return 0;

	t = now;
	timestr = update_clock(GET_TIME);


	if (format)
	{
		va_list ap;
		va_start(ap, format);
		vsnprintf(msglog_buffer, BIG_BUFFER_SIZE, format, ap);
		va_end(ap);
	}


	switch (flag)
	{
	case 0:
		if (!(type = bits_to_lastlog_level(log_type)))
			type = "Unknown";
		if (msglog_level & log_type && format)
		{
			char *format;
			
			if (!do_hook(MSGLOG_LIST, "%s %s %s %s", timestr, type, from, msglog_buffer))
				break;
			if (!logptr)
				return 0;
			if (!(format = fget_string_var(FORMAT_MSGLOG_FSET)))
				format = "[$[10]0] [$1] - $2-";
			lines = split_up_line(stripansicodes(convert_output_format(format, "%s %s %s %s", type, timestr, from, msglog_buffer)), 80);
			for ( ; *lines; lines++)
			{
				char *local_copy;
				int len = strlen(*lines) * 2 + 1;
				if (!*lines || !**lines) break;
				local_copy = alloca(len);
				strcpy(local_copy, *lines);

				if (local_copy[strlen(local_copy)-1] == ALL_OFF)
					local_copy[strlen(local_copy)-1] = 0;
				if (logfile_line_mangler)
					mangle_line(local_copy, logfile_line_mangler, len);
				if (*local_copy)
					fprintf(logptr, "%s\n", local_copy);
			}
			fflush(logptr);
		}
		break;
	case 1:
		malloc_sprintf(&filename, "%s/%s", get_string_var(CTOOLZ_DIR_VAR), get_string_var(MSGLOGFILE_VAR));
		expand = expand_twiddle(filename);
		new_free(&filename);
		if (!do_hook(MSGLOG_LIST, "%s %s %s %s", timestr, "On", expand, empty_string))
		{
			new_free(&expand);
			return 1;
		}
		if (logptr)
		{
			new_free(&expand);
			return 1;
		}
		if (!(logptr = fopen(expand, get_int_var(APPEND_LOG_VAR)?"at":"wt")))
		{
			set_int_var(MSGLOG_VAR, 0);
			new_free(&expand);
			return 0;
		} 

		fprintf(logptr, "MsgLog started [%s]\n", my_ctime(t));
		fflush(logptr);
		if (format)
		{
			int i;

			i = logmsg(LOG_CURRENT, from, 0, "%s", msglog_buffer);
			return i;
		}
		bitchsay("Now logging messages to: %s", expand);
		new_free(&expand);
		break;
	case 2:
		if (!do_hook(MSGLOG_LIST, "%s %s %s %s", timestr, "Off", empty_string, empty_string))
			return 1;
		if (!logptr)
			return 1;
		fprintf(logptr, "MsgLog ended [%s]\n", my_ctime(t));
		fclose(logptr);
		logptr = NULL;
		break;
	case 3:
		return logptr ? 1 : 0;
		break;
	case 4:
		if (!logptr)
			return 1;
		fprintf(logptr, "[TimeStamp %s]\n", my_ctime(t));
		fflush(logptr);
		break;
	default:
		bitchsay("Bad Flag passed to logmsg");
		return 0;
	}
	return 1;
#endif
}

void 	set_beep_on_msg (Window *win, char *str, int unused)
{
	beep_on_level = parse_lastlog_level(str, 1);
	set_string_var(BEEP_ON_MSG_VAR, bits_to_lastlog_level(beep_on_level));
}

BUILT_IN_COMMAND(awaylog)
{
	if (args && *args)
	{
		msglog_level = parse_lastlog_level(args, 1);
		set_string_var(MSGLOG_LEVEL_VAR, bits_to_lastlog_level(msglog_level));
		put_it("%s", convert_output_format("$G Away logging set to: $0-", "%s", get_string_var(MSGLOG_LEVEL_VAR)));
	}
	else
		put_it("%s", convert_output_format("$G Away logging currently: $0-", "%s", bits_to_lastlog_level(msglog_level)));		
}

#define EMPTY empty_string
#define RETURN_EMPTY return m_strdup(EMPTY)
#define RETURN_IF_EMPTY(x) if (empty( x )) RETURN_EMPTY
#define GET_INT_ARG(x, y) {RETURN_IF_EMPTY(y); x = my_atol(safe_new_next_arg(y, &y));}
#define GET_STR_ARG(x, y) {RETURN_IF_EMPTY((y)); x = new_next_arg((y), &(y));RETURN_IF_EMPTY((x));}
#define RETURN_STR(x) return m_strdup(x ? x : EMPTY);

/*
 * $line(<line number> [window number])
 * Returns the text of logical line <line number> from the lastlog of 
 * window <window number>.  If no window number is supplied, the current
 * window will be used.  If the window number is invalid, the function
 * will return the false value.
 *
 * Lines are numbered from 1, starting at the most recent line in the buffer.
 * Contributed by Crackbaby (Matt Carothers) on March 19, 1998.
 */

BUILT_IN_FUNCTION(function_line)
{
	int	line = 0;
	char *	windesc = zero;
	Lastlog	*start_pos;
	Window	*win;
	char	*extra;
	int	do_level = 0;


	GET_INT_ARG(line, input);

	while (input && *input)
	{
		GET_STR_ARG(extra, input);

		if (!my_stricmp(extra, "-LEVEL"))
			do_level = 1;
		else
			windesc = extra;
	}

	/* Get the current window, default to current window */
	if (!(win = get_window_by_desc(windesc)))
		RETURN_EMPTY;

	/* Make sure that the line request is within reason */
	if (line < 1 || line > win->lastlog_size)
		RETURN_EMPTY;

	/* Get the line from the lastlog */
	for (start_pos = win->lastlog_head; line; start_pos = start_pos->next)
		line--;

	if (!start_pos)
		start_pos = win->lastlog_tail;
	else
		start_pos = start_pos->prev;

	if (do_level)
		return m_sprintf("%s %s", start_pos->msg, 
					levels[start_pos->level]);
	else
		RETURN_STR(start_pos->msg);
}

/*
 * $lastlog(<window description> <lastlog levels>)
 * Returns all of the lastlog lines (suitable for use with $line()) on the
 * indicated window (0 for the current window) that have any of the lastlog 
 * levels as represented by the lastlog levels. If the window number is 
 * invalid, the function will return the false value.
 */

BUILT_IN_FUNCTION(function_lastlog)
{
	char *	windesc = zero;
	char *	pattern = NULL;
	char *	retval = NULL;
	Lastlog	*iter;
	Window *win;
	int	levels;
	int	line = 1;

	GET_STR_ARG(windesc, input);
	GET_STR_ARG(pattern, input);
	levels = parse_lastlog_level(input, 0);

	/* Get the current window, default to current window */
	if (!(win = get_window_by_desc(windesc)))
		RETURN_EMPTY;

	for (iter = win->lastlog_head; iter; iter = iter->next, line++)
	{
		if (iter->level & levels)
			if (wild_match(pattern, iter->msg))
				m_s3cat(&retval, space, ltoa(line));
	}

	if (retval)
		return retval;

	RETURN_EMPTY;
}



--- NEW FILE: words.c ---
/*
 * words.c -- right now it just holds the stuff i wrote to replace
 * that beastie arg_number().  Eventually, i may move all of the
 * word functions out of ircaux and into here.  Now wouldnt that
 * be a beastie of a patch! Beastie! Beastie!
 *
 * Oh yea.  This file is beastierighted (C) 1994 by the beastie author.
 * Right now the only author is Jeremy "Beastie" Nelson.  See the
 * beastieright file for beastie info.
 */

#include "irc.h"
static char cvsrevision[] = "$Id: words.c,v 1.1.1.1 2003/04/11 01:09:08 dan Exp $";
CVS_REVISION(words_c)
#include "ircaux.h"
#include "modval.h"

/*
 * search() looks for a character forward or backward from mark 
 */
extern char	*BX_search(register char *start, char **mark, char *chars, int how)
{
        if (!mark || !*mark)
                *mark = start;

        if (how > 0)   /* forward search */
        {
		*mark = sindex(*mark, chars);
		how--;
		for (;(how > 0) && *mark && **mark;how--)
			*mark = sindex(*mark+1, chars);
	}

	else if (how == 0)
		return (char *) 0;

	else  /* how < 0 */
	{
		*mark = rsindex(*mark, start, chars, -how);
#if 0
		how++;
		for (;(how < 0) && *mark && **mark;how++)
			*mark = rsindex(*mark-1, start, chars);
#endif
	}

	return *mark;
}

/* Move to an absolute word number from start */
/* First word is always numbered zero. */
extern char	*BX_move_to_abs_word (const register char *start, char **mark, int word)
{
	register char *pointer = (char *)start;
	register int counter = word;

	/* This fixes a bug that counted leading spaces as
	 * a word, when theyre really not a word.... 
	 * (found by Genesis K.)
	 *
	 * The stock client strips leading spaces on both
	 * the cases $0 and $-0.  I personally think this
	 * is not the best choice, but im not going to stick
	 * my foot in this one... im just going to go with
	 * what the stock client does...
	 */
	 while (pointer && *pointer && my_isspace(*pointer))
		pointer++;

	for (;counter > 0 && *pointer;counter--)
	{
		while (*pointer && !my_isspace(*pointer))
			pointer++;
		while (*pointer && my_isspace(*pointer))
			pointer++;
	}

	if (mark)
		*mark = pointer;
	return pointer;
}

/* Move a relative number of words from the present mark */
extern char	*BX_move_word_rel (const register char *start, char **mark, int word)
{
	register char *pointer = *mark;
	register int counter = word;
	char *end = (char *)start + strlen((char *)start);

	if (end == start) 	/* null string, return it */
		return (char *)start;

	/* 
	 * XXXX - this is utterly pointless at best, and
	 * totaly wrong at worst.
 	 */

	if (counter > 0)
	{
		for (;counter > 0 && pointer;counter--)
		{
			while (*pointer && !my_isspace(*pointer))
				pointer++;
			while (*pointer && my_isspace(*pointer)) 
				pointer++;
		}
	}
	else if (counter == 0)
		pointer = *mark;
	else /* counter < 0 */
	{
		for (;counter < 0 && pointer > start;counter++)
		{
			while (pointer >= start && my_isspace(*pointer))
				pointer--;
			while (pointer >= start && !my_isspace(*pointer))
				pointer--;
		}
		pointer++; /* bump up to the word we just passed */
	}

	if (mark)
		*mark = pointer;
	return pointer;
}

/*
 * extract2 is the word extractor that is used when its important to us
 * that 'firstword' get special treatment if it is negative (specifically,
 * that it refer to the "firstword"th word from the END).  This is used
 * basically by the ${n}{-m} expandos and by function_rightw(). 
 *
 * Note that because of a lot of flak, if you do an expando that is
 * a "range" of words, unless you #define STRIP_EXTRANEOUS_SPACES,
 * the "n"th word will be backed up to the first character after the
 * first space after the "n-1"th word.  That apparantly is what everyone
 * wants, so thats whatll be the default.  Those of us who may not like
 * that behavior or are at ambivelent can just #define it.
 */
#undef STRIP_EXTRANEOUS_SPACES
extern char	*BX_extract2(const char *start, int firstword, int lastword)
{
	/* If firstword or lastword is negative, then
	   we take those values from the end of the string */
	char *mark;
	char *mark2;
	char *booya = NULL;

	/* If firstword is EOS, then the user wants the last word */
	if (firstword == EOS)
	{
		mark = (char *)start + strlen(start);
		mark = move_word_rel(start, &mark, -1);
#ifndef NO_CHEATING
		/* 
		 * Really. the only case where firstword == EOS is
		 * when the user wants $~, in which case we really
		 * dont need to do all the following crud.  Of
		 * course, if there ever comes a time that the
		 * user would want to start from the EOS (when??)
		 * we couldnt make this assumption.
		 */
		return m_strdup(mark);
#endif
	}

	/* SOS is used when the user does $-n, all leading spaces 
	 * are retained  
	 */
	else if (firstword == SOS)
		mark = (char *)start;

	/* If the firstword is positive, move to that word */
	else if (firstword >= 0)
	{
		move_to_abs_word(start, &mark, firstword);
		if (!*mark)
			return m_strdup(empty_string);
	}
	/* Otherwise, move to the firstwords from the end */
	else
	{
		mark = (char *)start + strlen((char *)start);
		move_word_rel(start, &mark, firstword);
	}

#ifndef STRIP_EXTRANEOUS_SPACES
	/* IF the user did something like this:
	 *	$n-  $n-m
	 * then include any leading spaces on the 'n'th word.
	 * this is the "old" behavior that we are attempting
	 * to emulate here.
	 */
#ifndef NO_CHEATING
	if (lastword == EOS || (lastword > firstword))
#else
	if (((lastword == EOS) && (firstword != EOS)) || (lastword > firstword))
#endif
	{
		while (mark > start && my_isspace(mark[-1]))
			mark--;
		if (mark > start)
			mark++;
	}
#endif

	/* 
	 * When we find the last word, we need to move to the 
         * END of the word, so that word 3 to 3, would include
	 * all of word 3, so we sindex to the space after the word
	 */
	if (lastword == EOS)
		mark2 = mark + strlen(mark);

	else 
	{
		if (lastword >= 0)
			move_to_abs_word(start, &mark2, lastword+1);
		else
		{
			mark2 = (char *)start + strlen(start);
			move_word_rel(start, &mark2, lastword);
		}

		while (mark2 > start && my_isspace(mark2[-1]))
			mark2--;
	}

	/* 
	 * If the end is before the string, then there is nothing
	 * to extract (this is perfectly legal, btw)
         */
	if (mark2 < mark)
		booya = m_strdup(empty_string);

	else
	{
#if 0
		/* Otherwise, copy off the string we just isolated */ 
		char tmp;
		tmp = *mark2;
		*mark2 = '\0';
		booya = m_strdup(mark);
		*mark2 = tmp;
#endif
		booya = new_malloc(mark2 - mark + 1);
		strmcpy(booya, mark, (mark2 - mark));
	}

	return booya;
}

/*
 * extract is a simpler version of extract2, it is used when we dont
 * want special treatment of "firstword" if it is negative.  This is
 * typically used by the word/list functions, which also dont care if
 * we strip out or leave in any whitespace, we just do what is the
 * fastest.
 */
extern char	*BX_extract(char *start, int firstword, int lastword)
{
	/* 
	 * firstword and lastword must be zero.  If they are not,
	 * then they are assumed to be invalid  However, please note
	 * that taking word set (-1,3) is valid and contains the
	 * words 0, 1, 2, 3.  But word set (-1, -1) is an empty_string.
	 */
	char *mark;
	char *mark2;
	char *booya = NULL;

	/* 
	 * before we do anything, we strip off leading and trailing
	 * spaces. 
	 *
	 * ITS OK TO TAKE OUT SPACES HERE, AS THE USER SHOULDNT EXPECT
	 * THAT THE WORD FUNCTIONS WOULD RETAIN ANY SPACES. (That is
	 * to say that since the word/list functions dont pay attention
	 * to the whitespace anyhow, noone should have any problem with
	 * those ops removing bothersome whitespace when needed.)
	 */
	while (my_isspace(*start))
		start++;
	remove_trailing_spaces(start);

	if (firstword == EOS)
	{
		mark = start + strlen(start);
		mark = move_word_rel(start, &mark, -1);
	}

	/* If the firstword is positive, move to that word */
	else if (firstword >= 0)
		move_to_abs_word(start, &mark, firstword);

	/* Its negative.  Hold off right now. */
	else
		mark = start;


	/* When we find the last word, we need to move to the 
           END of the word, so that word 3 to 3, would include
	   all of word 3, so we sindex to the space after the word
 	 */
	/* EOS is a #define meaning "end of string" */
	if (lastword == EOS)
		mark2 = start + strlen(start);
	else 
	{
		if (lastword >= 0)
			move_to_abs_word(start, &mark2, lastword+1);
		else
			/* its negative -- thats not valid */
			return m_strdup(empty_string);

		while (mark2 > start && my_isspace(mark2[-1]))
			mark2--;
	}

	/* Ok.. now if we get to here, then lastword is positive, so
	 * we sanity check firstword.
	 */
	if (firstword < 0)
		firstword = 0;
	if (firstword > lastword)	/* this works even if fw was < 0 */
		return m_strdup(empty_string);

	/* If the end is before the string, then there is nothing
	 * to extract (this is perfectly legal, btw)
         */
#if 0
	booya = NULL;
#endif
	if (mark2 < mark)
		return m_strdup(empty_string);
	
	booya = new_malloc(mark2 - mark + 1);
	strmcpy(booya, mark, (mark2 - mark));
#if 0
		malloc_strcpy(&booya, empty_string);
	else
	{
		/* Otherwise, copy off the string we just isolated */ 
		char tmp;
		tmp = *mark2;
		*mark2 = '\0';
		malloc_strcpy(&booya, mark);
		*mark2 = tmp;
	}
#endif
	return booya;
}

--- NEW FILE: help.c ---
/*
 * help.c: handles the help stuff for irc 
 *
 * Written by Michael Sandrof
 * Extensively modified by Troy Rollo
 * Re-modified by Matthew Green
 *
 * Copyright(c) 1992 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

/*
 * This has been replaced almost entirely from the original by Michael
 * Sandrof in order to fit in with the multiple screen code.
 *
 * ugh, this wasn't easy to do, but I got there, after working out what
 * had been changed and why, by myself - phone, October 1992.
 *
 * And when I started getting /window create working, I discovered new
 * bugs, and there has been a few more major changes in here again.
 * It is invalid to call help from more than one screen, at the moment,
 * because there is to much to keep track of - phone, jan 1993.
 */

#if 0
static char rcsid[] = "@(#)$Id: help.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
#endif

#include "irc.h"
static char cvsrevision[] = "$Id: help.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(help_c)

#ifdef WANT_EPICHELP
#include "struct.h"
#include "help.h"
#include "input.h"
#include "ircaux.h"
#include "hook.h"
#include "output.h"
#include "screen.h"
#include "server.h"
#include "ircterm.h"
#include "vars.h"
#include "window.h"
#include <sys/stat.h>
#include "bsdglob.h"
#define MAIN_SOURCE
#include "modval.h"

/* Forward declarations */

static	void	help_me 		(char *, char *);
static	void	help_show_paused_topic 	(char *, char *);
static	void	create_help_window 	(void);
static	void	set_help_screen 	(Screen *);
static	void	help_put_it	(const char *topic, const char *format, ...);

/*
 * A few variables here - A lot added to get help working with
 * non - recursive calls to irc_io, and also have it still 
 * reading things from the server(s), so not to ping timeout.
 */
static	int	dont_pause_topic = 0;
static	int	entry_size;
static	int	finished_help_paging = 0;
static	FILE *	help_fp;
#define HELP_PAUSED_LINES_MAX 500
static	int	help_paused_lines = 0;
static	char *	help_paused_topic[HELP_PAUSED_LINES_MAX]; /* Should be enough */
static	Screen *help_screen = (Screen *) 0;
static	int	help_show_directory = 0;
static	char	help_topic_list[BIG_BUFFER_SIZE + 1];
static	Window *help_window = (Window *) 0;
static	char	no_help[] = "NOHELP";
static	char	paused_topic[128];
static	char *	this_arg;
static	int	use_help_window = 0;


/* 
 * show_help:  show's either a page of text from a help_fp, or the whole
 * thing, depending on the value of HELP_PAGER_VAR.  If it gets to the end,
 * (in either case it will eventally), it closes the file, and returns 0
 * to indicate this.
 */ 
static	int	show_help (Window *window, char *name)
{
	Window	*old_target_window = target_window;
	int	rows = 0;
	char	line[256];

	target_window = window ? window : current_window;

	if (get_int_var(HELP_PAGER_VAR))
		rows = window->display_size;

	while (rows)
	{
 		if (!fgets(line, 255, help_fp))
		{
			fclose(help_fp);
			help_fp = NULL;
			target_window = old_target_window;
			return 0;
		}

		if (*(line + strlen(line) - 1) == '\n')
			*(line + strlen(line) - 1) = (char) 0;
			
		/*
		 * This is for compatability with ircII-4.4
		 */
		if (*line == '!' || *line == '#')
			continue;

		help_put_it(name, "%s", line);
		rows--;
	}

	target_window = old_target_window;
	return (1);
}

/*
 * help_prompt: The main procedure called to display the help file
 * currently being accessed.  Using add_wait_prompt(), it sets it
 * self up to be recalled when the next page is asked for.   If
 * called when we have finished paging the help file, we exit, as
 * there is nothing left to show.  If line is 'q' or 'Q', exit the
 * help pager, clean up, etc..  If all is cool for now, we call
 * show_help, and either if its finished, exit, or prompt for the
 * next page.   From here, if we've finished the help page, and
 * doing help prompts, prompt for the help..
 */
static	void	help_prompt (char *name, char *line)
{
	if (finished_help_paging)
	{
		if (*paused_topic)
			help_show_paused_topic(paused_topic, empty_string);
		return;
	}

	if (line && toupper(*line) == 'Q')
	{
		finished_help_paging = 1;
#if 0
		help_paused_lines = 0;		/* Thanks robo */
#endif
		fclose(help_fp);
		help_fp = NULL;
		set_help_screen((Screen *) 0);
		return;
	}

	if (show_help(help_window, name))
	{
		if (dumb_mode)
			help_prompt(name, NULL);
		else
			add_wait_prompt("*** Hit any key for more, 'q' to quit ***",
				help_prompt, name, WAIT_PROMPT_KEY, 1);
	}
	else
	{
		finished_help_paging = 1;
		if (help_fp)
			fclose(help_fp);
		help_fp = NULL;

		if (help_show_directory)
		{
			if (get_int_var(HELP_PAGER_VAR))
			{
			    if (dumb_mode)
				help_show_paused_topic(name, empty_string);
			    else
				add_wait_prompt("*** Hit any key to end ***", 
					help_show_paused_topic, paused_topic,
					WAIT_PROMPT_KEY, 1);
			}
			else
			{
			    help_show_paused_topic(paused_topic, empty_string);
			    set_help_screen((Screen *) 0);
			}
			help_show_directory = 0;
			return;
		}
	}

	if (finished_help_paging)
	{
		if (get_int_var(HELP_PROMPT_VAR))
		{
			char	tmp[BIG_BUFFER_SIZE + 1];

			sprintf(tmp, "%s%sHelp? ", help_topic_list,
				*help_topic_list ? space : empty_string);
			if (!dumb_mode)
				add_wait_prompt(tmp, help_me, help_topic_list,
					WAIT_PROMPT_LINE, 1);
		}
		else
		{
			if (*paused_topic)
				help_show_paused_topic(paused_topic, empty_string);
			set_help_screen((Screen *) 0);
		}
	}
}

/*
 * help_topic:  Given a topic, we search the help directory, and try to
 * find the right file, if all is cool, and we can open it, or zcat it,
 * then we call help_prompt to get the actually displaying of the file
 * on the road.
 */
static	void	help_topic (char *path, char *name)
{
	char	*filename = NULL;

	if (!name)
		return;

	/* what is the base name? */
	filename = m_sprintf("%s/%s", path, name);
	if (filename[strlen(filename)-1] == '/')
		chop(filename, 1);

	/* let uzfopen have all the fun */
	if ((help_fp = uzfopen (&filename, path, 0)))
	{
		/* Isnt this a heck of a lot better then the kludge you were using? */
		help_put_it(name, "*** Help on %s", name);
		help_prompt(name, NULL);
	}
	else
		help_put_it (name, "*** No help available on %s: Use ? for list of topics", name);

	new_free(&filename);
	return;
}

/*
 * help_pause_add_line: this procedure does a help_put_it() call, but
 * puts off the calling, until help_show_paused_topic() is called.
 * I do this because I need to create the list of help topics, but
 * not show them, until we've seen the whole file, so we called
 * help_show_paused_topic() when we've seen the file, if it is needed.
 */
static 	void 	help_pause_add_line (char *format, ...)
{
	char	buf[BIG_BUFFER_SIZE];
	va_list args;

	va_start (args, format);
	vsnprintf(buf, BIG_BUFFER_SIZE - 1, format, args);
	va_end (args);
	if ((help_paused_lines + 1) >= HELP_PAUSED_LINES_MAX)
		ircpanic("help_pause_add_line: would overflow the buffer");
	malloc_strcpy(&help_paused_topic[help_paused_lines++], buf);
}

/*
 * help_show_paused_topic:  see above.  Called when we've seen the
 * whole help file, and we have a list of topics to display.
 */
static	void	help_show_paused_topic (char *name, char *line)
{
	static int i = 0;
	int j = 0;
	int rows;

	if (!help_paused_lines)
		return;

	if (toupper(*line) == 'Q')
		i = help_paused_lines + 1;	/* just big enough */

	rows = help_window->display_size;
	if (i < help_paused_lines)
	{
		for (j = 0; j < rows; j++)
		{
			help_put_it (name, "%s", help_paused_topic[i]);
			new_free(&help_paused_topic[i]);

			/* if we're done, the recurse to break loop */
			if (++i >= help_paused_lines)
				break;
		}
		if (!dumb_mode)
		{
			if ((i < help_paused_lines) && get_int_var(HELP_PAGER_VAR))
				add_wait_prompt("[MORE]", help_show_paused_topic, name, WAIT_PROMPT_KEY, 1);
		}
		else
			help_show_paused_topic(name, line);
	}

	/* 
	 * This cant be an else of the previous if because 'i' can 
	 * change in the previous if and we need to test it again
	 */
	if (i >= help_paused_lines)
	{
		if (get_int_var(HELP_PROMPT_VAR))
		{
			char	buf[BIG_BUFFER_SIZE];

			sprintf(buf, "%s%sHelp? ", name, (name && *name) ? space : empty_string);
			if (!dumb_mode)
				add_wait_prompt(buf, help_me, name, WAIT_PROMPT_LINE, 1);
		}
		else
			set_help_screen((Screen *) 0);

		dont_pause_topic = 0;
		help_paused_lines = 0;	/* Probably should reset this ;-) */
		i = 0;
	}
}

/*
 * help_me:  The big one.  The help procedure that handles working out
 * what was actually requested, sets up the paused topic list if it is
 * needed, does pretty much all the hard work.
 */
static	void	help_me (char *topics, char *args)
{
	char *	ptr;
	glob_t	g;
	int	entries = 0,
		cnt,
		i,
		cols;
	struct	stat	stat_buf;
	char	path[BIG_BUFFER_SIZE+1];
	int	help_paused_first_call = 0;
	char *	help_paused_path = (char *) 0;
	char *	help_paused_name = (char *) 0;
	char *	temp;
	char	tmp[BIG_BUFFER_SIZE+1];
	char	buffer[BIG_BUFFER_SIZE+1];
	char *	pattern = NULL;

	strcpy(help_topic_list, topics);
	ptr = get_string_var(HELP_PATH_VAR);

	sprintf(path, "%s/%s", ptr, topics);
	for (ptr = path; (ptr = strchr(ptr, ' '));)
		*ptr = '/';

	/*
	 * first we check access to the help dir, whinge if we can't, then
	 * work out we need to ask them for more help, else we check the
	 * args list, and do the stuff 
	 */
	if (help_show_directory)
	{
		help_show_paused_topic(paused_topic, empty_string);
		help_show_directory = 0;
	}
		
	finished_help_paging = 0;
	if (access(path, R_OK|X_OK))
	{
		help_put_it(no_help, "*** Cannot access help directory!");
		set_help_screen((Screen *) 0);
		return;
	}

	this_arg = next_arg(args, &args);
	if (!this_arg && *help_topic_list && get_int_var(HELP_PROMPT_VAR))
	{
		if ((temp = strrchr(help_topic_list, ' ')) != NULL)
			*temp = '\0';
		else
			*help_topic_list = '\0';

		sprintf(tmp, "%s%sHelp? ", help_topic_list, *help_topic_list ? space : empty_string);

		if (!dumb_mode)
			add_wait_prompt(tmp, help_me, help_topic_list, WAIT_PROMPT_LINE, 1);
		return;
	}

	if (!this_arg)
	{
		set_help_screen((Screen *) 0);
		return;
	}

	create_help_window();

	/*
	 * This is just a bogus while loop which is intended to allow
	 * the user to do '/help alias expressions' without having to
	 * include a slash inbetween the topic and subtopic.
	 *
	 * If all goes well, we 'break' at the bottom of the loop.
	 */
	while (this_arg)
	{
		entries = 0;
		reset_display_target();

		if (!*this_arg)
			help_topic(path, NULL);

		if (strcmp(this_arg, "?") == 0)
		{
			this_arg = empty_string;
			if (!dont_pause_topic)
				dont_pause_topic = 1;
		}

		/*
		 * entry_size is set to the width of the longest help topic
		 * (adjusted for compression extensions, of course.)
		 */
		entry_size = 0;

		/*
		 * Gather up the names of the files in the help directory.
		 */
		{
#ifndef HAVE_FCHDIR
			char 	opath[MAXPATHLEN + 1];
			getcwd(opath, MAXPATHLEN);
#else
			int 	cwd = open(".", O_RDONLY);
#endif

			chdir(path);
			pattern = alloca(strlen(path) + 2 + 
					 strlen(this_arg) + 3);
			strcpy(pattern, this_arg);
			strcat(pattern, "*");
#ifdef GLOB_INSENSITIVE
			bsd_glob(pattern, GLOB_INSENSITIVE /* GLOB_MARK */, NULL, &g);
#else
			bsd_glob(pattern, 0 /* GLOB_MARK */, NULL, &g);
#endif
#ifndef HAVE_FCHDIR
			chdir(opath);
#else
			fchdir(cwd);
			close(cwd);
#endif
		}

		for (i = 0; i < g.gl_matchc; i++)
		{
			char	*tmp = g.gl_pathv[i];
			int 	len = strlen(tmp);

			if (!end_strcmp(tmp, ".gz", 3))
				len -= 3;
			else if (!end_strcmp(tmp, ".bz2", 4))
				len -= 4;
			entry_size = (len > entry_size) ? len : entry_size;
		}

		/*
		 * Right here we need to check for an 'exact match'.
		 * An 'exact match' would be sitting in gl_pathv[0],
		 * and it is 'exact' if it is identical to what we are
		 * looking for, or if it is the same except that it has
		 * a compression extension on it
		 */
		if (g.gl_matchc > 1)
		{
			char *str1 = g.gl_pathv[0];
			char *str2 = this_arg;
			int len1 = strlen(str1);
			int len2 = strlen(str2);


			     if (len1 == len2 && !my_stricmp(str1, str2))
				entries = 1;
			else if (len1 - 3 == len2 && !my_strnicmp(str1, str2, len2) && !end_strcmp(str1, ".gz", 3))
				entries = 1;
			else if (len1 - 2 == len2 && !my_strnicmp(str1, str2, len2) && !end_strcmp(str1, ".Z", 2))
				entries = 1;
			else if (len1 - 2 == len2 && !my_strnicmp(str1, str2, len2) && !end_strcmp(str1, ".z", 2))
				entries = 1;
		}

		if (!*help_topic_list)
			dont_pause_topic = 1;

/* reformatted */
/*
 * entries: -1 means something really died, 0 means there
 * was no help, 1, means it wasn't a directory, and so to
 * show the help file, and the default means to add the
 * stuff to the paused topic list..
 */
if (!entries)
	entries = g.gl_matchc;

switch (entries)
{
	case -1:
	{
		help_put_it(no_help, "*** Error during help function: %s", strerror(errno));
		set_help_screen(NULL);
		if (help_paused_first_call)
		{
			help_topic(help_paused_path, help_paused_name);
			help_paused_first_call = 0;
			new_free(&help_paused_path);
			new_free(&help_paused_name);
		}
		return;
	}
	case 0:
	{
		help_put_it(this_arg, "*** No help available on %s: Use ? for list of topics", this_arg);
		if (!get_int_var(HELP_PROMPT_VAR))
		{
			set_help_screen(NULL);
			break;
		}
		sprintf(tmp, "%s%sHelp? ", help_topic_list, *help_topic_list ? space : empty_string);
		if (!dumb_mode)
			add_wait_prompt(tmp, help_me, help_topic_list, WAIT_PROMPT_LINE, 1);

		if (help_paused_first_call)
		{
			help_topic(help_paused_path, help_paused_name);
			help_paused_first_call = 0;
			new_free(&help_paused_path);
			new_free(&help_paused_name);
		}
	
		break;
	}
	case 1:
	{
		sprintf(tmp, "%s/%s", path, g.gl_pathv[0]);
		stat(tmp, &stat_buf);
		if (stat_buf.st_mode & S_IFDIR)
		{
			strcpy(path, tmp);
			if (*help_topic_list)
				strcat(help_topic_list, space);

			strcat(help_topic_list, g.gl_pathv[0]);

			if (!(this_arg = next_arg(args, &args)))
			{
				help_paused_first_call = 1;
				malloc_strcpy(&help_paused_path, path);
				malloc_strcpy(&help_paused_name, g.gl_pathv[0]);
				dont_pause_topic = -1;
				this_arg = "?";
			}
			bsd_globfree(&g);
			continue;
		}
		else
		{
			help_topic(path, g.gl_pathv[0]);
			finished_help_paging = 0;
			break;
		}
	}
	default:
	{
		help_show_directory = 1;
		strcpy(paused_topic, help_topic_list);
		help_pause_add_line("*** %s choices:", help_topic_list);
		entry_size += 2;
		cols = (current_term->TI_cols - 10) / entry_size;

		strcpy(buffer, empty_string);
		cnt = 0;

		for (i = 0; i < entries; i++)
		{
			if (!end_strcmp(g.gl_pathv[i], ".gz", 3))
				chop(g.gl_pathv[i], 3);
			else if (!end_strcmp(g.gl_pathv[i], ".bz2", 4))
				chop(g.gl_pathv[i], 4);
			strcat(buffer, g.gl_pathv[i]);

			/*
			 * Since we already know how many columns each
			 * line will contain, we check to see if we have
			 * accumulated that many entries.  If we have, we
			 * output the line to the screen.
			 */
			if (++cnt == cols)
			{
				help_pause_add_line("%s", buffer);
				strcpy(buffer, empty_string);
				cnt = 0;
			}

			/*
			 * If we have not finished this line, then we have
			 * to pad the name length out to the expected width.
			 * 'entry_size' is the column width.  We also have
			 * do adjust for compression extension.
			 */
			else
				strextend(buffer, ' ', entry_size - strlen(g.gl_pathv[i]));
		}

		help_pause_add_line("%s", buffer);
		if (help_paused_first_call)
		{
			help_topic(help_paused_path, help_paused_name);
			help_paused_first_call = 0;
			new_free(&help_paused_path);
			new_free(&help_paused_name);
		}
		if (dont_pause_topic == 1)
		{
			help_show_paused_topic(paused_topic, empty_string);
			help_show_directory = 0;
		}
		break;
	}
}
/* end of reformatting */


		bsd_globfree(&g);
		break;
	}

	/*
	 * This one is for when there was never a topic and the prompt
	 * never got a topic..  and help_screen was never reset..
	 * phone, jan 1993.
	 */
	if (!*help_topic_list && finished_help_paging)
		set_help_screen((Screen *) 0);
}

/*
 * help: the HELP command, gives help listings for any and all topics out
 * there 
 */
BUILT_IN_COMMAND(epichelp)
{
	char	*help_path;

	finished_help_paging = 0;
	help_show_directory = 0;
	dont_pause_topic = 0;
	use_help_window = 0;

	/*
	 * The idea here is to work out what sort of help we are using - 
	 * either the installed help files, or some help service, what
	 * ever it maybe.  Once we have worked this out, if we are using
	 * a help window, set it up properly.
	 */
	help_path = get_string_var(HELP_PATH_VAR);

	if (!help_path || !*help_path || access(help_path, R_OK | X_OK))
	{
		help_put_it(no_help, "*** HELP_PATH variable not set or set to an invalid path");
		return;
	}

	/* Allow us to wait until help is finished */
	if (!my_strnicmp(args, "-wait", 2))
	{
		while (help_screen)
			io("help");
		return;
	}

	if (help_path && help_screen && help_screen != current_window->screen)
	{
		say("You may not run help in two screens");
		return;
	}

	help_screen = current_window->screen;
	help_window = (Window *) 0;
	help_me(empty_string, (args && *args) ? args : "?");
}




static	void create_help_window (void)
{
	if (help_window)
		return;

	if (!dumb_mode && get_int_var(HELP_WINDOW_VAR))
	{
		use_help_window = 1;
		help_window = new_window(current_window->screen);
		help_window->hold_mode = OFF;
		help_window->window_level = LOG_HELP;
		update_all_windows();
	}
	else
		help_window = current_window;
}



static	void	set_help_screen (Screen *screen)
{
	help_screen = screen;
	if (!help_screen && help_window)
	{
		if (use_help_window)
		{
			int display = window_display;

			window_display = 0;
			delete_window(help_window);
			window_display = display;
		}
		help_window = (Window *) 0;
		update_all_windows();
	}
}

static	void	help_put_it	(const char *topic, const char *format, ...)
{
	char putbuf[BIG_BUFFER_SIZE * 3 + 1];

	if (format)
	{
		va_list args;
		va_start (args, format);
		vsnprintf(putbuf, BIG_BUFFER_SIZE * 3, format, args);
		va_end(args);

		if (do_hook(HELP_LIST, "%s %s", topic, putbuf))
		{
			int old_level = who_level;
			Window *old_target_window = target_window;

			/*
			 * LOG_HELP is a completely bogus mode.  We use
			 * it only to make sure that the current level is
			 * not LOG_CURRENT, so that the to_window will stick.
			 */
			who_level = LOG_HELP;
			if (help_window)
				target_window = help_window;
			add_to_screen(putbuf);
			target_window = old_target_window;
			who_level = old_level;
		}
	}
}
#endif


--- NEW FILE: functions.c ---
/*
 * functions.c -- Built-in functions for ircII
 *
 * Written by Michael Sandrof
 * Copyright(c) 1990 Michael Sandrof
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 *
 */

#include "irc.h"
static char cvsrevision[] = "$Id: functions.c,v 1.1.1.3 2003/06/11 07:00:41 root Exp $";
CVS_REVISION(functions_c)
#include "struct.h"

#include "alias.h"
#include "alist.h"
#include "array.h"
#include "dcc.h"
#include "commands.h"
[...7375 lines suppressed...]
}

BUILT_IN_FUNCTION(function_getset, input)
{
	RETURN_MSTR(make_string_var(input));
}

BUILT_IN_FUNCTION(function_builtin, input)
{
	RETURN_MSTR(built_in_alias (*input, NULL));
}

BUILT_IN_FUNCTION(function_ipv6, input)
{
#ifdef IPV6
	RETURN_INT(1);
#else
	RETURN_INT(0);
#endif
}

--- NEW FILE: cdns.c ---
/************
 *  cdns.c  *
 ************
 *
 * A threaded DNS lookup implementation.
 *
 * This was written exclusively to get around the fact that
 * gethostbyaddr()/gethostbyname() will BLOCK until the resolver either
 * gets an answer, or times out. So we create a thread, and allow
 * the calls to block as long as they want, as the main app's thread
 * will keep on chugging along regardless.
 *
 * BTW, even tho the file is .cc, it really is only C code, I just like
 * C++'s strict type checking.
 *
 * Written by Scott H Kilau
 *
 * Copyright(c) 1999
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
 * $Id: cdns.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $
 */

#include <sys/types.h>
#include <netinet/in.h>

#include "cdns.h"
#include "irc.h"	/* To pick up our next #define checks */
static char cvsrevision[] = "$Id: cdns.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(cdns_c)
#include "commands.h"

#include "struct.h"
#include "newio.h"
#define MAIN_SOURCE
#include "modval.h"

#if defined(THREAD) && defined(WANT_NSLOOKUP)

/* Our static functions */
static void init_dns_mutexes(void);
static void *start_dns_thread(void *);
static void do_dns_lookup(DNS_QUEUE *);
static void cleanup_dns(void);
static void destroy_dns_queue(DNS_QUEUE **, DNS_QUEUE **);
static void free_dns_entry(DNS_QUEUE *);
static void kill_dns_thread(int);
static DNS_QUEUE *build_dns_entry(char *, void (*)(DNS_QUEUE *), char *, void *);
static DNS_QUEUE *dns_dequeue(DNS_QUEUE **,  DNS_QUEUE **);
static DNS_QUEUE *dns_enqueue(DNS_QUEUE **, DNS_QUEUE **, DNS_QUEUE *);
static DNS_QUEUE *dns_enqueue_urgent(DNS_QUEUE **, DNS_QUEUE **, DNS_QUEUE *);

/* Our static globals */
static pthread_t dns_thread = {0};
static pthread_mutex_t pending_queue_mutex = {0};
static pthread_mutex_t finished_queue_mutex = {0};
static pthread_mutex_t quit_mutex = {0};
static pthread_cond_t pending_queue_cond;
static DNS_QUEUE *PendingQueueHead = NULL, *PendingQueueTail = NULL;
static DNS_QUEUE *FinishedQueueHead = NULL, *FinishedQueueTail = NULL;
static int cdns_write = -1;
static int cdns_read = -1;

/*
 * start_dns : This should be called by the main app thread,
 * whenever it wants to start up the dns stuff
 */
void start_dns(void)
{
	int fd_array[2];
	/* Init our queues. */
	Q_OPEN(&PendingQueueHead, &PendingQueueTail);
	Q_OPEN(&FinishedQueueHead, &FinishedQueueTail);
	/* Create our mutexes */
	init_dns_mutexes();
	/* init our pending queue condition, and set to default attributes (NULL) */
	pthread_cond_init(&pending_queue_cond, NULL);
	/* lock up the quit mutex */
	pthread_mutex_lock(&quit_mutex);
	/* create our pipe [0] = main to read from, [1] = cdns to write to */
	pipe(fd_array);
	cdns_read = fd_array[0];
	cdns_write = fd_array[1];
	new_open(cdns_read);
	new_open(cdns_write);
	/* create our dns thread */
	pthread_create(&dns_thread, NULL, start_dns_thread, NULL);
}

/* 
 * stop_dns : This should be called by the main app thread,
 * whenever it wants to stop the dns stuff.
 */
void stop_dns(void)
{
	void *ptr;
	if (!dns_thread)
		return;
	/* Unlock the quit mutex */
	pthread_mutex_unlock(&quit_mutex);
	/* lock up pending queue mutex */
	pthread_mutex_lock(&pending_queue_mutex);
	/* signal thread to wake up */
	pthread_cond_signal(&pending_queue_cond);
	/* Give lock back, so dns thread can react to signal */
	pthread_mutex_unlock(&pending_queue_mutex);
	/* Wait until the thread kills itself. */
	pthread_join(dns_thread, &ptr);
	cleanup_dns();
}

/* 
 * kill_dns : This should be called by the main app thread,
 * when it wants the dns thread to go away forever. This function
 * kills the thread uncleanly, and probably should only be used
 * when the main app is about to exit()
 */
void kill_dns(void)
{
	sigset_t set, oldset;

	/* Fill set with all known signals */
	sigfillset(&set);
	/* Remove the few signals that POSIX says we should */
	sigdelset(&set, SIGFPE);
	sigdelset(&set, SIGILL);
	sigdelset(&set, SIGSEGV);
	/* Tell our thread (main) to block against the above signals */
	sigprocmask(SIG_BLOCK, &set, &oldset);
	/* lock up pending queue mutex */
	pthread_mutex_lock(&pending_queue_mutex);
	/* signal thread to wake up */
	pthread_cond_signal(&pending_queue_cond);
	/* Kill the dns thread, using the SIGQUIT signal */
	pthread_kill(dns_thread, SIGQUIT);
	/* Put back the previous blocking signals */
	sigprocmask(SIG_BLOCK, &oldset, &set);
	/* cleanup anything dealing with the dns stuff */
	cleanup_dns();
}

/*
 * add_to_dns_queue : This should be called by the main app thread,
 * whenever it wants us to resolve something... host <---> ip.
 */
void 
add_to_dns_queue(char *userhost, void (*callback)(DNS_QUEUE *), char *cmd, void *data, int urgency)
{
	char *split = (char *) 0;
	DNS_QUEUE *tmp;

	if (userhost && *userhost) {
		/* Look for user at host */
		split = index(userhost, '@');
		if (split)
			split++;
		else
			split = userhost;
		if (split && *split) {
			/* Build dns entry */
			tmp = build_dns_entry(split, callback, cmd, data);
			/* Wait until we can get mutex lock for queue */
			pthread_mutex_lock(&pending_queue_mutex);
			/* Enqueue the entry, checking urgency */
			if (urgency == DNS_URGENT)
				dns_enqueue_urgent(&PendingQueueHead,
					&PendingQueueTail, tmp);
			else
				dns_enqueue(&PendingQueueHead,
					&PendingQueueTail, tmp);
			/* signal dns thread, its got work to do */
			pthread_cond_signal(&pending_queue_cond);
			/* Give lock back, so dns thread can react to signal */
			pthread_mutex_unlock(&pending_queue_mutex);
		}
		else
			fprintf(stderr, "%s:%d error: No host!", __FILE__, __LINE__);
	}
	else
		fprintf(stderr, "%s:%d error: NULL!", __FILE__, __LINE__);
}

/* set_dns_output_fd : This makes check_dns_queue much better.
 * This will add our piped fd into the main's select() loop, so
 * we will know right away when the output queue has data waiting.
 */
void set_dns_output_fd(fd_set *rd)
{
	if (cdns_read >= 0)
		FD_SET(cdns_read, rd);
}

/* dns_check : See above. */
void dns_check(fd_set *rd)
{
	char blah[2];
	if (cdns_read >= 0 && FD_ISSET(cdns_read, rd)) {
		read(cdns_read, &blah, 1);
		check_dns_queue();
	}
}

/*
 * check_dns_queue : This should be called by the main app thread, at
 * periodic intervals, ie, whenever its convenient.
 */
void check_dns_queue()
{
	DNS_QUEUE *dns = (DNS_QUEUE *) 0;
	while (pthread_mutex_trylock(&finished_queue_mutex) == 0) {
		dns = dns_dequeue(&FinishedQueueHead, &FinishedQueueTail);
		pthread_mutex_unlock(&finished_queue_mutex);
		if (dns) {
#ifdef MY_DEBUG
			fprintf(stderr, "DNS IN: %s: %s <-> %s",
				dns->in ? dns->in : "<NULL>",
				dns->in ? dns->in : "<NULL>",
				dns->out ? dns->out : "<NULL>");
#endif
			if (dns->alias)
			{
				char buffer[BIG_BUFFER_SIZE+1];
				snprintf(buffer, BIG_BUFFER_SIZE, "%s %s ", dns->in ? dns->in : "<NULL>", dns->out ? dns->out : "<NULL>");
				parse_line("NSLOOKUP", dns->alias, buffer, 0, 0, 1);
			}
			else if (dns->callback)
				dns->callback(dns);
			else
				fprintf(stderr, "%s:%d error: No callback!",
				   __FILE__, __LINE__);
			free_dns_entry(dns);
		}
		else
			return;
	}
}

/* Give back memory that we allocated for this entry */
static void free_dns_entry(DNS_QUEUE *tmp)
{
	if (tmp->in)
		free(tmp->in);
	if (tmp->out)
		free(tmp->out);
	if (tmp->alias)
		free(tmp->alias);
	if (tmp->hostentr)
        freemyhostent(tmp->hostentr);
	free(tmp);
}

/* init our dns mutexes */
static void init_dns_mutexes(void)
{
	pthread_mutex_init(&pending_queue_mutex, NULL);
	pthread_mutex_init(&finished_queue_mutex, NULL);
	pthread_mutex_init(&quit_mutex, NULL);
}

static void kill_dns_thread(int notused)
{
	/* Empty */
}

static void kill_dns_thread2(int notused)
{
	int ecode = 0;
	pthread_exit(&ecode);
}

static void dns_thread_signal_setup(void)
{
	sigset_t set;

	/* Create SIGQUIT signal handler for this thread */
#if 0
	signal(SIGQUIT, kill_dns_thread);
#endif
	/* Use ircii's portable implementation of signal() instead */
	my_signal(SIGQUIT, (sigfunc *) kill_dns_thread, 0);
	/* Fill set with every signal */
	sigfillset(&set);
	/* Remove the SIGQUIT signal from the set */
	sigdelset(&set, SIGQUIT);
	/* Apply the mask on this thread */
	pthread_sigmask(SIG_BLOCK, &set, NULL);
}	

/* Our main function of the dns thread */
static void *start_dns_thread(void *args)
{
	DNS_QUEUE *dns = NULL;
	/* Set up the thread's signal handlers and mask */
	dns_thread_signal_setup();
	/* loop */
	while(1) {
		/* Try the quit mutex, if we get it, that means
		 * main() wants us to die.
		 */
		if (pthread_mutex_trylock(&quit_mutex) == 0) {
			kill_dns_thread2(0);
		}
		/* Lock the queue */
		pthread_mutex_lock(&pending_queue_mutex);
		dns = dns_dequeue(&PendingQueueHead, &PendingQueueTail);
		if (!dns) {
			/*
			 * Give back the cpu, and go to sleep until cond gets signalled.
			 * Note: the mutex MUST be locked, before going into the wait!
			 */
			pthread_cond_wait(&pending_queue_cond, &pending_queue_mutex);
			/* We have been woken up. Thus, 2 conditions are present.
			 * 1) We have been signalled that data is waiting.
			 * 2) The mutex is locked.
			 */
			pthread_mutex_unlock(&pending_queue_mutex);
		}
		else {
			char c = ' ';
			pthread_mutex_unlock(&pending_queue_mutex);
			do_dns_lookup(dns);
			/* block until we get the lock */
			pthread_mutex_lock(&finished_queue_mutex);
			dns_enqueue(&FinishedQueueHead, &FinishedQueueTail, dns);
			pthread_mutex_unlock(&finished_queue_mutex);
			/* Write to our pipe, this will wake up mains select loop */
			write(cdns_write, &c, 1);
		}
	}
}

/* Makes a malloced duplicate of the hostent returned. */
my_hostent *duphostent(struct hostent *orig)
{
	my_hostent *tmp = new_malloc(sizeof(my_hostent));
    int z;

	tmp->h_name = m_strdup(orig->h_name);
	tmp->h_length = orig->h_length;
	tmp->h_addrtype = orig->h_addrtype;

	for(z=0;z<MAXALIASES && orig->h_aliases[z];z++)
		tmp->h_aliases[z] = m_strdup(orig->h_aliases[z]);

    for(z=0;z<MAXADDRS && orig->h_addr_list[z];z++)
		memcpy(&tmp->h_addr_list[z], orig->h_addr_list[z], sizeof(*orig->h_addr_list));

    return (my_hostent *)tmp;
}

/* Free's the replica hostent. */
void freemyhostent(my_hostent *freeme)
{
	int z;

	if(!freeme)
		return;

	new_free(&freeme->h_name);

	for(z=0;z<MAXALIASES;z++)
		if(freeme->h_aliases[z])
			new_free(&freeme->h_aliases[z]);

	new_free(&freeme);
}

/* Does the actual DNS lookup.... host <---> ip  */
static void do_dns_lookup(DNS_QUEUE *dns)
{
	struct hostent *temp;
	struct in_addr temp1;
	int ip = 0;
 
	/* If nothing, give back nothing */
	if (!dns->in)
		return;
	if (isdigit(*(dns->in + strlen(dns->in) - 1))) {
		ip = 1;
		temp1.s_addr = inet_addr(dns->in);
		temp = gethostbyaddr((char*) &temp1,
			sizeof (struct in_addr), AF_INET);
	}
	else {
		temp = gethostbyname(dns->in);
		if (temp)
#if defined(_Windows)
			memcpy(&temp1, temp->h_addr, temp->h_length);
#else
			memcpy((caddr_t)&temp1, temp->h_addr, temp->h_length);
#endif
		else
			return;
	}
	if (!temp)
		return;
	if (ip) {
		dns->ip = 1;
		if (temp->h_name && *temp->h_name) {
			dns->out = (char *) malloc(strlen(temp->h_name) + 1);
			strcpy(dns->out, temp->h_name);
            dns->hostentr = duphostent(temp);
		}
	}
	else {
		dns->ip = 0;
		dns->out = (char *) malloc(strlen(inet_ntoa(temp1)) + 1);
		strcpy(dns->out, inet_ntoa(temp1));
        dns->hostentr = duphostent(temp);
	}
}

/* dequeue an entry from the passed in queue */
DNS_QUEUE *
dns_dequeue(DNS_QUEUE **headp, DNS_QUEUE **tailp)
{
	DNS_QUEUE *tmp = NULL;

	if (*headp == NULL)
		return NULL;
	tmp = *headp;
	*headp = Q_NEXT(tmp);
	if (*headp == NULL)
		*tailp = NULL;
	Q_NEXT(tmp) = NULL;
	return tmp;
}

/* enqueue a request onto the passed in queue */
DNS_QUEUE *
dns_enqueue(DNS_QUEUE **headp, DNS_QUEUE **tailp, DNS_QUEUE *tmp)
{
	Q_NEXT(tmp) = NULL;
	if (*headp == NULL)
		*headp = *tailp = tmp;
	else {
		Q_NEXT(*tailp) = tmp;
		*tailp = tmp;
	}
	return NULL;
}

/* enqueue a request onto the passed in queue, putting it at the front
 * of the queue. This means it will be the next requested dequeue.
 */
DNS_QUEUE *
dns_enqueue_urgent(DNS_QUEUE **headp, DNS_QUEUE **tailp, DNS_QUEUE *tmp)
{
	Q_NEXT(tmp) = *headp;
	*headp = tmp;
	if (*tailp == NULL)
		*tailp = tmp;
	return NULL;
}

/* build a dns entry struct */
static DNS_QUEUE *
build_dns_entry(char *text, void (*callback) (DNS_QUEUE *), char *cmd, void *data)
{
	DNS_QUEUE *tmp = (DNS_QUEUE *) malloc(sizeof(DNS_QUEUE));
	bzero(tmp, sizeof(DNS_QUEUE));
	tmp->in = (char *) malloc(strlen(text) + 1);
	strcpy(tmp->in, text);
	tmp->callback = callback;
	tmp->callinfo = data;
        if (cmd)
		tmp->alias = strdup(cmd);
	return tmp;
}

/* cleanup_dns : cleanup anything regarding the dns thread */
static void cleanup_dns(void)
{
	pthread_mutex_destroy(&pending_queue_mutex);
	pthread_mutex_destroy(&finished_queue_mutex);
	pthread_mutex_destroy(&quit_mutex);
	pthread_cond_destroy(&pending_queue_cond);
	destroy_dns_queue(&PendingQueueHead, &PendingQueueTail);
	destroy_dns_queue(&FinishedQueueHead, &FinishedQueueTail);
	close(cdns_read);
	close(cdns_write);
	cdns_read = cdns_write = -1;
}

/* destroy_dns_queue : Walks the queue, blowing away each node */
static void destroy_dns_queue(DNS_QUEUE **QueueHead, DNS_QUEUE **QueueTail)
{
	DNS_QUEUE *dns;
	
	while((dns = dns_dequeue(QueueHead, QueueTail)) != NULL)
		free_dns_entry(dns);
}
#endif /* THREAD  && NSLOOKUP */

--- NEW FILE: bot_link.c ---
/*
 * Copyright Colten Edwards. Oct 96
 */
 
#include "irc.h"
static char cvsrevision[] = "$Id: bot_link.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(botlink_c)
#include <stdarg.h>

#include "ircaux.h"
#include "struct.h"
#include "commands.h"
#include "dcc.h"
#include "server.h"
#include "output.h"
#include "list.h"
#include "who.h"
#include "ctcp.h"
#include "tcl_bx.h"
[...1010 lines suppressed...]
	else
	{
		for (i = 0; C_dcc[i].name; i++)
			if (!my_stricmp(C_dcc[i].name, command))
				break;
		if (C_dcc[i].name)
#ifdef WANT_USERLIST
			dcc_printf(idx, "%6s %-10s %s\n", convert_flags_to_str(C_dcc[i].access), C_dcc[i].name, C_dcc[i].help);
#else
			dcc_printf(idx, "%-10s %s\n", C_dcc[i].name, C_dcc[i].help);
#endif
		else
			dcc_printf(idx, "No such command [%s]\n", command);
	}
	return TCL_OK;
}


#endif


--- NEW FILE: modules.c ---

/*
 * Routines within this files are Copyright Colten Edwards 1996
 * Aka panasync on irc.
 * Thanks to Shiek and Flier for helpful hints and suggestions. As well 
 * as code in some cases.
 */

#define __modules_c

#include "irc.h"
static char cvsrevision[] = "$Id: modules.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(modules_c)
#include "struct.h"
#include "alias.h"
#include "encrypt.h"
#include "commands.h"
#include "dcc.h"
#include "input.h"
[...1346 lines suppressed...]
		} else
			put_it("%s", convert_output_format("$G Unsuccessful module unload", NULL, NULL));
	} else
		bitchsay("No such module loaded");
}

int add_module(unsigned int mod_type, Function * table, char *modname)
{
Function *p = table;
int count = 0;

	while(p)
	{
		add_module_proc(mod_type, modname, p->name, p->desc, p->id, p->flag, p->func1, p->func2);
		p++;count++;
	}
	return count;
}
#endif


--- NEW FILE: window.c ---
/*
 * window.c: Handles the Main Window stuff for irc.  This includes proper
 * scrolling, saving of screen memory, refreshing, clearing, etc. 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 * Modified 1996 Colten Edwards
 */


#include "irc.h"
static char cvsrevision[] = "$Id: window.c,v 1.1.1.1 2003/04/11 01:09:08 dan Exp $";
CVS_REVISION(window_c)
#include "struct.h"

#include "screen.h"
#include "commands.h"
#include "exec.h"
[...4970 lines suppressed...]
	if (new_window)
		make_window_current(new_window);
	else
		say("Window [%s] doesn't exist any more.  Punting.", desc);
}

void	make_window_current_by_winref (int refnum)
{
	Window	*new_window;

	if (refnum == -1)
		return;

	if ((new_window = get_window_by_refnum(refnum)))
		make_window_current(new_window);
	else
		say("Window [%d] doesn't exist any more.  Punting.", refnum);
}



--- NEW FILE: log.c ---
/*
 * log.c: handles the irc session logging functions 
 *
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"
static char cvsrevision[] = "$Id: log.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(log_c)
#include "struct.h"

#include <sys/stat.h>

#include "log.h"
#include "vars.h"
#include "screen.h"
#include "misc.h"
#include "output.h"
#include "ircaux.h"
#define MAIN_SOURCE
#include "modval.h"

FILE	*irclog_fp = NULL;

void do_log(int flag, char *logfile, FILE **fp)
{
#ifdef PUBLIC_ACCESS
	bitchsay("This command has been disabled on a public access system");
	return;
#else
	time_t	t = now;

	if (flag)
	{
		if (*fp)
			say("Logging is already on");
		else
		{
			if (!logfile)
				return;
			if (!(logfile = expand_twiddle(logfile)))
			{
				say("SET LOGFILE: No such user");
				return;
			}

			if ((*fp = fopen(logfile, get_int_var(APPEND_LOG_VAR)?"a":"w")) != NULL)
			{
				say("Starting logfile %s", logfile);
				chmod(logfile, S_IREAD | S_IWRITE);
				fprintf(*fp, "IRC log started %.24s\n", ctime(&t));
				fflush(*fp);
			}
			else
			{
				say("Couldn't open logfile %s: %s", logfile, strerror(errno));
				*fp = NULL;
			}
			new_free(&logfile);
		}
	}
	else
	{
		if (*fp)
		{
			fprintf(*fp, "IRC log ended %.24s\n", ctime(&t));
			fflush(*fp);
			fclose(*fp);
			*fp = NULL;
			say("Logfile ended");
		}
	}
#endif
}

/* logger: if flag is 0, logging is turned off, else it's turned on */
void logger(Window *win, char *unused, int flag)
{
	char	*logfile;
	if ((logfile = get_string_var(LOGFILE_VAR)) == NULL)
	{
		say("You must set the LOGFILE variable first!");
		set_int_var(LOG_VAR, 0);
		return;
	}
	do_log(flag, logfile, &irclog_fp);
	if (!irclog_fp && flag)
		set_int_var(LOG_VAR, 0);
}

/*
 * set_log_file: sets the log file name.  If logging is on already, this
 * closes the last log file and reopens it with the new name.  This is called
 * automatically when you SET LOGFILE. 
 */
void set_log_file(Window *win, char *filename, int unused)
{
	char	*expanded;

	if (filename)
	{
		if (strcmp(filename, get_string_var(LOGFILE_VAR)))
			expanded = expand_twiddle(filename);
		else
			expanded = expand_twiddle(get_string_var(LOGFILE_VAR));
		set_string_var(LOGFILE_VAR, expanded);
		new_free(&expanded);
		if (irclog_fp)
		{
			logger(current_window, NULL, 0);
			logger(current_window, NULL, 1);
		}
	}
}

/*
 * add_to_log: add the given line to the log file.  If no log file is open
 * this function does nothing. 
 */
void BX_add_to_log(FILE *fp, time_t t, const char *line, int mangler)
{
#ifndef PUBLIC_ACCESS
	if (fp && !inhibit_logging)
	{
		char *local_line;
		int len = strlen(line) * 2 + 1;
		local_line = alloca(len);
		strcpy(local_line, line);

		/* Do this first */
		if (mangler)
			mangle_line(local_line, mangler, len);
		else if (!get_int_var(MIRCS_VAR))
		{
			char *tmp = alloca(strlen(local_line) + 1);
			strip_control(local_line, tmp);
			strcpy(local_line, tmp);
		}

		
		fprintf(fp, "%s\n", local_line);
		fflush(fp);
	}
#endif
}

--- NEW FILE: irc.c ---
/*
 * ircII: a new irc client.  I like it.  I hope you will too!
 *
 * Written By Michael Sandrof
 * Copyright(c) 1990 
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#define __irc_c

#include "irc.h"
#include "struct.h"

static char cvsrevision[] = "$Id: irc.c,v 1.1.1.2 2003/06/11 07:00:42 root Exp $";
CVS_REVISION(irc_c)

#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
[...1655 lines suppressed...]
		display_server_list();
	start_memdebug();	
		
	set_input(empty_string);
	set_input_prompt(current_window, get_string_var(INPUT_PROMPT_VAR), 0);

	/* We now move from run level 1 to run level 2
	 * signifying that we are in normal operation mode.
	 */
	run_level = 2;

	for (;;)
		io("main");
#ifdef GUI
	gui_exit();
#else
	ircpanic("get_line() returned");
#endif
	return (-((int)0xdead));
}

--- NEW FILE: commands2.c ---
/*
 * Routines within this files are Copyright Colten Edwards 1996
 * Aka panasync on irc.
 * Thanks to Shiek and Flier for helpful hints and suggestions. As well 
 * as code in some cases.
 */
 
#include "irc.h"
static char cvsrevision[] = "$Id: commands2.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(commands2_c)
#include "struct.h"
#include <sys/stat.h>

#if defined(_ALL_SOURCE) || defined(__EMX__) || defined(__QNX__)
#include <termios.h>
#else
#include <sys/termios.h>
#endif
#include <sys/ioctl.h>
[...2853 lines suppressed...]
		cvsfuncs[z](revisionbuf);
		/* If there are args... we look for a matching filename or start string */
		if(args && *args)
		{
			if(my_strnicmp(&revisionbuf[5], args, strlen(args)) == 0)
			{
				say(revisionbuf);
				found = 1;
			}
		}
		else
		{
			say(revisionbuf);
			found = 1;
		}
		z++;
	}
	if(!found)
		say("Could not find any revisions.");
}

--- NEW FILE: expr2.c ---
/*
 * $Id: expr2.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $
 * math.c - mathematical expression evaluation
 * This file is based on 'math.c', which is part of zsh, the Z shell.
 *
 * Copyright (c) 1992-1997 Paul Falstad, All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright notice,
 *    this list of conditions and the two following paragraphs.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimers in the
 *    documentation and/or other materials provided with the distribution
 * 3. The names of the author(s) may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
[...1801 lines suppressed...]
	char	*rest;
	int	call;

	if (!(start = c->ptr))
		return c->ptr;

	for (;;)
	{
		rest = after_expando(start, 0, &call);
		if (*rest != '$')
			break;
		start = rest + 1;
	}

	/*
	 * All done!
	 */
	return rest;
}


--- NEW FILE: dcc.c ---

/*
 * dcc.c: Things dealing with client to client connections. 
 *
 * Copyright(c) 1998 Colten Edwards aka panasync.
 * After alot of unhappiness with the old dcc.c code I took it upon myself
 * to rewrite this code. The only parts not greatly changed by myself was
 * code I had written by me in the first place. ie the ftp code and the 
 * dcc botlink code. Some great improvements in dcc speed were realized
 * using non-blocking connects. All the previous dcc modes/commands are still
 * in place and available. 
 */

#include "irc.h"
static char cvsrevision[] = "$Id: dcc.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $";
CVS_REVISION(dcc_c)
#include "struct.h"

#include <sys/stat.h>
[...4410 lines suppressed...]
	Client->local_port = port;
	Client->flags |= DCC_ACTIVE;
	Client->user = m_strdup(ltoa(Client->local_port));
	set_display_target(NULL, LOG_CURRENT);

	return m_strdup(Client->user);
#endif
	return m_strdup("-1");
}



int close_dcc_number(int number)
{
	if (!check_dcc_socket(number))
		return 0;
	erase_dcc_info(number, 1, NULL);
	close_socketread(number);
	return 1;
}

--- NEW FILE: scr-bx.c ---

/*
 * Copyright Colten Edwards (July 1/1998).
 * This code is for the purpose of re-attaching to a detached BitchX session.
 * The idea for this program was by kasper at efnet after I mentioned the trouble
 * I was having reconnecting to a detached terminal.
 */
 
 /* 
  * Version 1.0 released with BitchX 75
  * $Id: scr-bx.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $
  */

#include "irc.h"
#include "struct.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#ifdef USING_CURSES
#include <curses.h>
#endif
#include <stdarg.h>
#include <string.h>
#include "ircterm.h"
#include "screen.h"
#include "ircaux.h"

#if defined(_ALL_SOURCE) || defined(__EMX__) || defined(__QNX__)
#include <termios.h>
#else
#include <sys/termios.h>
#endif

#include <sys/ioctl.h>



#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif

#ifdef MEM_DEBUG
#include <dmalloc.h>
#endif

#ifdef TRANSLATE
char translation = 0;
unsigned char   transToClient[256];    /* Server to client translation. */
#endif

int dumb_mode = 0;
int already_detached = 1;
int do_check_pid = 0;
char socket_path[500];
char attach_ttyname[500];


struct param 
{
	pid_t	pgrp,
		pid;
	uid_t	uid;
	int	cols;
	int	rows;
	char	tty[80];
	char	cookie[30];
	char	password[80];
	char	termid[81];
};
                                
struct param parm;                                                
char *old_pass = NULL;
Screen *output_screen = NULL, *last_input_screen = NULL, *main_screen = NULL;
char empty_string[] = "";
int foreground = 0;
int use_input = 1;
int use_flow_control = 0;
static int displays = 0;

#define SOCKMODE (S_IWRITE | S_IREAD | (displays ? S_IEXEC : 0))

#ifdef CLOAKED
extern char proctitlestr[140];
extern char **Argv;             /* pointer to argument vector */
extern char *LastArgv;          /* end of argv */
#endif


char	*n_m_strdup (const char *str, const char *module, const char *file, const int line)
{
	char *ptr;
	if (!str)
		str = empty_string;
	ptr = (char *)malloc(strlen(str) + 1);
	return strcpy(ptr, str);
}

void ircpanic(char *string, ...)
{
	return;
}

int get_int_var(int var)
{
	return 1;
}

char *ltoa (long foo)
{
	static char buffer[BIG_BUFFER_SIZE/8+1];
	char *pos = buffer + BIG_BUFFER_SIZE/8-1;
	unsigned long absv;
	int negative;

	absv = (foo < 0) ? (unsigned long)-foo : (unsigned long)foo;
	negative = (foo < 0) ? 1 : 0;

	buffer[BIG_BUFFER_SIZE/8] = 0;
	for (; absv > 9; absv /= 10)
		*pos-- = (absv % 10) + '0';
	*pos = (absv) + '0';

	if (negative)
		*--pos = '-';

	return pos;
}

char *lower(char *str)
{
register char   *ptr = NULL;

	if (str)
	{
		ptr = str;
		for (; *str; str++)
		{
			if (isupper(*str))
				*str = tolower(*str);
		}
	}
	return (ptr);
}

#ifndef HAVE_GETPASS
char *getpass(char *);
char *get_string_var(int var)
{
	return NULL;
}
#endif

#ifdef WINNT
void refresh_screen(int i, char *u)
{
	return;
}
#endif

char *find_tty_name(char *name)
{
static char tty[20];
char *q, *s;
	*tty = 0;
	if ((q = strrchr(name, '/')))
	{
		q++;
		if ((q = strchr(q, '.')))
		{
			q++;
			if ((s = strchr(q, '.')))
				strncpy(tty, q, s-q);
		}
	}
	return tty;
}

char *find_tty_path(char *name)
{
static char ttypath[200];
char *q;
	*ttypath = 0;
	if ((q = strrchr(name, '/')))
		strncpy(ttypath, name, q - name);
	return ttypath;
}

void display_socket_list(char *path, int unl, char *arg)
{
DIR	*dptr;
struct	dirent	*dir;
struct	stat	st;
char buffer[2000];
char *new_path, *p;
int count = 0;
int doit = 0;

	new_path = alloca(strlen(path)+1);
	strcpy(new_path, path);
	if ((p = strrchr(new_path, '/')))
		*p = 0;
	if (!(dptr = opendir(new_path)))
	{
		fprintf(stderr, "No such directory %s\r\n ", new_path);
		exit(1);
	}
	while ((dir = readdir(dptr)))
	{
		doit = 0;
		if (!dir->d_ino)
			continue;
		if (dir->d_name[0] == '.')
			continue;
		sprintf(buffer, "%s/%s", new_path, dir->d_name);
		if ((stat(buffer, &st) == -1))
			continue;
		if (arg && strstr(dir->d_name, arg))
			doit++;
		if (!count && !unl)
			fprintf(stderr, "There is more than one sockets available - \r\n");
		else if (!count)
			fprintf(stderr, "unlinking the following\r\n");
		count++;
		if (unl)
		{
			if (!((st.st_mode & 0700) == 0600) || doit)
			{
				fprintf(stderr, "%30s\r\n", dir->d_name);
				unlink(buffer);
			}
		} 
		else if ((!doit && !arg) || (doit && arg))
			fprintf(stderr, "%30s %s\r\n", dir->d_name, ((st.st_mode & 0700) == 0600) ? "detached":"Attached or dead");
	}
	if (!count)
		fprintf(stderr, "No sockets to attach too\r\n");
	closedir(dptr);
	exit(1);
}

char *find_detach_socket(char *path, char *name)
{
char	*new_path;
DIR	*dptr;
struct	dirent	*dir;
struct	stat	st;
char *ret = NULL, *p;
int count = 0;
	new_path = alloca(strlen(path)+1);
	strcpy(new_path, path);
	if ((p = strrchr(new_path, '/')))
		*p = 0;
	else
		return NULL;
	if (!(dptr = opendir(new_path)))
		return NULL;
	ret = malloc(2000);
	*ret = 0;
	while ((dir = readdir(dptr)))
	{
		*ret = 0;
		if (!dir->d_ino)
			continue;
		if (dir->d_name[0] == '.')
			continue;
		sprintf(ret, "%s/%s", new_path, dir->d_name);
		p = strrchr(ret, '/'); p++;
		if ((stat(ret, &st) == -1) || (st.st_uid != getuid()) || S_ISDIR(st.st_mode))
		{
			*ret = 0;
			continue;
		}
		if (name)
		{
			char *pid, *n_tty, *h_name;
			pid = alloca(strlen(p)+1);
			strcpy(pid, p);
			n_tty = strchr(pid, '.'); *n_tty++ = 0;
			h_name = strchr(n_tty, '.'); *h_name++ = 0;
			if (strcmp(name, pid))
			{
				if (strcmp(n_tty, name))
				{
					if (strcmp(h_name, name))
					{
						if (strcmp(p, name))
						{
							if (!strstr(p, name))
							{
								*ret = 0;
								continue;
							}
						}
					}
				}
			}
		}
		if ((st.st_mode & 0700) == 0600)
			break;
		count++;
		*ret = 0;
	}
	closedir(dptr);
	if (ret && !*ret)
	{
		free(ret);
		ret = NULL;
	}
	switch (count)
	{
		case 0:
			break;
		case 1:
			break;
		default:
			display_socket_list(path, 0, name);
			if (ret) free(ret);
			ret = NULL;
	}
	return ret;
}

void charset_ibmpc (void)
{
	fwrite("\033(U", 3, 1, stdout);	/* switch to IBM code page 437 */
}

SIGNAL_HANDLER(handle_pipe)
{
	
	term_cr();
	term_clear_to_eol();
	term_reset2();
	fprintf(stdout, "\rdetached from %s. To re-attach type scr-bx %s\n\r", attach_ttyname, old_pass? "password":"");
	fflush(stdout);
	exit(0);
}

SIGNAL_HANDLER(handle_hup)
{
	term_cr();
	term_clear_to_eol();
	term_reset2();
	fprintf(stdout, "\r");
	fflush(stdout);
	exit(0);
}

volatile int ctrl_c = 0;

SIGNAL_HANDLER(handle_ctrlc)
{
	ctrl_c++;
}

/* set's socket options */
void set_socket_options (int s)
{
	int	opt = 1;
	int	optlen = sizeof(opt);
#ifndef NO_STRUCT_LINGER
	struct linger	lin;

	lin.l_onoff = lin.l_linger = 0;
	setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&lin, optlen);
#endif

	setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, optlen);
	opt = 1;
	setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, optlen);
}


char *get_cookie(char *name)
{
	static char cookie[80];
	FILE *fp = NULL;
	*cookie = 0;
	if ((fp = fopen(name, "r")))
	{
		fread(cookie, 40, 1, fp);
		fclose(fp);
		if (*cookie)
			cookie[strlen(cookie)-1] = 0;
	}
	return cookie;
}

void reattach_tty(char *tty, char *password)
{
int s = -1;
char *name;
struct sockaddr_in addr;
struct hostent *hp;
int len = 0;
fd_set rd_fd;
struct timeval tm = {0};
char chr_c[] = "\003";

/* 
 * this buffer has to be big enough to handle a full screen of 
 * information from the detached process.
 */
unsigned char buffer[6 * BIG_BUFFER_SIZE+1];
char *p;
int port = 0;
#if defined (TIOCGWINSZ)
struct winsize window;
#endif
	memset(&parm, 0, sizeof(struct param));

	if (!(name = find_detach_socket(socket_path, tty)))
	{
		fprintf(stderr, "No detached process to attach too\r\n");
		_exit(1);
	}

	strcpy(parm.cookie, get_cookie(name));
	if (!*parm.cookie)
		_exit(1);
	if ((p = strrchr(name, '/')))
		p++;
	sscanf(p, "%d.%*s.%*s", &port);
	displays = 1;
	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		displays = 0;
		_exit(1);
	}

	chmod(name, SOCKMODE);
	set_socket_options(s);
	memset(&addr, 0, sizeof(struct sockaddr_in));
	addr.sin_port = htons(port);
	addr.sin_family = AF_INET;
	if((hp = gethostbyname("localhost")))
		memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
	else
		inet_aton("127.0.0.1", (struct in_addr *)&addr.sin_addr);
	if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
	{
		fprintf(stderr, "connection refused for %s\r\n", name);
		_exit(1);
	}

	parm.pid = getpid();
	parm.pgrp = getpgrp();
	parm.uid = getuid();
	strcpy(parm.tty, ttyname(0));
	strncpy(parm.termid, getenv("TERM"), 80);
	if (password) 
		strncpy(parm.password, password, 60);
	fprintf(stderr, "attempting to wakeup %s\r\n", find_tty_name(name));
#if defined (TIOCGWINSZ)
	if (ioctl(0, TIOCGWINSZ, &window) > -1)
	{
		parm.cols = window.ws_col;
		parm.rows = window.ws_row;
	}
	else
#endif
	{
		parm.cols = 79;
		parm.rows = 25;
	}
	write(s, &parm, sizeof(struct param));	
	sleep(2);
	alarm(15);
	len = read(s, &parm, sizeof(struct param));
	alarm(0);
	if (len <= 0)
	{
		fprintf(stderr, "error reconnecting to %s\r\n", find_tty_name(name));
		displays = 0;
		chmod(name, SOCKMODE);
		exit(1);
	}
	unlink(name);

	term_init(parm.termid);
	set_term_eight_bit(1);
	charset_ibmpc();
	term_clear_screen();
	term_resize();
	term_move_cursor(0,0);

	my_signal(SIGPIPE, handle_pipe, 0);
	my_signal(SIGINT,  handle_ctrlc, 0);
	my_signal(SIGHUP,  handle_hup, 0);

	/*
	 * according to MHacker we need to set errno to 0 under BSD.
	 * for some reason we get a address in use from a socket 
	 *
	 */
	errno = 0;
	while (1)
	{
		FD_ZERO(&rd_fd);
		FD_SET(0, &rd_fd);
		FD_SET(s, &rd_fd);
		tm.tv_sec = 2;
		
		switch(select(s+1, &rd_fd, NULL, NULL, &tm))
		{
			case -1:
				if (ctrl_c)
				{
					write(s, chr_c, 1);
					ctrl_c = 0;
				}
				else if (errno != EINTR)
				{
					close(s);
					_exit(1);
				}
				break;
			case 0:
				break;
			default:
			{
				if (FD_ISSET(0, &rd_fd))
				{
					len = read(0, buffer, sizeof(buffer)-1);
					write(s, buffer, len);
				}
				if (FD_ISSET(s, &rd_fd))
				{
					len = read(s, buffer, sizeof(buffer)-1);
					write(1, buffer, len);
				}
			}
		}
	}
	close(s);
	fprintf(stderr, "Never should have got here");
	_exit(1);			 

	return; /* error return */
}

char *stripdev(char *ttynam)
{
	if (ttynam == NULL)
		return NULL;
#ifdef SVR4
  /* unixware has /dev/pts012 as synonym for /dev/pts/12 */
	if (!strncmp(ttynam, "/dev/pts", 8) && ttynam[8] >= '0' && ttynam[8] <= '9')
	{
		static char b[13];
		sprintf(b, "pts/%d", atoi(ttynam + 8));
		return b;
	}
#endif /* SVR4 */
	if (!strncmp(ttynam, "/dev/", 5))
		return ttynam + 5;
	return ttynam;
}


void init_socketpath(void)
{
#if !defined(__EMX__) && !defined(WINNT)
struct stat st;
extern char socket_path[], attach_ttyname[];

	sprintf(socket_path, "%s/.BitchX/screens", getenv("HOME"));
	if (access(socket_path, F_OK))
		return;
	if (stat(socket_path, &st) != -1)
	{
		char host[BIG_BUFFER_SIZE+1];
		char *ap;
		if (!S_ISDIR(st.st_mode))
			return;
		gethostname(host, BIG_BUFFER_SIZE);
		if ((ap = strchr(host, '.')))
			*ap = 0;
		ap = &socket_path[strlen(socket_path)];
		sprintf(ap, "/%%d.%s.%s", stripdev(attach_ttyname), host);
		ap++;
		for ( ; *ap; ap++)
			if (*ap == '/')
				*ap = '-';
	}	        
#endif
}


char *old_tty = NULL;
void parse_args(int argc, char **argv)
{
int ac = 1;
int disp_sock = 0;

	for (; ac < argc; ac++)
	{
		
		if (!strncasecmp(argv[ac], "tty", 3))
		{
			old_tty = malloc(strlen(argv[ac])+1);
			strcpy(old_tty, argv[ac]);
		}
		else if (argv[ac][0] == '-' && argv[ac][1] == 'p')
		{
			char *pass;
			pass = getpass("Enter password : ");
			old_pass = malloc(strlen(pass)+1);
			strcpy(old_pass, pass);
		}
		else if (argv[ac][0] == '-' && argv[ac][1] == 'h')
		{
			char *p;
			if ((p = strrchr(argv[0], '/')))
				p++;
			else
				p = argv[0];
			fprintf(stderr, "Usage %s: [tty] [-p] [-h] [-l]\r\n\t\ttty is the name of a tty\r\n\t\t-p to specify a password\r\n\t\t-l to list available sockets\r\n\t\t-w to wipe out dead sockets\r\n", p);
			exit(0);
		}
		else if (argv[ac][0] == '-' && argv[ac][1] == 'l')
			disp_sock = 1;
		else if (argv[ac][0] == '-' && argv[ac][1] == 'w')
			disp_sock = 2;
		else if (!old_tty)
		{
			old_tty = malloc(strlen(argv[ac])+1);
			strcpy(old_tty, argv[ac]);
		}
	}
	if (disp_sock)
		display_socket_list(socket_path, disp_sock - 1, old_tty);
}


int main(int argc, char **argv)
{
#ifdef MEM_DEBUG
	dmalloc_debug(0x1df47dfb);
#endif
	*socket_path = 0;
	strcpy(attach_ttyname, ttyname(0));
	init_socketpath();
	parse_args(argc, argv);
        chdir(getenv("HOME"));
	reattach_tty(old_tty, old_pass);
	return 0;
}




More information about the dslinux-commit mailing list