/*
 * 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 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */


#include "irc.h"

#include <sys/stat.h>

#ifdef ESIX
# include <lan/net_types.h>
#endif

#include "parse.h"
#include "ircterm.h"
#include "server.h"
#include "chelp.h"
#include "commands.h"
#include "crypt.h"
#include "vars.h"
#include "ircaux.h"
#include "lastlog.h"
#include "window.h"
#include "screen.h"
#include "whois.h"
#include "hook.h"
#include "input.h"
#include "ignore.h"
#include "keys.h"
#include "names.h"
#include "alias.h"
#include "history.h"
#include "funny.h"
#include "ctcp.h"
#include "dcc.h"
#include "output.h"
#include "exec.h"
#include "notify.h"
#include "numbers.h"
#include "status.h"
#include "if.h"
#include "help.h"
#include "stack.h"
#include "queue.h"
#include "timer.h"
#include "list.h"
#include "userlist.h"
#include "misc.h"
#include "alist.h"
#include "tcl_bx.h"
#include "hash2.h"
#include "cset.h"
#include "notice.h"

#ifdef TRANSLATE
#include "translat.h"
#endif

#ifdef WANT_CD
#include "cdrom.h"
#endif

#ifdef WANT_TCL
Tcl_Interp *interp = NULL;
#endif

#define COMMENT_HACK 

static	int	save_which;
static	int	save_do_all;
	int	user_changing_nickname;
extern	int	doing_notice;

Virtuals *virtuals = NULL;

static	void	oper_password_received _((char *, char *));

int	no_hook_notify = 0;
char	*last_sent_msg_body = NULL;

extern	char	*forwardnick;
extern char	cx_function[];

/* used with /save */
#define	SFLAG_ALIAS	0x0001
#define	SFLAG_BIND	0x0002
#define	SFLAG_ON	0x0004
#define	SFLAG_SET	0x0008
#define	SFLAG_NOTIFY	0x0010
#define	SFLAG_DIGRAPH	0x0020

/* The maximum number of recursive LOAD levels allowed */
#define MAX_LOAD_DEPTH 10

/* recv_nick: the nickname of the last person to send you a privmsg */
	char	*recv_nick = NULL;

/* sent_nick: the nickname of the last person to whom you sent a privmsg */
	char	*sent_nick = NULL;
	char	*sent_body = NULL;

/* Used to keep down the nesting of /LOADs and to determine if we
 * should activate the warning for /ONs if the NOVICE variable is set.
 */
	int	load_depth = 0;


	int	interactive = 0;
	
/* Used to prevent global messaging */
extern	int	who_on_join;
extern  int	doing_who;

typedef	struct	WaitCmdstru
{
	char	*stuff;
	struct	WaitCmdstru	*next;
}	WaitCmd;

static	WaitCmd	*start_wait_list = NULL,
		*end_wait_list = NULL;

char	lame_wait_nick[] = "#LW#";
char	wait_nick[] = "#W#";



	IrcCommand *find_command _((char *, int *));


#ifdef WANT_DLL
	IrcCommandDll *find_dll_command _((char *, int *));
#endif


AJoinList *ajoin_list = NULL;

/*
 * irc_command: all the availble irc commands:  Note that the first entry has
 * a zero length string name and a null server command... this little trick
 * makes "/ blah blah blah" to always be sent to a channel, bypassing queries,
 * etc.  Neato.  This list MUST be sorted.
 */

#ifdef WANT_DLL
IrcCommandDll *dll_commands = NULL;
#endif

char relay_help[] = "%R[%n-list|-kick|-wall|-wallop|-msg|-notice|-topic|-kboot|-ansi|-kill|-help%R]%n %Y<%n#|channel|nick%Y> <%nchannel|nick%Y>";
char scripting_command[] = "- Scripting command";

static IrcCommand irc_command[] =
{
	{ "",		empty_string,	do_send_text,		0,	NULL },
	{ "#",		NULL,		comment, 		0,	NULL },
	{ "4OP",	NULL,		do_4op,			0,	"%Y<%Cnick%Y>%n\n- Sets mode +oooo on %Y<%Cnick%Y>%n"},
	{ ":",		NULL,		comment, 		0,	NULL },
        { "ABORT",      NULL,           abortcmd,               0,	"- Saves IRCII settings then exits IRC" },
	{ "ABOUT",	NULL,		about,			0,	"- Shows yet another ansi screen with greets to people who have contributed to the %PBitchX%n project"},
	{ "ADDBOT",	"AddBot",	bot,			0,	"%Y<%Cnick%G|%Cnick%G!%nuser%Y@%nhostname%Y>%n %Y<%Bchannel%G|%Y*>%n %R[%nauto-op%R]%n %R[%nprotection%R]%n\n   - Adds %Y<%Cnick%G|%Cnick%G!%nuser%Y@%nhostname%Y>%n to your bot list on %Y<%Bchannel%G|%Y*>%n with %R[%nauto-op%R]%n and %R[%nprotection%R]%n levels"},
	{ "ADDFORWARD",	"AddForward", 	do_forward,		0,	"%Y<%Bchannel%G|%Cnick%Y>%n\n - Forward all messages to %Y<%Bchannel%G|%Cnick%Y>%n" },
	{ "ADDIDLE",	"AddIdle",	addidle,		0,	"%Y<%Bchannel%Y>%n %R[%nseconds%R]%n\n- Adds %Y<%Bchannel%Y>%n as idle channel with %R[%nseconds%R]%n" },
	{ "ADDLAMENICK","AddLameNick",	add_bad_nick,		0,	"%Y<%Cnick%G|%Cnick%C nick nick%Y>%n\n - Adds %Y<%Cnick%Y>%n to your lame nicklist, bans nick!*@*"},
	{ "ADDNOFLOOD",	"AddNoFlood",	add_no_flood,		0,	"%Y<%Cnick%Y>%n\n - Adds %Cnick%n to your no flood list" },
	{ "ADDSHIT",	"AddShit",	add_shit,		0,	"%Y<%Cnick%G|%Cnick%G!%nuser%Y@%nhostname%Y>%n %Y<%Bchannel%G|%Y*>%n %R[%nshitlevel%R]%n %R[%nreason%R]%n\n- Add %Y<%Cnick%G|%Cnick%G!%nuser%Y@%nhostname%Y>%n to your shitlist on %Y<%Bchannel%G|%Y*>%n with %R[%nshitlevel%R]%n and for %R[%nreason%R]%n" },
	{ "ADDUSER",	"AddUser",	add_user,		0,	"%Y<%Cnick%G|%Cnick%G!%nuser%Y@%nhostname%Y>%n %Y<%Bchannel%G|%Y*>%n %R[%nuserlevel%R]%n %R[%nauto-op%R]%n %R[%nprotection%R]%n %R[%npassword%R]%n\n - Adds %Y<%Cnick%G|%Cnick%G!%nuser%Y@%nhostname%Y>%n on %Y<%Bchannel%G|%Y*>%n with %R[%nuserlevel%R]%n %R[%nauto-op%R]%n and %R[%nprotection%R]%n levels to your userlist" },
	{ "ADDWORD",	"AddWord",	add_ban_word,		0,	"%Y<%Bchannel%G|%Y*>%n %Y<%nword%y(%ns%y)%Y>%n\n- Adds %Y<%nword%y(%ns%y)%Y>%n to wordlist, anyone saying it in %Y<%Bchannel%G|%Y*>%n will be kicked" },
	{ "ADMIN",	"ADMIN",	send_comm, 		0,	"%R[%nserver%R]%n\n- Shows the iRC Administration information on current server or %R[%nserver%R]%n" },
	{ "AJOIN",	"AJoin",	auto_join,		0,	"%Y<%Bchannel%y(%Bs%y)%Y>%n\n- Add %Y<%Bchannel%y(%Bs%y)%Y>%n to AJoin list%P;%n it joins %Y<%Bchannel%y(%Bs%y)%Y>%n on startup or change of server" },
	{ "AJOINLIST",	"AJoinList",	auto_join,		0,	"- Display list of ajoin channels" },
	{ "ALIAS",	"0",		aliascmd,		0,	scripting_command },
	{ "ASSIGN",	"1",		assigncmd,		0,	scripting_command },
	{ "AWAY",	"Away",		away,			0,	"%R[%nreason%R]%n\n- Sets you away on server if %R[%nreason%R]%n else sets you back" },
	{ "AWAYLOG",	"AwayLog",	awaylog,		0,	"%R[%nALL|MSGS|NOTICES|...%R]%n\n- Displays or changes what you want logged in your lastlog. This is a comma separated list" },
	{ "B",		NULL,		ban,			0,	"See %YBAN%n" },
	{ "BACK",	"Back",		back,			0,	"- Sets you back from being away" },
	{ "BAN",	NULL,		ban,			0,	"%Y<%Cnick%G|%Cnick%G!%nuser%y@%nhostname%Y>%n\n- Ban %Y<%Cnick%G|%Cnick%G!%nuser%y@%nhostname%Y>%n from current channel" },
	{ "BANSTAT",	NULL,		banstat,		0,	"%R[%Bchannel%R]%n\n- Show bans on current channel or %R[%Bchannel%R]%n" },
	{ "BANTYPE",	NULL,		bantype,		0,	"%W/%nbantype %Y<%nNormal%G|%nBetter%G|%nHost%G|%nDomain%G|%nScrew%Y>%n\n- When a ban is done on a nick, it uses %Y<%nbantype%Y>%n" },
	{ "BANWORDS",	NULL,		add_ban_word,		0,	"%Y<%nchannel|*%Y>%n word(s)\n- Adds word or words to the banned words list for %Y<%nchannel%Y>" },
	{ "BEEP",	NULL,		beepcmd,		0,	"- Creates a beep noise" },
	{ "BHELP",	NULL,		chelp,			0,	"%Y<%nhelp%W|%nindex%W|%nother%Y>%n\n - Externel help command for use with ~/.BitchX/BitchX.help" },
	{ "BIND",	NULL,		bindcmd,		0,	"- Command to bind a key to a function" },
	{ "BK",		NULL,		kickban,		0,	"%Y<%Cnick%Y>%n %R[%nreason%R]%n\n- Deops, bans and kicks %Y<%Cnick%Y>%n for %R[%nreason%R]%n" },
	{ "BKI",	"Bki",		kickban,		0,	"%Y<%Cnick%Y>%n %R[%nreason%R]%n\n- Deops, bans, kicks and ignores %Y<%Cnick%Y>%n for %R[%nreason%R]%n" },
	{ "BLESS",	NULL,		blesscmd,		0,	scripting_command },
	{ "BOT",	"Bot",		bot,			0,	"- Adds to internal botlist" },
	{ "BOTLIST",	"BotList",	bot,			0,	"- Show internal botlist" },
	{ "BYE",	"QUIT",		e_quit,			0,	"- Quit Irc"},
	{ "C",		"MODE",		send_mode,		0,	"%Y<%nnick|channel%Y> <%nmode%Y>%n\n- Sets a mode for a channel or nick"},
	{ "CALL",	NULL,		e_call,			0,	scripting_command },
	{ "CBOOT",	"Cboot",	cboot,			0,	"%Y<%nnick%Y>%n %R[%nreason%R]%n\n- Kicks a %Y<%nnick%Y>%n off the TwilightZone with %R[%nreason%R]" },
	{ "CD",		NULL,		cd,			0,	"%Y<%ndir%Y>%n\n - Changes current directory to %Y<%ndir%Y>%n or prints current directory" },
	{ "CDCC",	NULL,		cdcc,			0,	"%Y<%ncommand%Y>%n\n- Channel, DOffer, Echo, Load, List, MinSpeed, Notice, Offer, PList, Queue, ReSend, Save, Type Describe Note" },
#ifdef WANT_CD
	{ "CDEJECT",	NULL,		cd_eject,		0,	NULL },
	{ "CDHELP",	NULL,		cd_help,		0,	NULL },
	{ "CDLIST",	NULL,		cd_list,		0,	NULL },
	{ "CDPAUSE",	NULL,		cd_pause,		0,	NULL },
	{ "CDPLAY",	NULL,		cd_play,		0,	NULL },
	{ "CDSTOP",	NULL,		cd_stop,		0,	NULL },
	{ "CDVOL",	NULL,		cd_volume,		0,	NULL },
#endif
	{ "CHANNEL",	"JOIN",		e_channel,		0,	"- Shows information on the channels, modes and server you are on" },
	{ "CHANST",	NULL,		channel_stats,		0,	"%Y<%n-ALL%Y> %R[%Bchannel%R]%n\n- Shows statistics on current channel or %R[%Bchannel%R]%n" },
	{ "CHAT",	"Chat",		chat,			0,	"%Y<%nNick%Y>%n\n- Attempts to dcc chat nick" },
	{ "CHGAOP",	"ChgAop",	change_user,		0,	"%Y<%Cnick%Y>%n <auto-oplevel>" },
	{ "CHGCHAN",	"ChgChan",	change_user,		0,	"%Y<%Cnick%Y>%n %Y<%Bchannel%Y>%n" },
	{ "CHGLEVEL",	"ChgLevel",	change_user,		0,	"%Y<%Cnick%Y>%n %Y<%nlevel%Y>%n" },
	{ "CHGPASS",	"ChgPass",	change_user,		0,	"%Y<%Cnick%Y>%n %Y<%nprotection-level%Y>%n" },
	{ "CHGPROT",	"ChgProt",	change_user,		0,	"%Y<%Cnick%Y>%n %Y<%nprotection-level%Y>%n" },
	{ "CHGUH",	"ChgUH",	change_user,		0,	"%Y<%Cnick%Y>%n %Y<%nuserhost%Y>%n" },
	{ "CHOPS",	"Chops",	users,			0,	"%R[%Bchannel%R]%n\n- Shows, in a full format, all the nicks with op status" },
	{ "CLEAR",	NULL,		my_clear,		0,	"- Clears the screen" },
	{ "CLEARAUTO",	"CLEARAUTO",	clear_tab,		0,	"- Clears all the nicks in the auto-response list" },
	{ "CLEARLOCK",	"ClearLock",	mode_lock,		0,	"%Y<%Cchannel%G|%Y*>%n\n- Unlocks the mode lock for %Y<%Cchannel%G|%Y*>%n" },
	{ "CLEARTAB",	NULL,		clear_tab,		0,	"- Clears the nicks in the tabkey list" },
	{ "CLINK",	"Clink",	clink,			0,	"%Y<%Cnick%Y>%n %Y<%ntext%Y>%n" },
	{ "CMSG",	"Cmsg",		cmsg,			0,	"%Y<%Cnick%Y>%n %Y<%ntext%Y>%n\n- While in the TwilightZone, a private message will be sent to %Y<%Cnick%Y>%n with %Y<%ntext%Y>%n" },
	{ "CONNECT",	"CONNECT",	send_comm,		0,	"%Y<%nserver1%Y>%n %Y<%nport%Y>%n %R[%nserver2%R]%n\n%Y*%n Requires irc operator status" },
	{ "CSAY",	"Csay",		csay,			0,	"%Y<%ntext%Y>%n" },
	{ "CSET",	"Cset",		cset_variable,		0,	"%R[%n*|channel|chan*%R]%n\n- changes variables that affect a channel or displays them" },
	{ "CTCP",	NULL,		ctcp,			0,	"%Y<%Cnick%Y>%n %Y<%nrequest%Y>%n\n- CTCP sends %Y<%Cnick%Y>%n with %Y<%nrequest%Y>%n" },
	{ "CTOGGLE",	NULL,		toggle_xlink,		0,	NULL },
	{ "CWHO",	"Cwho",		cwho,			0,	"- Lists the clients and bots connected to the TwilightZone" },
	{ "CWHOM",	"Cwhom",	cwho,			0,	"- Lists the clients and bots connected to the TwilightZone" },
	{ "CYCLE",	NULL,		cycle,			0,	"%R[%Bchannel%R]%n\n- Leaves current channel or %R[%Bchannel%R]%n and immediately rejoins" },
	{ "D",		NULL,		describe,		0,	"See %YDESCRIBE%n" },
	{ "DATE",	"TIME",		send_comm,		0,	"- Shows current time and date from current server" },
	{ "DBAN",	NULL,		unban,			0,	"- Clears all bans on current channel" },
	{ "DC",		NULL,		init_dcc_chat,		0,	"%Y<%Cnick%Y>%n\n- Starts a DCC CHAT to %Y<%Cnick%Y>%n" },
	{ "DCA",	"Dca",		dcx,			0,	"- Kills all dcc connections" },
	{ "DCC",	NULL,		dcc,			0,	"try /dcc help" },
	{ "DCCCRASH",	NULL,		dcc_crash,		0,	NULL },
	{ "DCG",	"Dcg",		dcx,			0,	"%Y<%nnick%Y>%n\n- Kills all dcc gets to %Y<%nnick%Y>" },
	{ "DCS",	"Dcs",		dcx,			0,	"%Y<%nnick%Y>%n\n- Kills all dcc sends to %Y<%nnick%Y>" },
	{ "DCX",	"Dcx",		dcx,			0,	"%Y<%nnick%Y>%n\n- Kills all dcc chats to %Y<%nnick%Y>" },
	{ "DEBUG",	NULL,		e_debug,		0,	"%R[%nflags%R]%n\n- Sets various debug flags in the client" },
	{ "DEBUGHASH",	NULL,		show_hash,		0,	NULL },
	{ "DEOP",	NULL,		dodeop,			0,	"%Y<%C%nnick(s)%Y>%n\n- Deops %Y<%Cnick%y(%Cs%y)%Y>%n" },
	{ "DEOPER",	NULL,		deop,			0,	"%Y*%n Requires irc operator status\n- Removes irc operator status" },
	{ "DESCRIBE",	NULL,		describe,		0,	"%Y<%Cnick%G|%Bchannel%Y>%n %Y<%naction%Y>%n\n- Describes to %Y<%Cnick%G|%Bchannel%Y>%n with %Y<%naction%Y>%n" },
	{ "DEVOICE",	"DeVoice",	dodeop,			0,	"%Y<%C%nnick(s)%Y>%n\n- de-voices %Y<%Cnick%y(%Cs%y)%Y>%n" },
	{ "DF",		"df",		exec_cmd,		0,	"- Show disk space usage" },
	{ "DIE",	"DIE",		send_comm,		0,	"%Y*%n Requires irc operator status\n- Kills the IRC server you are on" },
#ifdef TRANSLATE 
	{ "DIGRAPH",	NULL,		digraph,		0,	NULL },
#endif
	{ "DISCONNECT",	NULL,		disconnectcmd,		0,	"- Disconnects you from the current server" },
	{ "DME",	"dme",		me,			0,	"<action>\n- Sends an action to current dcc" },
	{ "DNS",	NULL,		nslookup,		0,	"%Y<%nnick|hostname%y>%n\n- Attempts to nslookup on nick or hostname"},
	{ "DO",		NULL,		docmd,			0,	scripting_command },
	{ "DOP",	NULL,		dodeop,			0,	"- See deop" },
	{ "DS",		NULL,		dcc_stat_comm,		0,	"- Displays some dcc file transfer stats" },
	{ "DUMP",	"Dump",		dumpcmd,		0,	"%Y<%ntype%Y>%n\n- Dumps %Y<%ntype%Y>%n to screen\n%Y<%ntype%Y>%n:\n%YAlias%n          %YAll  %n          %YBind %n\n%YFile %n          %YOn   %n          %YVar  %n" },
	{ "ECHO",	NULL,		my_echo,		0,	NULL },
	{ "ENCRYPT",	NULL,		encrypt_cmd,		0,	NULL },
	{ "EVAL",	NULL,		evalcmd,		0,	scripting_command },
	{ "EXEC",	NULL,		execcmd,		0,	"%Y<%ncommand%Y>%n\n- Executes %Y<%ncommand%Y>%n with the shell set from %PSHELL%n" },
	{ "EXIT",	"QUIT",		e_quit,			0,	"- Quits IRC" },
	{ "FE",		"Fe",		fe,			0,	scripting_command },
	{ "FEC",	"Fec",		fe,			0,	scripting_command },
	{ "FINGER",	NULL,		finger,			0,	"%Y<%Cnick%Y>%n\n- Fetches finger info on %Y<%Cnick%Y>%n" },
	{ "FK",		"FK",		masskick,		0,	"%Y<%Cnick%G!%nuser%Y@%nhostname%Y>%n%R[%nreason%R]%n\nFinds clients matching %Y<%Cnick%G!%nuser%Y@%nhostname%Y>%n and immediately kicks them from current channel for %R[%nreason%R]%n" },
#if 0
	{ "FKLINE",	NULL,		statkgrep,		0,	"%Y<%Cnick%G|%Cnick%G!%nuser%Y@%nhostname%Y>%n%R[%nserver%R]%n\n- Finds K-line(s) matching %Y<%Cnick%G|%Cnick%G!%nuser%Y@%nhostname%Y>%n on current server or %R[%nserver%R]%n" },
#endif
	{ "FLUSH",	NULL,		flush,			0,	"- Flush ALL server output" },
	{ "FOR",	NULL,		forcmd,			0,	scripting_command },
	{ "FOREACH",	NULL,		foreach,		0,	scripting_command },
	{ "FORWARD",	"Forward", 	do_forward,		0,	"%Y<%Cnick%Y>%n\n- Forwards all messages to %Y<%Cnick%Y>%n" },
	{ "FPORTS",	NULL,		findports,		0,	"%Y<%nhostname%Y>%n %R[%Y<%nlowport%Y>%n %Y<%nhighport%Y>%R]%n\n- Attempts to find ports on %Y<%nhostname%Y>%n on %R[%Y<%nlowport%Y>%n %Y<%n %Y<%nhighport%Y>%R]%n" },
	{ "FPROT",	"FProt",	tog_fprot,		0,	"- Toggles flood protection to be either on or off" },
	{ "FSET",	"Fset",		fset_variable,		0,	NULL },
	{ "FTP",	"Ftp",		init_ftp,		0,	"%Y<%nhostname%Y> %R[%nlogin%R] [%npassword%R] [%n-p port%R]%n\n- Open a ftp connection to a host" },
	{ "FUCK",	"Fuck",		kickban,		0,	"%Y<%Cnick%Y>%n %R[%nreason%R]%n\n- Deops, bans and kicks %Y<%Cnick%Y>%n for %R[%nreason%R]%n" },
	{ "FUCKEM",	NULL,		fuckem,			0,	NULL },
	{ "HASH",	"HASH",		send_comm,		0,	NULL },
	{ "HELP",	NULL,		help,			0,	NULL },
	{ "HISTORY",	NULL,		history,		0,	"- Shows recently typed commands" },
	{ "HOOK",	NULL,		hookcmd,		0,	scripting_command },
	{ "HOST",	"USERHOST",	userhost,		0,	"- Shows host of yourself or %R[%Cnick%R]%n" },
	{ "HOSTNAME",	"HOSTNAME",	e_hostname,		0,	"%Y<%nhostname%Y>%n\n- Shows list of possible hostnames with option to change it on virtual hosts" },
	{ "I",		"INVITE",	do_invite,		0,	"- See %YINVITE%n" },
	{ "IF",		"IF",		ifcmd,			0,	scripting_command },
	{ "IG",		"Ig",		do_ig,			0,	"+%G|%n-%Y<%Cnick%Y>%n\n- Ignores ALL except crap and public of nick!host matching %Y<%Cnick%Y>%n" },
	{ "IGH",	"IgH",		do_ig,			0,	"+%G|%n-%Y<%Cnick%Y>%n\n- Ignores ALL except crap and public of hostname matching %Y<%Cnick%Y>%n" },
	{ "IGHT",	"IgHt",		do_ig,			0,	"+%G|%n-%Y<%Cnick%Y>%n\n- Ignores ALL except crap and public of hostname matching %Y<%Cnick%Y>%n and expires this on a timer"},
	{ "IGNORE",	NULL,		ignore,			0,	NULL },
	{ "IGT",	"Igt",		do_ig,			0,	"+%G|%n-%Y<%Cnick%Y>%n\n- Ignores ALL except crap and public of nick!host matching %Y<%Cnick%Y>%n and expires this on a timer"  },
	{ "INFO",	"INFO",		info,			0,	"- Shows current client info" },
	{ "INPUT",	"Input",	inputcmd,		0,	scripting_command  },
	{ "INPUT_CHAR", "Input_Char",	inputcmd,		0,	scripting_command },
	{ "INVITE",	"INVITE",	do_invite,		0,	"%Y<%Cnick%Y>%n %R[%Bchannel%R]%n\n- Invites %Y<%Cnick%Y>%n to current channel or %R[%Bchannel%R]%n" },
	{ "IRCHOST",	"HOSTNAME",	e_hostname,		0,	"%Y<%nhostname%Y>%n\n- Shows list of possible hostnames with option to change it on virtual hosts"  },
	{ "IRCNAME",	NULL,		realname_cmd,		0,	NULL },
	{ "IRCUSER",	NULL,		set_username,		0,	"<username>\n- Changes your <username>" },
	{ "ISON",	"ISON",		ison,			0,	NULL },
	{ "J",		"JOIN",		e_channel,		0,	"%Y<%nchannel%Y> %R[%nkey%R]%n\n- Joins a %Y<%nchannel%Y>" },
	{ "JNW",	"Jnw",		jnw,			0,	"%Y<%nchannel%Y> %R[%nkey%R]%n %R[%n-hide%R]%n\n- Joins a %Y<%nchannel%Y>%n in a new window"},
	{ "JOIN",	"JOIN",		e_channel,		0,	NULL },
	{ "K",		NULL,		dokick,			0,	"%Y<%Cnick%Y>%n %R[%nreason%R]%n\n- Kicks %Y<%Cnick%Y>%n for %R[%nreason%R]%n on current channel" },
	{ "KB",		NULL,		kickban,		0,	"%Y<%Cnick%Y>%n %R[%nreason%R]%n\n- Deops, kicks and bans %Y<%Cnick%Y>%n for %R[%nreason%R]%n" },
	{ "KICK",	"KICK",		send_kick,		0,	"%Y<%Bchannel%G|%Y*>%n %Y<%Cnick%Y>%n %R[%nreason%R]%n" },
	{ "KICKIDLE",	NULL,		kickidle,		0,	"%Y<%Bchannel%Y>%n\n- Kicks all idle people on %Y<%Bchannel%Y>%n" },
	{ "KILL",	"KILL",		send_kill,		0,	"%Y<%Cnick%Y>%n %R[%nreason%R]%n\n%Y*%n Requires irc operator status\n- Kills %Y<%Cnick%Y>%n for %R[%nreason%R]%n" },
	{ "L",		"PART",		do_getout,		0,	"- See %YLEAVE" },
	{ "LAMENICKLIST",NULL,		add_bad_nick,		0,	"- Lists nicks in your lame nick list" },
	{ "LASTLOG",	NULL,		lastlog,		0,	"-file filename%G|%n-clear%G|%n-max #%G|%n-liternal pat%G|%n-beep%G|%nlevel" },
	{ "LEAVE",	"PART",		send_2comm,		0,	"%Y<%Bchannel%Y>%n\n- Leaves current channel or %Y<%Bchannel%Y>%n" },
	{ "LINKS",	"LINKS",	send_comm,		0,	"- Shows servers and links to other servers" },
	{ "LIST",	"LIST",		funny_stuff,		0,	"- Lists all channels" },
#ifdef WANT_DLL
	{ "LISTDLL",	"LISTDLL",	dll_load,		0,	"- List currently loaded DLLs" },
#endif
	{ "LK",		"LameKick",	LameKick,		0,	"%R[%nreason%R]%n\n- Kicks all non +o people on current channel with %R[%nreason%R]%n" },
	{ "LKW",	"Lkw",		lkw,			0,	"%R[%Bchannel%R]%n\n- Leave the current channel, killing the window in current channel or%R[%Bchannel%R]%n as well" },
	{ "LLOOK",	NULL,		linklook,		0,	"%Y*%n Requires set %YLLOOK%n %RON%n\n- Lists all the servers which are current split from the IRC network" },
	{ "LOAD",	"LOAD",		load,			0,	"filename\n- Loads filename as a irc script" },
#ifdef WANT_DLL
	{ "LOADDLL",	NULL,		dll_load,		0,	"filename.so\n- Loads filename as a dll"},
#endif
#ifdef WANT_TCL
	{ "LOADTCL",	NULL,		tcl_load,		0,	"filename.tcl\n- Loads a tcl file" },
#endif
	{ "LOCAL",	"2",		localcmd,		0,	scripting_command },
	{ "LS",		"ls",		exec_cmd,		0,	"- Displays list of file" },
	{ "LUSERS",	"LUSERS",	send_comm,		0,	"- Shows stats on current server" },
	{ "M",		"PRIVMSG",	e_privmsg,		0,	"See %YMSG%n" },
	{ "MAP",	NULL,		do_map,			0,	"- Displays a map of the current servers"},
	{ "MB",		NULL,		massban,		0,	"- Mass bans everybody on current channel" },
	{ "MD",		NULL,		massdeop,		0,	"- Mass deops current channel" },
	{ "MDOP",	NULL,		massdeop,		0,	"- Mass deops current channel" },
	{ "MDVOICE",	"MDVoice",	massdeop,		0,	"- Mass de-voices current channel" },
	{ "ME",		NULL,		me,			0,	"<action>\n- Sends an action to current channel" },
	{ "MESG",	NULL,		extern_write,		0,	"<YesB|BNoB||BOnB|BOff>\n- Turns mesg <YesB|BNoB||BOnB|BOff>" },
	{ "MK",		NULL,		masskick,		0,	"- Mass kicks current channel" },
	{ "MKB",	NULL,		masskickban,		0,	"- Mass kick bans current channel" },
	{ "MKNU",	NULL,		mknu,			0,	"- Mass kick bans non-userlist users on current channel" },
	{ "MODE",	"MODE",		send_channel_com,	0,	NULL },
	{ "MODELOCK",	"ModeLock",	mode_lock,		0,	"%Y<%Bchannel%Y>%n +%G|%n-%Y<%ninstampkl #%Y>%n\n- Locks %Y<%Bchannel%Y>%n with +%G|%n-%Y<%nmodes%Y>%n" },
	{ "MOP",	NULL,		massop,			0,	"- Mass ops everybody on current channel" },
	{ "MORE",	"More",		readlog,		0,	"%R[%nfilename%R]%n\n- displays file in pages" },
	{ "MOTD",	"MOTD",		send_comm,		0,	"%R[%nserver%R]%n\n- Shows MOTD on current server %R[%nserver%R]%n" },
	{ "MSAY",	"MSay",		do_msay,		0,	NULL },
	{ "MSG",	"PRIVMSG",	e_privmsg,		0,	"%Y<%Cnick%Y>%n %Y<%ntext%Y>%n\n- Send %Y<%Cnick%Y>%n a message with %Y<%ntext%Y>%n" },
	{ "MUB",	NULL,		unban,			0,	"- Mass unbans current channel" },
	{ "MULT",	NULL,		multkick,		0,	"%Y<%nnick(s)%Y>%n %R[%nreason%R]%n\n- Kicks all %Y<%nnick(s)%Y>%n with %R[%nreason%R]" },
	{ "MVOICE",	"MVoice",	massop,			0,	"%R[%Cnick%G|%Bchannel%G|%Cnick%Y!%Cuser%Y@%nhostname%Y]%n\n- Mass voice nicks matching %R[%Cnick%G|%Bchannel%G|%Cnick%Y!%Cuser%Y@%nhostname%Y]%n" },
	{ "N",		"NAMES",	do_mynames,		0,	NULL },
	{ "NAMES",	"NAMES",	funny_stuff,		0,	"%R[%Bchannel%R]%n\n- Shows names on current channel or %R[%Bchannel%R]%n" },
	{ "NEWNICK",	NULL,		newnick,		0,	NULL },
	{ "NEWUSER",	NULL,		newuser,		0,	NULL },
	{ "NICK",	"NICK",		e_nick,			0,	"-Changes nick to specified nickname" },
	{ "NOCHAT",	"NoChat",	chat,			0,	"%Y<%nnick%Y>%n\n- Kills chat reqest from %Y<%nnick%Y>" },
	{ "NOPS",	"Nops",		users,			0,	"%R[%Bchannel%R]%n\n- Shows, in a full format, all the nicks without ops in %R[%Bchannel%R]%n or channel" },
	{ "NOTE",	"NOTE",		send_comm,		0,	NULL },
	{ "NOTICE",	"NOTICE",	e_privmsg,		0,	"%Y<%Cnick%G|%Bchannel%Y> %Y<%ntext%Y>%n\n- Sends a notice to %Y<%Cnick%G|%Bchannel%Y> with %Y<%ntext%Y>%n" },
	{ "NOTIFY",	NULL,		notify,			0,	"%Y<%Cnick|+|-nick%Y>%n\n- Adds/displays/removes %Y<%Cnick%Y>%n to notify list" },
	{ "NSLOOKUP",	NULL,		nslookup,		0,	"%Y<%nhostname%Y>%n\n- Returns the IP adress and IP number for %Y<%nhostname%Y>%n" },
	{ "NWHOIS",	NULL,		nwhois,			0,	"%Y<%Cnick|channel%Y>%n\n- Shows internal statistics for %Y<%Cnick%Y>%n" },
	{ "NWHOWAS",	NULL,		whowas,			0,	"- Displays internal whowas info for all channels. This information expires after 20 minutes for users on internal list, 10 minutes for others" },
	{ "OFFERS",	"Offers",	do_offers,		0,	NULL },
	{ "ON",		NULL,		oncmd,			0,	scripting_command },
	{ "OOPS",	NULL,		do_oops,		0,	"%Y<%Cnick%Y>%n\n- Sends a oops message to last recipient of a message and sends the correct message to %Y<%Cnick%Y>%n" },
	{ "OP",		NULL,		doop,			0,	"%Y<%Cnick%Y>%n\n- Gives %Y<%Cnick%Y>%n +o" },
	{ "OPER",	"OPER",		oper,			0,	"%Y*%n Requires irc operator status\n%Y<%Cnick%Y>%n %R[%npassword%R]%n" },
	{ "ORIGNICK",	"OrigNick",	orig_nick,		0,	"%Y<%Cnick%Y>%n\n- Attempts to regain a nick that someone else has taken from you." },
	{ "OSTAT",	NULL, 		serv_stat,		0,	"-Displays some internal statistics gathered about the server" },
	{ "OV",		NULL,		ov_window,		0,	"%R[%non|off%R]%n %R[%-hide|+hide|hide%R]%n\n- turns on/off oper view window as well as creating hidden window" },
	{ "P",		"Ping",		pingcmd,		0,	"%Y<%nnick%Y>%n\n- Pings %Y<%nnick%Y>" },
	{ "PARSEKEY",	NULL,		parsekeycmd,		0,	scripting_command },
	{ "PART",	"PART",		send_2comm,		0,	"- Leaves %Y<%nchannel%Y>%n" },
	{ "PARTALL",	"PARTALL",	do_getout,		0,	"- Leaves all channels" },
	{ "PAUSE",	NULL,		e_pause,		0,	scripting_command },
	{ "PING",	NULL, 		pingcmd,		0,	"%Y<%nnick%Y>%n\n- Pings %Y<%nnick%Y>" },
	{ "POP",	NULL,		pop_cmd,		0,	scripting_command },
	{ "PRETEND",	NULL,		pretend_cmd,		0,	scripting_command },
	{ "PS",		"ps",		exec_cmd,		0,	"- Displays process table" },
	{ "PUSH",	NULL,		push_cmd,		0,	scripting_command },
	{ "Q",		NULL,		query,			0,	"%Y<%n-cmd cmdname%Y> <%Cnick%Y>%n\n- Starts a query to %Y<%Cnick%Y>%n" },
	{ "QME",	"qme",		me,			0,	"- Sends a action to a query" },
	{ "QUERY",	NULL,		query,			0,	"%Y<%n-cmd cmdname%Y> <%Cnick%Y>%n\n- Starts a query to %Y<%Cnick%Y>%n" },
	{ "QUEUE",      NULL,           queuecmd,               0,	scripting_command },
	{ "QUIT",	"QUIT",		e_quit,			0,	"- Quit BitchX" },
	{ "QUOTE",	NULL,		quote,			0,	"%Y<%ntext%Y>%n\n- Sends text directly to the server" },
	{ "RANDOMNICK",	NULL,		randomnick,		0,	"%Y<%Cnick%Y>%n\n- Changes your nick to a random nick. If nick is specified is is used as a prefix" },
	{ "RBIND",	NULL,		rbindcmd,		0,	"- Removes a key binding" },
	{ "READLOG",	"ReadLog",	readlog,		0,	"- Displays current away log" },
	{ "RECONNECT",	NULL,		reconnect_cmd,		0,	"- Reconnects you to current server" },
	{ "REDIRECT",	NULL,		redirect,		0,	"%Y<%Cnick%G|%Bchannel%Y> <command>\n- Redirects <command> to %Y<%Cnick%G|%Bchannel%Y>" },
	{ "REHASH",	"REHASH",	send_comm,		0,	"%Y*%n Requires irc operator status\n- Rehashs ircd.conf for new configuration" },
	{ "REINIT",	NULL,		init_vars,		0,	"- Reinitializes internal variables" },
	{ "REINITSTAT",	NULL,		init_window_vars,	0,	"- Reinitializes window variables" },

	{ "REL",	"Rel",		do_dirlasttype,		0,	relay_help  },

	{ "RELC",	"RelC",		do_dirlasttype,		0,	relay_help },
	{ "RELCR",	"RelCR",	do_dirlasttype,		0,	relay_help  },
	{ "RELCRT",	"RelCRT",	do_dirlasttype,		0,	relay_help  },
	{ "RELCT",	"RelCT",	do_dirlasttype,		0,	relay_help  },
	{ "RELD",	"RelD",		do_dirlasttype,		0,	relay_help  },
	{ "RELDT",	"RelDT",	do_dirlasttype,		0,	relay_help  },

	{ "RELI",	"RelI",		do_dirlasttype,		0,	relay_help  },
	{ "RELIT",	"RelIT",	do_dirlasttype,		0,	relay_help  },
	{ "RELM",	"RelM",		do_dirlasttype,		0,	relay_help  },
	{ "RELMT",	"RelMT",	do_dirlasttype,		0,	relay_help  },
	{ "RELN",	"RelN",		do_dirlasttype,		0,	relay_help  },
	{ "RELNT",	"RelNT",	do_dirlasttype,		0,	relay_help  },

	{ "RELOAD",	NULL,		reload_save,		0,	"- Reloads BitchX.sav" },

	{ "RELS",	"RelS",		do_dirlasttype,		0,	relay_help  },
	{ "RELSM",	"RelSM",	do_dirlasttype,		0,	relay_help  },
	{ "RELSMT",	"RelSMT",	do_dirlasttype,		0,	relay_help  },

	{ "RELSN",	"RelSN",	do_dirlasttype,		0,	relay_help  },
	{ "RELSNT",	"RelSNT",	do_dirlasttype,		0,	relay_help  },
	{ "RELST",	"RelST",	do_dirlasttype,		0,	relay_help  },
	{ "RELSTT",	"RelSTT",	do_dirlasttype,		0,	relay_help  },

	{ "RELSW",	"RelSW",	do_dirlasttype,		0,	relay_help  },
	{ "RELSWT",	"RelSWT",	do_dirlasttype,		0,	relay_help  },

	{ "RELT",	"RelT",		do_dirlasttype,		0,	relay_help  },
	{ "RELTT",	"RelTT",	do_dirlasttype,		0,	relay_help  },
	{ "RELW",	"RelW",		do_dirlasttype,		0,	relay_help  },
	{ "RELWT",	"RelWT",	do_dirlasttype,		0,	relay_help  },


	{ "REMLOG",	"RemLog",	remove_log,		0,	"- Removes logfile" },
	{ "REPEAT", 	NULL, 		repeatcmd,		0,	"%Y<%ncommand%Y>%n %Y<%ntimes%Y>%n\n- Repeats %Y<%ncommand%Y>%n %Y<%ntimes%Y>%n" },
	{ "REQUEST",	NULL,		ctcp,			0,	"%Y<%Cnick%G|%Bchannel%Y>%n %Y<%nrequest%Y>%n\n- Sends CTCP %Y<%nrequest%Y>%n to %Y<%Cnick%G|%Bchannel%Y>%n" },
	{ "RESET",	NULL,		reset,			0,	"- Fixes flashed terminals" },
	{ "RESTART",	"RESTART",	send_comm,		0,	"%Y*%n Requires irc operator status\n- Restarts server" },
	{ "SAVE",	"SaveAll",	savelists,		0,	"- Saves ~/.BitchX/BitchX.sav" },
	{ "SAVEIRC",	NULL,		save_settings,		0,	"- Saves ~/.bitchxrc" },
	{ "SAVELIST",	NULL, 		savelists,		0,	"- Saves ~/.BitchX/BitchX.sav" },
	{ "SAY",	empty_string,	do_send_text,		0,	"-%Y<%ntype%Y>%n [-c for cool -a for annoy -e for elite]\n- Says whatever you write in either cool, annoy or elite format" },
	{ "SC",		"NAMES",	do_mynames,		0,	NULL },
	{ "SCAN",	"scan",		do_scan,		0,	"%R[%Bchannel%R]%n\n- Scans %R[%Bchannel%R]%n or current channel for all nicks" },
	{ "SCANB",	"ScanB",	do_scan,		0,	"%R[%Bchannel%R]%n\n- Scans %R[%Bchannel%R]%n or current channel for bots on your botlist" },
	{ "SCANF",	"ScanF",	do_scan,		0,	"%R[%Bchannel%R]%n\n- Scans %R[%Bchannel%R]%n or current channel for friends" },
	{ "SCANI",	"ScanI",	do_scan,		0,	"%R[%Bchannel%R]%n\n- Scans %R[%Bchannel%R]%n or current channel for ircops" },
	{ "SCANN",	"ScanN",	do_scan,		0,	"%R[%Bchannel%R]%n\n- Scans %R[%Bchannel%R]%n or current channel for non-ops" },
	{ "SCANO",	"ScanO",	do_scan,		0,	"%R[%Bchannel%R]%n\n- Scans %R[%Bchannel%R]%n or current channel for ops" },
	{ "SCANS",	"ScanS",	do_scan,		0,	"%R[%Bchannel%R]%n\n- Scans %R[%Bchannel%R]%n or current channel for shitlisted nicks" },
	{ "SCANV",	"ScanV",	do_scan,		0,	"%R[%Bchannel%R]%n\n- Scans %R[%Bchannel%R]%n or current channel for voice" },
	{ "SEND",	NULL,		do_send_text,		0,	NULL },
	{ "SENDLINE",	empty_string,	sendlinecmd,		0,	NULL },
	{ "SERVER",	NULL,		servercmd,		0,	"%Y<%nserver%Y>%n\n- Changes to %Y<%nserver%Y>%n" },
	{ "SERVLIST",	"SERVLIST",	send_comm,		0,	"- Unknown server command" },
	{ "SET",	NULL,		setcmd,			0,	"- Set Variables" },
	{ "SETAR",	NULL,		set_autoreply,		0,	"%Y<%n-|d|pat1 pat2 ..%Y>%n\n- Adds or removes from your auto-response" },
	{ "SETENV",	NULL,		setenvcmd,		0,	NULL },
	{ "SHELL",	"Shell",	ftp,			0,	"- Opens shell in a virtual screen" },
	{ "SHIFT",	NULL,		shift_cmd,		0,	scripting_command },
	{ "SHITLIST",	NULL,		showshitlist,		0,	"- Displays internal shitlist" },
	{ "SHOOK",	NULL,		shook,			0,	NULL },
	{ "SHOWIDLE",	NULL,		showidle,		0,	"%R[%Bchannel%R]%n\n- Shows idle people on current channel or %R[%Bchannel%R]%n" },
	{ "SHOWLOCK",	"ShowLock",	mode_lock,		0,	"- Show the mode lock on the channel" },
	{ "SHOWSPLIT",	NULL,		linklook,		0,	"- Shows split servers" },
	{ "SHOWWORDKICK",NULL,		show_word_kick,		0,	"- Shows the internal banned word list" },

	{ "SLEEP",	NULL,		sleepcmd,		0,	scripting_command },
	{ "SPING",	"Sping",	sping,			0,	"%Y<%nserver%Y>%n\n- Checks how lagged it is to %Y<%nserver%Y>%n" },
	{ "SQUERY",	"SQUERY",	send_2comm,		0,	NULL },
	{ "SQUIT",	"SQUIT",	send_2comm,		0,	"%Y<%nserver1%Y>%n %R[%nserver2%R]%n\n%Y*%n Requires irc operator status\n- Disconnects %Y<%nserver1%Y>%n from current server or %R[%nserver2%R]%n" },
	{ "STACK",	NULL,		stackcmd,		0,	scripting_command },
	{ "STATS",	"STATS",	do_stats,		0,	NULL },
	{ "STUB",	"Stub",		stubcmd,		0,	scripting_command },
	{ "SV",		"Sv",		show_version,		0,	"%R[%nnick|#channel%R]%n\nShow Client Version information" },
	{ "SWATCH",	NULL,		s_watch,		0,	"- Display or Modify what messages you want to see from the server"},
	{ "SWITCH",	"SWITCH",	switchcmd,		0,	scripting_command },
	{ "T", 		"TOPIC",	e_topic,		0,	"%Y<%ntext%Y>%n\n- Sets %Y<%ntext%Y>%n as topic on current channel"},
	{ "TBAN",	NULL,		tban,			0,	"- Interactive channel ban delete" },
	{ "TCL",	NULL,		tcl_command,		0,	NULL },
	{ "TELNET",	"telnet",	ftp,			0,	"- Creates a virtual hidden window with a telnet session in it" },
	{ "TIGNORE",	NULL,		tignore,		0,	NULL },
	{ "TIME",	"TIME",		send_comm,		0,	"- Shows time and date of current server" },
	{ "TIMER",	"TIMER",	timercmd,		0,	NULL },
	{ "TLOCK",	"TLock",	topic_lock,		0,	"%Y<%Bchannel%Y>%n %R[%non%G|%noff%R]%n\n- [Un]Locks %Y<%Bchannel%Y>%n with the current topic" },
	{ "TOGGLE",	NULL,		do_toggle,		0,	"%Y<%Bchannel%Y>%n %R[%non%G|%noff%R]%n\n- [Un]Locks %Y<%Bchannel%Y>%n with the current topic" },
	{ "TOPIC",	"TOPIC",	e_topic,		0,	"- Various Client toggles which can be change" },
	{ "TRACE",	"TRACE",	do_trace,		0,	"<argument> <server>\n- Without a specified server it shows the current connections on local server\n- Arguments: -s -u -o   trace for servers, users, ircops" },
	{ "TRACEKILL",	"TraceKill",	tracekill,		0,	NULL },
	{ "TRACESERV",	"TraceServ",	traceserv,		0,	NULL },
	{ "TYPE",	NULL,		type,			0,	scripting_command },
	{ "U",		NULL,		users,			0,	"- Shows users on current channel" },
	{ "UB",		NULL,		unban,			0,	"%R[%Cnick%R]%n\n- Removes ban on %R[%Cnick%R]%n" },
	{ "UMODE",	"MODE",		do_umode,		0,	"%Y<%nmode%Y>%n\n- Sets %Y<%nmode%Y>%n on yourself" },
	{ "UNAJOIN",	"UnAjoin",	auto_join,		0,	"%Y<%Bchannel%Y>%n\n- Removes autojoin %Y<%Bchannel%Y>%n from list" },
	{ "UNBAN",	NULL,		unban,			0,	NULL },
	{ "UNBANWORD",	"UnWordKick",	add_ban_word,		0,	"- Removes a banned word" },
	{ "UNBOT",	"UnBot",	bot,			0,	"%Y<%nbot%Y>%n %Y<%Bchannel%Y>%n\n- Removes %Y<%nbot%Y>%n from your botlist" },
	{ "UNFORWARD",  "NoForward",	do_forward,		0,	"%Y<%Cnick%G|%Bchannel%Y>\n- Remove %Y<%Cnick%G|%Bchannel%Y> from forward list" },
	{ "UNIDLE",	"UnIdle",	addidle,		0,	"%Y<%Bchannel%Y>%n\n- Removes %Y<%Bchannel%Y>%n from idle list" },
	{ "UNIG",	"UnIg",		do_ig,			0,	"%Y<%Cnick%Y>%n\n- UnIgnores %Y<%Cnick%Y>%n"},
	{ "UNIGH",	"UnIgH",	do_ig,			0,	"%Y<%Cnick%Y>%n\n- Removes %Y<%Cnick%Y>%n's host from the ignore list" },
	{ "UNKEY",	NULL,		do_unkey,		0,	"- Removes channel key from current channel" },
	{ "UNLAMENICK","UnLameNick",	add_bad_nick,		0,	"%Y<%nnick(s)%Y>%n\n- Remove nicks from you lame nick list"},
	{ "UNLESS",	"UNLESS",	ifcmd,			0,	scripting_command },
	{ "UNLOAD",	NULL,		unload,			0,	"- Unloads all loaded scripts" },
#ifdef WANT_DLL
	{ "UNLOADDLL",	NULL,		unload_dll,		0,	"- Unloads loaded DLL's" },
#endif
	{ "UNSCREW",	NULL,		do_unscrew,		0,	"%Y<%Cnick%Y>%n\n- Unscrew %Y<%Cnick%Y>%n" },
	{ "UNSHIFT",	NULL,		unshift_cmd,		0,	scripting_command },
	{ "UNSHIT",	"UnShit",	add_shit,		0,	NULL },
	{ "UNTIL",	"UNTIL",	whilecmd,		0,	scripting_command },
	{ "UNTOPIC",	"UNTOPIC",	e_topic,		0,	NULL },
	{ "UNUSER",	"UnUser",	add_user,		0,	NULL },
	{ "UNVOICE",	"Unvoice",	dodeop,			0,	NULL },
	{ "UNWORDKICK",	"UnWordKick",	add_ban_word,		0,	NULL },
	{ "UPING",	"uPing",	pingcmd,		0,	NULL },
	{ "UPTIME",	NULL,		do_uptime,		0,	NULL },
#if defined(HAVE_GETRUSAGE)
	{ "USAGE",	NULL,		usage,			0,	NULL },
#endif
	{ "USER",	NULL,		users,			0,	NULL },
	{ "USERHOST",	NULL,		userhost,		0,	NULL },
	{ "USERINFO",	NULL,		set_user_info,		0,	NULL },
	{ "USERLIST",	NULL,		showuserlist,		0,	NULL },
	{ "USERS",	"USERS",	send_comm,		0,	NULL },
	{ "USERSHOW",	"UserShow",	set_user_info,		0,	NULL },
	{ "VER",	"Version",	ctcp_version,		0,	NULL },
	{ "VERSION",	"VERSION",	version1,		0,	NULL },
	{ "VOICE",	"Voice",	doop,			0,	NULL },
	{ "W",		"W",		who,			0,	NULL },
	{ "WAIT",	NULL,		waitcmd,		0,	scripting_command },
	{ "WALL",	"WALL",		ChanWallOp,		0,	NULL },
	{ "WALLCHOPS",	"WALLCHOPS",	send_2comm,		0,	NULL },
	{ "WALLMSG",	NULL,		ChanWallOp,		0,	NULL },
	{ "WALLOPS",	"WALLOPS",	e_wall,			0,	NULL },
	{ "WHICH",	"WHICH",	load,			0,	"- Shows which script would be loaded without loading it" },
	{ "WHILE",	"WHILE",	whilecmd,		0,	scripting_command },
	{ "WHO",	"Who",		who,			0,	NULL },
	{ "WHOIS",	"WHOIS",	whois,			0,	NULL },
	{ "WHOKILL",	"WhoKill",	whokill,		0,	"%Y<%Cnick%G!%nuser%Y@%nhostname%Y>%n %R[%nreason%R]%n\n%Y*%n Requires irc operator status\n- Kills multiple clients matching the filter %Y<%Cnick%G!%nuser%Y@%nhostname%Y>%n with %R[%nreason%R]%n" },
	{ "WHOLEFT",	"WhoLeft",	whowas,			0,	NULL },
	{ "WHOWAS",	"WhoWas",	whois,			0,	NULL },
	{ "WI",		"Whois",	whois,			0,	NULL },
	{ "WII",	"Whois",	my_whois,		0,	NULL },
	{ "WILM",	"WILM",		my_whois,		0,	NULL },
	{ "WILN",	"WILN",		my_whois,		0,	NULL },
	{ "WINDOW",	NULL,		windowcmd,		0,	NULL },
	{ "WORDLIST",	NULL,		show_word_kick,		0,	NULL },
	{ "WSET",	NULL,		wset_variable,		0,	NULL },
	{ "WW",		"WhoWas",	whois,			0,	NULL },
	{ "XDEBUG",	NULL,		xdebugcmd,		0,	NULL },
	{ "XECHO",	"XECHO",	my_echo,		0,	NULL },
	{ "XTRA",	"XTRA",		e_privmsg,		0,	NULL },
	{ "XTYPE",	NULL,		xtypecmd,		0,	NULL },
	{ NULL,		NULL,		comment,		0,	NULL }
};

/* number of entries in irc_command array */
#define	NUMBER_OF_COMMANDS (sizeof(irc_command) / sizeof(IrcCommand)) - 2

BUILT_IN_COMMAND(about)
{
	int i = strip_ansi_in_echo;
	strip_ansi_in_echo = 0; 

#ifdef WINNT
	put_it("[0;25;35;40m [0m");
#else
	charset_ibmpc();
	put_it("[0;25;35;40m [0m");
#endif
#ifdef ASCII_LOGO
	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("                                                                   `");
	put_it(empty_string);
	put_it("[0;30;47m                                                                            [0m");
	put_it("[0;30;47m Grtz To: Trench, HappyCrappy, Yak, Zircon, Otiluke, Masonry, BuddahX, Hob, [0m");
	put_it("[0;30;47m          Lifendel, JondalaR, JVaughn, Suicide, NovaLogic, Jordy, BigHead,  [0m");
	put_it("[0;30;47m          Ananda, Hybrid, Reefa, BlackJac, GenX, MHacker, PSiLiCON,         [0m");
	put_it("[0;30;47m          hop, Sheik, psykotyk, oweff and Bark0de!                          [0m");
	put_it("[0;30;47m                                                                            Û[0m");
	put_it(empty_string);
#else
	put_it("[35m    ÜÜÜÜÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛßÛÛ[1;45m°±²[0;35;40m  [1;45m²±[0;35;40mß[1;32mÜ[42m±²[40mÜ[0;35;40m [1;45m²[40mÞ[0m");
	put_it("[35m [1;31mÜß[0m   [1;35;45m²[0;35;40mÛÛÛÛÛÛÛÛÛÛÛÛ[1;31;41m²[40mß[0m   [1;35;45m²[0;35;40mÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ[1;31;41m²[40mß[0m   [1;35;45m°[0;35;40mÛÛÛÛÛÛÛÛßß[1;32mÜÝ[0;35;40mÞÛÛ[1;45m°±[0;35;40m  [1mß[0;32;40mÜ[1;42m°±²Û[40mß[0;35;40m [1;45m±[0;35;40mÞ[0m");
	put_it("[35mÞ[1;31;41m±[0m    [35mÜ[1;31mÜ[0;32;40m   [35mßÛ[1;31;41m²[40mß[0;32;40m  [1;35mÜ[45m²[31;41m±[0m    [35mÜÜÜÜ[1;45mß[0;35;40mÛÛ[1;31;45mÜ[40mß[0;32;40m   [35mÜ[1;31mÜ[0;32;40m  [1;35m ß[45mÜ[31;41m±[0m    [35mÜ[1;31mÜ[0;32;40m   ÜÜÜÜ[1;42m°±²[0m [35mÛÛÛÛ[1;45m°[0;35;40m [32mÜ[1;42m°±[0;32;40mßß[1mß[35mÜ[45m²[0;35;40m [1;45m°[0;35;40mÞ[0m");
	put_it("[35mÛ[1;31;41m°[0m    [1;35;45m°[31;41mÛ[0m    [1;35;45m [31mÜÜ[0;35;40mßßßÛ[1;31;41m°[0m    [1;35;45m°[31;41mÛ[40mß[0;32;40m   [1;35;45m°[31;41mÛ[0m    [1;35;45m°[31;41m²[0m    [1;35;45m²[31;41m°[0m    [1;35;45m°[31;41mÛ[0m    [35mÜ[32mßßßß[1;42m±²[40mÜ[0;35;40mß[1;45m°[0;35;40mß[32mÜßß[1;35mÜÜ[45m²[40mÝ[0;35;40m [1;45m²±[0;35;40m ÛÞ[0m");
	put_it("[35mÛ[1;31;41m [0m    [1;35;45m±[31;41m²[0m    [1;35;45m°[31;41m²[0m    [1;35;45m°[31;41m [0m    [1;35;45m±[31;41m²[0m    [1;35;45m±[31;41m²[0m    [1;35;45m±[31mÜ[0;35;40mßßßß[1;45mÜ[31;41m [0m    [1;35;45m±[31;41m²[0m    [1;35;45m°[0;35;40mÛÛÛÛÜÜÜ[32mß[1mþ[0;32;40mÜ[35mþ  [1;45m²±±[0;35;40mÝ [1;45m±°[0;35;40m Û[1;30mÞ[0m");
	put_it("[35mÛ[31;45m²[0m    [1;35;45m²[31;41m±[0m    [1;35;45m±[31;41m±[0m    [1;35;45m±[0;31;45m²[0m    [1;35;45m²[31;41m±[0m    [1;35;45m²[31;41m±[0m    [1;35;45m²[31;41m±[0m    [1;35;45m±[0;31;45m²[0m    [1;35;45m²[31;41m±[0m    [1;35;45m±[0;35;40mÛÛÛÛÛß[32mÜß[35mÜÜ[1;32mßÜÜ[0;32;40mÜ[35mßßÝ [1;45m°[0;35;40mÛ ÛÞ[0m");
	put_it("[35mÛ[31;45m±[0m    [1;35;45mÛ[31;41m°[0m    [1;35;45m²[31;41m°[0m    [1;35;45m²[0;31;45m±[0m    [1;35;45mÛ[31;41m°[0m    [1;35;45mÛ[31;41m°[0m    [1;35;45mÛ[31;41m°[0m    [1;35;45m²[0;31;45m±[0m    [1;35;45mÛ[31;41m°[0m    [1;35;45m²[0;35;40mÛÛÛß[32mÜ[1;42m°[0;32;40mÝ[35mÞÛ[1;30;45m°[0;35;40mÝ[1;32mÞÛ[42m²±°[0;32;40mÜÜÜ[35mß ÛÞ[0m");
	put_it("[35mÛ[31;45m°[0m    [1;35mß[0;31;40mß[1;34m   [35mÜ[45mÛ[31;41m [0m   [1;35mÜ[45mÛ[0;31;45mß[40mÜ[1;34m   [35mß[0;31;40mß[1;34m   [35mÜ[45mß[0;31;45mß[40mÜ[1;34m   [35mß[0;31;40mß[1;34m   [35mÜ[45mß[0;31;45m°[0m   [1;35mÜ[45mÛ[31;41m [0m   [1;35mÜ[45mÛ[0;35;40mÛß[1;32mÜ[42m±°[0;32;40mÛ[0m [35mÛÛÛÛ[34m [1;32;42m²±±[0;32;40mÛ[1;42m°[0;32;40mÛÛÛÜÜ[1;30mÞ[0m");
	put_it("[35mÞÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛß[1;32mÜ[42m²±°[0;32;40mÛÝ[35mÞÛ[1;30;45m°[0;35;40mÛ[1;30;45m°[0;35;40m [34m [1;32;42m±°°[0;32;40mÛÛÛßß[1;30mÜ[0;35;40mÞ");
	put_it(" ßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÝ[1;32mÞ[42m²±°[0;32;40mÛÛ[0m [35mÛÛÛ[1;30;45m°±[0;35;40m  [34m [1;32;42m°[0;32;40mßß[35m [1;30mÜ[45m²[0;35;40m [1;30;45m²[40mÞ[0;35;40m");
	put_it("    ßßßßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ[1;32mß[0;32;40mßßß[35mÜ[30;45mrE[35;40mÛ[1;30;45m°±²[0;35;40m  [1;30;45m²[40mÜ[45m²[40mÝ[0;35;40m [1;30;45m²±[0;35;40m [1;30;45m±[40mÞ[0m");
	put_it(empty_string);
	put_it("[1mÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ²[0mÜ");
	put_it("[1;47mÛ[0;30;47m                                                                             [0m");
	put_it("[1;47m²[0;30;47m Grtz To: Trench, HappyCrappy, Yak, Zircon, Otiluke, Masonry, BuddahX, Hob, [1m°[0m");
	put_it("[1;47m±[0;30;47m          Lifendel, JondalaR, JVaughn, suicide, NovaLogic, Jordy, BigHead,  [1m±[0m");
	put_it("[1;47m°[0;30;47m          Ananda, Hybrid, Reefa, BlackJac, GenX, MHacker, PSiLiCON,         [1m²[0m");
	put_it("[1;47m°[0;30;47m          hop, Sheik, psykotyk, oweff and bark0de!                          [1m²[0m");
	put_it("[1;47m [0;30;47m                                                                            [1mÛ[0m");
	put_it("ß[1;30m²ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß[0m");
	put_it(empty_string);
#endif
#if !defined(WINNT) && defined(LATIN1)
	charset_lat1();
#endif
	strip_ansi_in_echo = i; 
}

BUILT_IN_COMMAND(dcc_stat_comm)
{
	dcc_stats(NULL, NULL);
}

void handle_dcc_chat(WhoisStuff *stuff, char *nick, char *args)
{
	if (!stuff || !stuff->nick || !nick || !strcmp(stuff->user, "<UNKNOWN>") || !strcmp(stuff->host, "<UNKNOWN>"))
	{
		bitchsay("No such nick %s", nick);
		return;
	}
	dcc_chat(NULL, nick);
}

BUILT_IN_COMMAND(init_dcc_chat)
{
char *nick = next_arg(args, &args);
	if (nick)
	{
		do {
			add_to_userhost_queue(nick, handle_dcc_chat, nick, NULL);
		} while ((nick = next_arg(args, &args)));
	}
	else
		userage("dc", helparg);
}

BUILT_IN_COMMAND(init_ftp)
{
	if (args && *args)
		dcc_ftpopen(NULL, args);
	else
		userage("ftp", helparg);
}

/*
 * find_command: looks for the given name in the command list, returning a
 * pointer to the first match and the number of matches in cnt.  If no
 * matches are found, null is returned (as well as cnt being 0). The command
 * list is sorted, so we do a binary search.  The returned commands always
 * points to the first match in the list.  If the match is exact, it is
 * returned and cnt is set to the number of matches * -1.  Thus is 4 commands
 * matched, but the first was as exact match, cnt is -4.
 */
IrcCommand *find_command(char *com, int *cnt)
{
	IrcCommand *retval;
	int loc;

	/*
	 * As a special case, the empty or NULL command is send_text.
	 */
	if (!com || !*com)
	{
		*cnt = -1;
		return irc_command;
	}

	retval = (IrcCommand *)find_fixed_array_item ((void *)irc_command, sizeof(IrcCommand), NUMBER_OF_COMMANDS + 1, com, cnt, &loc);
	return retval;
}

#ifdef WANT_DLL
IrcCommandDll * find_dll_command(char *com, int *cnt)
{
/*
	int loc;
	IrcCommandDll *retval = NULL;
	context;
	if (!com || !*com || !dll_commands)
	{
		*cnt = 0;
		return NULL;
	}
	retval = (IrcCommandDll *)find_array_item ((array *)dll_commands, com, cnt, &loc);
	return retval;
*/

	int len = 0;
	
	if (com && (len = strlen(com)) && dll_commands)
	{
		int	min,
			max;
		IrcCommandDll *old, *old_next = NULL;
		
		*cnt = 0;
		min = 1;
		max = 0;
		for (old = dll_commands; old; old = old->next)
			max++;
		
		old = dll_commands;
		while (1)
		{
			if (!my_strnicmp(com, old->name, len))
			{
				if (!old_next)
					old_next = old;
				(*cnt)++;
			}
			if (old->next == NULL)
			{
				if (old_next && strlen(old_next->name) == len)
					*cnt *= -1;
				return (old_next);
			}
			else
				old = old->next;
		}
	}
	else
	{
		*cnt = -1;
		return (NULL);
	}
}
#endif

/* IRCUSER command. Changes your userhost on the fly.  Takes effect
 * the next time you connect to a server 
 */
BUILT_IN_COMMAND(set_username)
{
        char *blah = next_arg(args, &args);
	context;
	if (blah && *blah)
	{
		if (!strcmp(blah, "-"))
			strmcpy(username, empty_string, NAME_LEN);
		else 
			strmcpy(username, blah, NAME_LEN);
		say("Username has been changed to '%s'",username);
	}
	else
		userage("IrcUser", helparg);
}

/* This code is courtesy of Richie B. (richie@morra.et.tudelft.nl) */
/*
 * REALNAME command. Changes the current realname. This will only be parsed
 * to the server when the client is connected again.
 */
BUILT_IN_COMMAND(realname_cmd)
{
	context;
        if (*args)
	{
                strmcpy(realname, args, REALNAME_LEN);
		say("Realname at next server connnection: %s", realname);
	}
	else
		userage("RealName", helparg);
}

BUILT_IN_COMMAND(pop_cmd)
{
	extern char *function_pop(char *);
        char *blah = function_pop(args);
        new_free(&blah);
}

BUILT_IN_COMMAND(push_cmd)
{
	extern char *function_push(char *);
        char *blah = function_push(args);
        new_free(&blah);
}

BUILT_IN_COMMAND(shift_cmd)
{
	extern char *function_shift(char *);
        char *blah = function_shift(args);
	context;
        new_free(&blah);
}

BUILT_IN_COMMAND(unshift_cmd)
{
	extern char *function_unshift(char *);
        char *blah = function_unshift(args);
	context;
        new_free(&blah);
}

BUILT_IN_COMMAND(do_forward)
{
	context;
	if (command && (!my_stricmp(command, "NOFORWARD") || !my_stricmp(command, "UNFORWARD")))
	{
		if (forwardnick)
		{
			bitchsay("No longer forwarding messages to %s", forwardnick);
		        send_to_server("NOTICE %s :%s is no longer forwarding to you",
				forwardnick, get_server_nickname(from_server));
		} 
		else
			userage(command, helparg);
		new_free(&forwardnick);
		return;
	}
	if (args && *args)
	{
		char *q;
		malloc_strcpy(&forwardnick, args);
		if ((q = strchr(forwardnick, ' ')))
			*q = 0;
		send_to_server("NOTICE %s :%s is now forwarding messages to you",
			forwardnick, get_server_nickname(from_server));
		bitchsay("Now forwarding messages to %s", forwardnick);
	}
	else
		userage(command, helparg);
	return;
}

BUILT_IN_COMMAND(blesscmd)
{
	bless_local_stack();
}

BUILT_IN_COMMAND(auto_join)
{
AJoinList *new = NULL;
	context;
	if (command && *command && !my_stricmp(command, "AJoinList"))
	{
		int count = 0;
		for (new = ajoin_list; new; new = new->next)
		{
			if (!count)
				put_it("AJoin List");
			put_it("%10s %s", new->name, new->key?new->key:empty_string);
			count++;
		}
		if (count)
			put_it("End of AJoin List");
		return;
	}
	if (!args || !*args)
	{
		userage(command, helparg);
		return;
	}
	if (command && *command && !my_stricmp(command, "UNAJOIN"))
	{
		char *channel;
		channel = make_channel(args);
		if ((new = (AJoinList *)remove_from_list((List **)&ajoin_list, channel)))
		{
			if (new->ajoin_list)
			{
				bitchsay("Removing Auto-Join channel %s", new->name);
				new_free(&new->name);
				new_free(&new->key);
				new_free((char **)&new);
			} else
				add_to_list((List **)&ajoin_list, (List *)new);
		}
		return;
	} 
	if ((new = (AJoinList *)find_in_list((List **) &ajoin_list, args, 1)) == NULL)
	{
		char *channel;
		char *key;
		channel = next_arg(args, &args);
		key = next_arg(args, &args);

		new = (AJoinList *) new_malloc(sizeof(AJoinList));
		malloc_sprintf(&new->name, "%s", make_channel(channel));
		if (key && *key)
			new->key = m_strdup(key);
		new->ajoin_list = 1;
		new->server = -1;
		add_to_list((List **) &ajoin_list, (List *)new);
	}
	if (from_server > -1 && server_list[from_server].chan_list)
	{
		bitchsay("Auto-Joining %s%s%s", new->name, new->key?space:empty_string, new->key?new->key:empty_string);
		send_to_server("JOIN %s %s", new->name, new->key? new->key:empty_string);
	}
}

BUILT_IN_COMMAND(do_oops)
{
	char 	*newmsg;

	context;
	if (args && *args && last_sent_msg_body)
	{
		newmsg = next_arg(args, &args);
		send_to_server("PRIVMSG %s :Oops, that /msg wasn't for you", sent_nick);
		send_to_server("PRIVMSG %s :%s", newmsg, last_sent_msg_body);
		if (window_display && do_hook(SEND_MSG_LIST, "%s %s", newmsg, last_sent_msg_body))
			put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_MSG_FSET),"%s %s %s %s", update_clock(GET_TIME), newmsg, get_server_nickname(from_server), last_sent_msg_body));
	}
	else
		userage(command, helparg);
	return;
}

/*
 * RECONNECT command. Closes the server, and then reconnects again.
 * Works also while connected to multiple servers. It only reconnects to the
 * current server number (which is stored in from_server). 
 * This command puts the REALNAME command in effect.
 */
BUILT_IN_COMMAND(reconnect_cmd)
{
	char scommnd[6];
	context;
	if (from_server == -1)
	{
		bitchsay("Try connecting to a server first.");
		return;
	}
	if (do_hook(DISCONNECT_LIST, "Reconnecting to server"))
		put_it("%s", convert_output_format("$G Reconnecting to server %K[%W$1%K]", "%s %d", update_clock(GET_TIME), from_server));
	snprintf(scommnd, 5, "+%i", from_server);
	close_server(from_server,(args && *args) ? args : "Reconnecting");
	clean_whois_queue();
	window_check_servers();
	servercmd(NULL, scommnd, empty_string, NULL);
}

/* End of contributed code */

/* clear: the CLEAR command.  Figure it out */
BUILT_IN_COMMAND(my_clear)
{
	char	*arg;
	int	all = 0,
		unhold = 0;

	context;
	while ((arg = next_arg(args, &args)) != NULL)
	{
	/* -ALL and ALL here becuase the help files used to be wrong */
		if (!my_strnicmp(arg, "A", 1) || !my_strnicmp(arg+1, "A", 1))
			all = 1;
	/* UNHOLD */
		else if (!my_strnicmp(arg+1, "U", 1))
			unhold = 1;
		else
			userage("clear", helparg);
	}
	if (all)
		clear_all_windows(unhold);
	else
	{
		if (unhold)
			hold_mode(NULL, OFF, 1);
		clear_window_by_refnum(0);
	}
	update_input(UPDATE_JUST_CURSOR);
}


BUILT_IN_COMMAND(do_invite)
{
	char *inick;
	ChannelList *chan = NULL;
	int server = from_server;
	
	context;
	if (args && *args)
	{
		while(1)
		{
			inick = next_arg(args, &args);
			if (!inick)
				return;
			if (args && *args)
			{
				if (!is_channel(args) || !(chan = prepare_command(&server, args, NO_OP)))
					return;
			}
			else
				if (!(chan = prepare_command(&server, NULL, NO_OP)))
					return;
			
			if (!chan)
				return;
			my_send_to_server(server, "INVITE %s %s%s%s", inick, chan->channel, chan->key?" ":"", chan->key?chan->key:"");
		}
	}
	else
		userage("invite", helparg);
	return;
}

/*
   This isnt a command, its used by the wait command.  Since its extern,
   and it doesnt use anything static in this file, im sure it doesnt
   belong here.
 */
void oh_my_wait _((void))
{
	int w_server;

	if ((w_server = from_server) == -1)
		w_server = primary_server;

	if (is_server_connected(w_server))
	{
		int old_in_on_who = in_on_who;
		int old_doing_privmsg = doing_privmsg;
		int old_doing_notice = doing_notice;
		int old_in_ctcp_flag = in_ctcp_flag;
		int old_from_server = from_server;
				
		waiting_out++;
		lock_stack_frame();
		send_to_server("%s", lame_wait_nick);
		while (waiting_in < waiting_out)
			io("oh_my_wait");

		in_on_who = old_in_on_who;
		doing_privmsg = old_doing_privmsg;
		doing_notice = old_doing_notice;
		in_ctcp_flag = old_in_ctcp_flag;
		from_server = old_from_server;
	}
}

BUILT_IN_COMMAND(do_umode)
{
	context;
	send_to_server("%s %s %s", command, get_server_nickname(from_server), 
		(args && *args) ? args : empty_string);
}


BUILT_IN_COMMAND(do_getout)
{
char    *channel = NULL;
ChannelList *chan;
int server = from_server;
int all = 0;

	context;
	if (command && !my_stricmp(command, "PARTALL"))
		all = 1;
	if (!all)
	{
	        if (args)
        	        channel = next_arg(args, &args);
		if (!(chan = prepare_command(&server, channel?make_channel(channel):channel, NO_OP)))
			return;
		my_send_to_server(server, "PART %s", chan->channel);
	}
	else
	{
		for (chan = server_list[server].chan_list; chan; chan = chan->next)
			my_send_to_server(server, "PART %s", chan->channel);
	}
}

BUILT_IN_COMMAND(do_unscrew)
{
        char    *channel = NULL;
	int server = from_server;
	ChannelList *chan;

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

	if (!(chan = prepare_command(&server, channel, NEED_OP)))
		return;

	my_send_to_server(server, "MODE %s -k %s", chan->channel, chan->key);
	my_send_to_server(server, "MODE %s +k \033(B\033[2J", chan->channel);
	my_send_to_server(server, "MODE %s -k \033(B\033[2J", chan->channel);
}

BUILT_IN_COMMAND(do_4op)
{
        char    *channel = NULL;
	char	*nick = NULL;
ChannelList *chan;
int	server = from_server;


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

	if (channel)
	{
		if (is_channel(channel))
			nick = args;
		else
		{
			nick = channel;
			channel = NULL;
		}
	}
	if (!(chan = prepare_command(&server, channel, NEED_OP)))
		return;

	if (!nick)
	{
		userage(command, helparg);
		return;
	}
	my_send_to_server(server, "MODE %s +oooo %s %s %s %s", chan->channel, nick, nick, nick, nick);
}

BUILT_IN_COMMAND(do_scan)
{
int voice = 0, ops = 0, nops = 0, ircops = 0, all = 0, friends = 0, bots = 0, shit = 0;
char *channel = NULL;
ChannelList *chan;
NickList *nick, *snick = NULL;
char *s;
char *buffer = NULL;
int count = 0;
int server;
	context;
	if (command && !my_stricmp(command, "scanv"))
		voice = 1;
	else if (command && !my_stricmp(command, "scano"))
		ops = 1;
	else if (command && !my_stricmp(command, "scann"))
		nops = 1;
	else if (command && !my_stricmp(command, "scanf"))
		friends = 1;
	else if (command && !my_stricmp(command, "scanb"))
		bots = 1;
	else if (command && !my_stricmp(command, "scanb"))
		shit = 1;
	else if (command && !my_stricmp(command, "scani"))
		ircops = 1;
	else 
		all = 1;

	while (args && *args)
	{
		s = next_arg(args, &args);
		if (is_channel(s))
			channel = s;
		else if (s && all)
		{
			all = 0;
			if (*s == 'v')
				voice = 1;
			else if (*s == 'o')
				ops = 1;
			else if (*s == 'n')
				nops = 1;
			else if (*s == 'i')
				ircops = 1;
			else if (*s == 'b')
				bots = 1;
			else if (*s == 'f')
				friends = 1;
			else if (*s == 's')
				shit = 1;
		}
	}
	if (!(chan = prepare_command(&server, channel, NO_OP)))
		return;

	for (nick = next_nicklist(chan, NULL); nick; nick = next_nicklist(chan, nick))
	{
		if (voice && nick->voice)
			count++;
		else if (ops && nick->chanop)
			count++;
		else if (nops && !nick->chanop)
			count++;
		else if (ircops && nick->ircop)
			count++;
		else if (friends && nick->userlist)
			count++;
		else if (bots && nick->botlist)
			count++;
		else if (all)
			count++;
	}

	snick = sorted_nicklist(chan);
	if (voice) 
		s = fget_string_var(FORMAT_NAMES_VOICE_FSET);
	else if (ops)
		s = fget_string_var(FORMAT_NAMES_OP_FSET);
	else if (ircops)
		s = fget_string_var(FORMAT_NAMES_IRCOP_FSET);
	else if (friends)
		s = fget_string_var(FORMAT_NAMES_FRIEND_FSET);
	else if (bots)
		s = fget_string_var(FORMAT_NAMES_BOT_FSET);
	else if (nops)
		s = fget_string_var(FORMAT_NAMES_NONOP_FSET);
	else if (shit)
		s = fget_string_var(FORMAT_NAMES_SHIT_FSET);
	else
		s = fget_string_var(FORMAT_NAMES_FSET);

	put_it("%s", convert_output_format(s, "%s %s %d %s", update_clock(GET_TIME), chan->channel, count, space));
	if (count)
	{
		count = 0;
		for (nick = snick; nick; nick = nick->next)
		{
			if (all && (nick->chanop || nick->voice))
				malloc_strcat(&buffer, convert_output_format(fget_string_var(nick->chanop?FORMAT_NAMES_OPCOLOR_FSET:FORMAT_NAMES_VOICECOLOR_FSET),"%c %s",nick->chanop?'@':'+', nick->nick));
			else if (all)
				malloc_strcat(&buffer, convert_output_format(fget_string_var(FORMAT_NAMES_NICKCOLOR_FSET),"%c %s",'$', nick->nick));
			else if (voice && nick->voice)
				malloc_strcat(&buffer, convert_output_format(fget_string_var(FORMAT_NAMES_VOICECOLOR_FSET),"%c %s",'+', nick->nick));
			else if (ops && nick->chanop)
				malloc_strcat(&buffer, convert_output_format(fget_string_var(FORMAT_NAMES_OPCOLOR_FSET),"%c %s",'@', nick->nick));
			else if (friends && nick->userlist)
				malloc_strcat(&buffer, convert_output_format(nick->chanop?fget_string_var(FORMAT_NAMES_FRIENDCOLOR_FSET):fget_string_var(FORMAT_NAMES_NICKCOLOR_FSET),"%c %s",nick->chanop?'@':'ÿ', nick->nick));
			else if (bots && nick->botlist)
				malloc_strcat(&buffer, convert_output_format(nick->chanop?fget_string_var(FORMAT_NAMES_BOTCOLOR_FSET):fget_string_var(FORMAT_NAMES_NICKCOLOR_FSET),"%c %s",nick->chanop?'@':'ÿ', nick->nick));
			else if (nops && !nick->chanop)
				malloc_strcat(&buffer, convert_output_format(fget_string_var(FORMAT_NAMES_NICKCOLOR_FSET),"%c %s",'$', nick->nick));
			else if (ircops && nick->ircop)
				malloc_strcat(&buffer, convert_output_format(fget_string_var(FORMAT_NAMES_OPCOLOR_FSET),"%c %s",'*', nick->nick));
			else if (shit && nick->shitlist)
				malloc_strcat(&buffer, convert_output_format(fget_string_var(FORMAT_NAMES_SHITCOLOR_FSET),"%c %s",'*', nick->nick));
			else 
				continue;
			malloc_strcat(&buffer, space);
			if (count++ == 4)
			{
				if (fget_string_var(FORMAT_NAMES_BANNER_FSET))
					put_it("%s%s", convert_output_format(fget_string_var(FORMAT_NAMES_BANNER_FSET), NULL, NULL), buffer);
				else
					put_it(buffer);
				new_free(&buffer);
				count = 0;
			}
		}
		if (count && buffer)
			if (fget_string_var(FORMAT_NAMES_BANNER_FSET))
				put_it("%s%s", convert_output_format(fget_string_var(FORMAT_NAMES_BANNER_FSET), NULL, NULL), buffer);
			else
				put_it(buffer);
		if (fget_string_var(FORMAT_NAMES_FOOTER_FSET))
			put_it("%s", convert_output_format(fget_string_var(FORMAT_NAMES_FOOTER_FSET), NULL, NULL));
		new_free(&buffer);
	}
	clear_sorted_nicklist(&snick);
}

BUILT_IN_COMMAND(do_mynames)
{
        char    *channel = NULL;
	int	server = from_server;
	ChannelList *chan;
	
	context;
        if (args)
                channel = next_arg(args, &args);

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

	my_send_to_server(server, "NAMES %s", chan->channel);
}

BUILT_IN_COMMAND(my_whois)
{
        char    *channel = NULL;

	context;
	if (command && !strcmp(command, "WILM"))
	{
		if (recv_nick)
			send_to_server("WHOIS %s", recv_nick);
		else
			bitchsay("Nobody has messaged you yet");
		return;
	}
	if (command && !strcmp(command, "WILN"))
	{
		if (last_notice[0].to)
			send_to_server("WHOIS %s", last_notice[0].to);
		else
			bitchsay("Nobody has noticed you yet");
		return;
	}
        if (args && *args)
	{
                channel = next_arg(args, &args);
        	send_to_server("WHOIS %s %s", channel, channel);
	}
	else
		send_to_server("WHOIS %s %s", get_server_nickname(from_server),
			get_server_nickname(from_server));
}

BUILT_IN_COMMAND(do_unkey)
{
	char	*channel = NULL;
	int	server = from_server;
	ChannelList *chan;
	
	context;
	if (args)
		channel = next_arg(args, &args);
	if (!(chan = prepare_command(&server, channel, NEED_OP)))
		return;
	if (chan->key)
		my_send_to_server(server, "MODE %s -k %s", chan->channel, chan->key);
}

BUILT_IN_COMMAND(pingcmd)
{
	struct  timeval         tp;
	char	*to;
	char	buffer[101];
	int	ping_type = get_int_var(PING_TYPE_VAR);

	context;
	if (command && !my_stricmp(command, "uPING"))
		ping_type = 1;
	get_time(&tp);

	if ((to = next_arg(args, &args)) == NULL || !strcmp(to, "*"))
	{
		if ((to = get_current_channel_by_refnum(0)) == NULL)
			to = zero;
	}

	switch(ping_type)
	{
		case 1:
			snprintf(buffer, 100, "%s PING %ld %ld", to, (long)tp.tv_sec,(long)tp.tv_usec);
			break;
		case 2:
			snprintf(buffer, 100, "%s ECHO %ld %ld", to, (long)tp.tv_sec,(long)tp.tv_usec);
			break;
		default:
			snprintf(buffer, 100, "%s PING %ld", to, time(NULL));
	}
	ctcp(command, buffer, empty_string, NULL);
}

BUILT_IN_COMMAND(ctcp_version)
{
char *person;
int type = 0;

	context;
	if ((person = next_arg(args, &args)) == NULL || !strcmp(person, "*"))
	{
		if ((person = get_current_channel_by_refnum(0)) == NULL)
			person = zero;
	}		
	if ((type = in_ctcp()) == -1)
		my_echo(NULL, "*** You may not use the CTCP command in an ON CTCP_REPLY!", empty_string, NULL);
	else
	{
		send_ctcp(type, person, CTCP_VERSION, NULL);
		put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_CTCP_FSET),
			"%s %s %s",update_clock(GET_TIME), person, "VERSION"));
/*		malloc_strcpy(&last_sent_ctcp, convert_output_format(fget_string_var(FORMAT_SEND_CTCP_FSET),
			"%s %s %s",update_clock(GET_TIME), person, "VERSION"));*/
		add_last_type(&last_sent_ctcp[0], 1, NULL, NULL, person, "VERSION");
	}
}

BUILT_IN_COMMAND(do_offers)
{
char *person;
int type = 0;

	context;
	if ((person = next_arg(args, &args)) == NULL || !strcmp(person, "*"))
	{
		if ((person = get_current_channel_by_refnum(0)) == NULL)
			person = zero;
	}		
	if ((type = in_ctcp()) == -1)
		my_echo(NULL, "*** You may not use the CTCP command in an ON CTCP_REPLY!", empty_string, NULL);
	else
	{
		send_ctcp(type, person, CTCP_CDCC2, "%s", "LIST");
		put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_CTCP_FSET),
			"%s %s %s",update_clock(GET_TIME), person, "XDCC LIST"));
/*		malloc_strcpy(&last_sent_ctcp, convert_output_format(fget_string_var(FORMAT_SEND_CTCP_FSET),
			"%s %s %s",update_clock(GET_TIME), person, "XDCC LIST"));*/
		add_last_type(&last_sent_ctcp[0], 1, NULL, NULL, person, "CDCC LIST");
	}
}

/*ARGSUSED*/
BUILT_IN_COMMAND(ctcp)
{
	char	*to;
	char	*stag = NULL;
	int	tag;
	int	type;

	context;
	if ((to = next_arg(args, &args)) != NULL)
	{
		if (!strcmp(to, "*"))
			if ((to = get_current_channel_by_refnum(0)) == NULL)
				to = "0";

		if ((stag = next_arg(args, &args)) != NULL)
			tag = get_ctcp_val(upper(stag));
		else
			tag = CTCP_VERSION;

		if ((type = in_ctcp()) == -1)
			say("You may not use the CTCP command from an ON CTCP_REPLY!");
		else
		{
			if (args && *args)
				send_ctcp(type, to, tag, "%s", args);
			else
				send_ctcp(type, to, tag, NULL);
		put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_CTCP_FSET),
			"%s %s %s %s",update_clock(GET_TIME), to, stag ? stag : "VERSION", args ? args: empty_string));
		add_last_type(&last_sent_ctcp[0], 1, NULL, NULL, to, stag? stag : "VERSION");
		}
	}
	else
		userage("Ctcp", helparg);
}

/*ARGSUSED*/
BUILT_IN_COMMAND(hookcmd)
{
	context;
	if (*args)
		do_hook(HOOK_LIST, "%s", args);
	else
		userage("Hook", helparg);
}

BUILT_IN_COMMAND(dcc)
{
	context;
	if (*args)
		process_dcc(args);
	else
		dcc_glist(NULL, NULL);
}

BUILT_IN_COMMAND(deop)
{
	send_to_server("MODE %s -o", get_server_nickname(from_server));
}

BUILT_IN_COMMAND(funny_stuff)
{
	char	*arg,
		*stuff;
	int	min = 0,
		max = 0,
		flags = 0,
		ircu = 0;
		
	context;
	stuff = empty_string;
	if (!args || !*args)
	{
		bitchsay("Doing this is not a good idea. Add -YES if you really mean it");
		return;
	}
	while ((arg = next_arg(args, &args)) != NULL)
	{
		if (*arg == '/' || *arg == '-')
		{
			if (my_strnicmp(arg+1, "I", 1) == 0)	/* MAX */
				ircu = 1;
			else if (my_strnicmp(arg+1, "MA", 2) == 0)	/* MAX */
			{
				if ((arg = next_arg(args, &args)) != NULL)
					max = atoi(arg);
			}
			else if (my_strnicmp(arg+1, "MI", 2) == 0) /* MIN */
			{
				if ((arg = next_arg(args, &args)) != NULL)
					min = atoi(arg);
			}
			else if (my_strnicmp(arg+1, "A", 1) == 0) /* ALL */
				flags &= ~(FUNNY_PUBLIC | FUNNY_PRIVATE);
			else if (my_strnicmp(arg+1, "PU", 2) == 0) /* PUBLIC */
			{
				flags |= FUNNY_PUBLIC;
				flags &= ~FUNNY_PRIVATE;
			}
			else if (my_strnicmp(arg+1, "PR", 2) == 0) /* PRIVATE */
			{
				flags |= FUNNY_PRIVATE;
				flags &= ~FUNNY_PUBLIC;
			}
			else if (my_strnicmp(arg+1, "T", 1) == 0)	/* TOPIC */
				flags |= FUNNY_TOPIC;
			else if (my_strnicmp(arg+1, "W", 1) == 0)	/* WIDE */
				flags |= FUNNY_WIDE;
			else if (my_strnicmp(arg+1, "U", 1) == 0)	/* USERS */
				flags |= FUNNY_USERS;
			else if (my_strnicmp(arg+1, "N", 1) == 0)	/* NAME */
				flags |= FUNNY_NAME;
			else if (!my_strnicmp(arg+1, "Y", 1))
				;
			else
				stuff = arg;
		}
		else stuff = arg;
	}
	set_funny_flags(min, max, flags);
	if (!strcmp(stuff, "*"))
		if (!(stuff = get_current_channel_by_refnum(0)))
			stuff = empty_string;
	if (strchr(stuff, '*'))
	{
		funny_match(stuff);
/*		send_to_server("%s %s", command, empty_string);*/
		if (min && ircu) 
		{
			if (max) 
				send_to_server("%s >%d,<%d", command, min - 1, max + 1);
			else
				send_to_server("%s >%d", command, min - 1);
		} 
		else if (max && ircu) 
			send_to_server("%s <%d", command, max + 1);
		else
			send_to_server("%s %s", command, empty_string);
	}
	else
	{
		funny_match(NULL);
/*		send_to_server("%s %s", command, stuff);*/
		if (min && ircu) 
		{
			if (max) 
				send_to_server("%s >%d,<%d", command, min - 1, max + 1);
			else
				send_to_server("%s >%d", command, min - 1);
		} 
		else if (max && ircu) 
			send_to_server("%s <%d", command, max + 1);
		else
			send_to_server("%s %s", command, stuff);
	}
}

BUILT_IN_COMMAND(waitcmd)
{
	char	*ctl_arg = next_arg(args, &args);

	if (ctl_arg && !my_strnicmp(ctl_arg, "-c", 2))
	{
		WaitCmd	*new;

		new = (WaitCmd *) new_malloc(sizeof(WaitCmd));
		new->stuff = m_strdup(args);
		new->next = NULL;

		if (end_wait_list)
			end_wait_list->next = new;
		end_wait_list = new;
		if (!start_wait_list)
			start_wait_list = new;
		send_to_server("%s", wait_nick);
	}

	else if (ctl_arg && !my_strnicmp(ctl_arg, "for", 3))
	{
		server_list[from_server].sent = 0;
		parse_line(NULL, args, subargs, 0, 0);
		if (server_list[from_server].sent)
			oh_my_wait();
		server_list[from_server].sent = 0; /* reset it again */
	}

	else if (ctl_arg && *ctl_arg == '%')
	{
		int	w_index = get_process_index(&ctl_arg);

		if (w_index != -1 && is_process_running(w_index))
		{
			if (args)
			{
				if (!my_strnicmp(args, "-cmd", 4))
					next_arg(args, &args);
				add_process_wait(w_index, args?args:empty_string);
			}
			else
			{
				set_input(empty_string);
				while (is_process_running(w_index))
					io("wait %proc");
			}
		}
		else
			say("Not a valid process!");
	}
	else if (ctl_arg)
		yell("Unknown argument to /WAIT");
	else
	{
		oh_my_wait();
		server_list[from_server].sent = 0;
	}
}

int check_wait_command(char *nick)
{
	if (waiting_out > waiting_in && !strcmp(nick, lame_wait_nick))
	{
		waiting_in++;
		unlock_stack_frame();
	        return 1;
	}
	if (start_wait_list && !strcmp(nick, wait_nick))
	{
		WaitCmd *old = start_wait_list;

		start_wait_list = old->next;
		if (old->stuff)
		{
			parse_line("WAIT", old->stuff, empty_string, 0, 0);
			new_free(&old->stuff);
		}
		if (end_wait_list == old)
			end_wait_list = NULL;
		new_free((char **)&old);
		return 1;
	}
	return 0;
}

BUILT_IN_COMMAND(redirect)
{
	char	*who;

	context;
	if ((who = next_arg(args, &args)) != NULL)
	{
		if (!strcmp(who, "*") && !(who = get_current_channel_by_refnum(0)))
		{
			bitchsay("Must be on a channel to redirect to '*'");
			return;
		}

		if (!my_stricmp(who, get_server_nickname(from_server)))
		{
			bitchsay("You may not redirect output to yourself");
			return;
		}


 		if ((*who == '=') && !dcc_active(who + 1))
 		{
 			bitchsay("No active DCC CHAT:chat connection for %s", who+1);
			return;
 		} 
		window_redirect(who, from_server);
		server_list[from_server].sent = 0;
		parse_line(NULL, args, NULL, 0, 0);
		if (server_list[from_server].sent)
			send_to_server("%s %d", current_screen->redirect_token, current_screen->screennum);
		else
			window_redirect(NULL, from_server);
	}
	else
		userage("redirect", helparg);
}

BUILT_IN_COMMAND(sleepcmd)
{
	char	*arg;

	context;
	if ((arg = next_arg(args, &args)) != NULL)
		sleep(atoi(arg));
	else
		userage("sleep", helparg);
}

/*
 * my_echo: simply displays the args to the screen, or, if it's XECHO,
 * processes the flags first, then displays the text on
 * the screen
 */
BUILT_IN_COMMAND(my_echo)
{
	unsigned int	display;
	int	lastlog_level = 0;
	int	from_level = 0;
	char	*flag_arg;
	int	temp;
	int	all_windows = 0;
	int	x = -1, y = -1, raw = 0;
	int	banner = 0;
	char	*stuff = NULL;
	Window *old_to_window;
	
	old_to_window = to_window;
	context;
	if (command && *command == 'X')
	{
		while (args && (*args == '-' || *args == '/'))
		{
			flag_arg = next_arg(args, &args);
			switch(toupper(flag_arg[1]))
			{
				case 'C':
				{
					to_window = curr_scr_win;
					break;
				}
				case 'L':
				{
					if (!(flag_arg = next_arg(args, &args)))
						break;
					if ((temp = parse_lastlog_level(flag_arg)) != 0)
					{
						lastlog_level = set_lastlog_msg_level(temp);
						from_level = message_from_level(temp);
					}
					break;
				}
				case 'W':
				{
					if (!(flag_arg = next_arg(args, &args)))
						break;
					if (isdigit(*flag_arg))
						to_window = get_window_by_refnum(atoi(flag_arg));
					else
						to_window = get_window_by_name(flag_arg);
					break;
				}
				case 'A':
				case '*':
					all_windows = 1;
					break;
				case 'X':
					if (!(flag_arg = next_arg(args, &args)))
						break;
					x = my_atol(flag_arg);
					break;
				case 'Y':
					if (!(flag_arg = next_arg(args, &args)))
						break;
					y = my_atol(flag_arg);
					break;
				case 'R':
					raw = 1;
					break;
				case 'B':
					banner = 1;
					break;
			}
			if (!args)
				args = empty_string;
		}
	}
	display = window_display;
	window_display = 1;
	strip_ansi_in_echo = 0;
	if (banner)
	{
		malloc_strcpy(&stuff, numeric_banner());
		if (*stuff)
		{
			m_3cat(&stuff, space, args);
			args = stuff;
		}
	}
	if (all_windows)
	{
		Window *win;
		int flag = 1;

		while ((win = traverse_all_windows(&flag)))
		{
			to_window = win;
			put_echo(args);
		}
	} 
	else if (x > -1 || y > -1 || raw)
	{
		if (x <= -1)
			x = 0;
		if (y <= -1)
		{
			if (to_window)
				y = to_window->cursor;
			else
				y = curr_scr_win->cursor;
		}
		if (!raw)
			term_move_cursor(x, y);
#ifdef WINNT
		nt_write(current_screen->hStdout, args, strlen(args));
#else
		term_puts(args, strlen(args));
#endif
	}
	else
		put_echo(args);
	strip_ansi_in_echo = 1;
	window_display = display;
	if (lastlog_level)
	{
		set_lastlog_msg_level(lastlog_level);
		message_from_level(from_level);
	}
	if (stuff) 
		new_free(&stuff);
	to_window = old_to_window;
}

/*
 */

static	void oper_password_received(char *data, char *line)
{
	send_to_server("OPER %s %s", data, line);
}

/* oper: the OPER command.  */
BUILT_IN_COMMAND(oper)
{
	char	*password;
	char	*nick;

	oper_command = 1;
	if (!(nick = next_arg(args, &args)))
		nick = get_server_nickname(from_server);
	if (!(password = next_arg(args, &args)))
	{
		add_wait_prompt("Operator Password:",
			oper_password_received, nick, WAIT_PROMPT_LINE);
		return;
	}
	send_to_server("OPER %s %s", nick, password);
}

        
/* This generates a file of your ircII setup */
void really_save(char *ircrc_file, char *line)
{
	FILE	*fp;
	int	save_do_all = 0;

	context;
	if (*line != 'y' && *line != 'Y')
		return;
	if ((fp = fopen(ircrc_file, "w")) != NULL)
	{
		if (save_which & SFLAG_BIND)
			save_bindings(fp, save_do_all);
		if (save_which & SFLAG_ON)
			save_hooks(fp, save_do_all);
		if (save_which & SFLAG_NOTIFY)
			save_notify(fp);
		if (save_which & SFLAG_SET)
			save_variables(fp, save_do_all);
		if (save_which & SFLAG_ALIAS)
			save_aliases(fp, save_do_all);
		fclose(fp);
		bitchsay("IRCII settings saved to %s", ircrc_file);
	}
	else
		bitchsay("Error opening %s: %s", ircrc_file, strerror(errno));
}

/* Full scale abort.  Does a "save" into the filename in line, and
        then does a coredump */
BUILT_IN_COMMAND(abortcmd)
{
        char    *filename = next_arg(args, &args);

	context;
        filename = filename ? filename : "irc.aborted";
	save_which = SFLAG_ALIAS | SFLAG_BIND | SFLAG_ON | SFLAG_SET |
			     SFLAG_NOTIFY | SFLAG_DIGRAPH;
        really_save(filename, "y");
        abort();
}

/* save_settings: saves the current state of IRCII to a file */
BUILT_IN_COMMAND(save_settings)
{
	char	*arg = NULL;
	int	all = 1;
	char buffer[BIG_BUFFER_SIZE + 1];

	context;
	save_which = save_do_all = 0;
	while ((arg = next_arg(args, &args)) != NULL)
	{
		if ('-' == *arg || '/' == *arg)
		{
			all = 0;
			arg++;
			if (0 == my_strnicmp("ALI", arg, 3))
				save_which |= SFLAG_ALIAS;
			else if (0 == my_strnicmp("AS", arg, 2))
				save_which |= SFLAG_ALIAS;
			else if (0 == my_strnicmp("B", arg, 1))
				save_which |= SFLAG_BIND;
			else if (0 == my_strnicmp("O", arg, 1))
				save_which |= SFLAG_ON;
			else if (0 == my_strnicmp("S", arg, 1))
				save_which |= SFLAG_SET;
			else if (0 == my_strnicmp("N", arg, 1))
				save_which |= SFLAG_NOTIFY;
			else if (0 == my_strnicmp("ALL", arg, 3))
				save_do_all = 1;
			else
			{
				userage("save", helparg);
				return;
			}
			continue;
		}
		if (!(arg = expand_twiddle(bircrc_file)))
		{
			bitchsay("Unknown user");
			return;
		}
	}
	if (all)
		save_which = SFLAG_ALIAS | SFLAG_BIND | SFLAG_ON | SFLAG_SET |
			     SFLAG_NOTIFY;
	if (dumb)
		really_save(arg?arg:bircrc_file, "y"); /* REAL dumb!  -lynx */
	else
	{
		snprintf(buffer, BIG_BUFFER_SIZE, "Really write %s? ", arg?arg:bircrc_file);
		add_wait_prompt(buffer, really_save, arg?arg:bircrc_file, WAIT_PROMPT_LINE);
	}
}

/*
 * e_channel: does the channel command.  I just added displaying your current
 * channel if none is given 
 */
BUILT_IN_COMMAND(e_channel)
{
	char	*chan;
	int	len;
	char	*buffer=NULL;
	context;
	message_from(NULL, LOG_CURRENT);

	if ((chan = next_arg(args, &args)) != NULL)
	{
		len = strlen(chan);
		if (my_strnicmp(chan, "-i", 2) == 0)
		{
			if (invite_channel)
				send_to_server("%s %s %s", command, invite_channel, args);
			else
				bitchsay("You have not been invited to a channel!");
		}
		else
		{
			buffer = make_channel(chan);
			if (is_on_channel(buffer, from_server, get_server_nickname(from_server)))
			{
				/* XXXX -
				   right here we want to check to see if the
				   channel is bound to this window.  if it is,
				   we set it as the default channel.  If it
				   is not, we warn the user that we cant do it
				 */
				if (is_bound_anywhere(buffer) &&
				    !(is_bound_to_window(curr_scr_win, buffer)))
					bitchsay("Channel %s is bound to another window", buffer);

				else
				{
					is_current_channel(buffer, from_server, 1);
					bitchsay("You are now talking to channel %s", 
						set_current_channel_by_refnum(0, buffer));
					update_all_windows();
					update_input(UPDATE_ALL);
				}
			}
			else
			{
				send_to_server("%s %s%s%s", command, buffer, args?" ":empty_string, args?args:empty_string);
				if (!is_bound(buffer, curr_scr_win->server))
					malloc_strcpy(&curr_scr_win->waiting_channel, buffer);
			}
		}
	}
	else
		list_channels();
	message_from(NULL, LOG_CRAP);
}

/* comment: does the /COMMENT command, useful in .ircrc */
BUILT_IN_COMMAND(comment)
{
	/* nothing to do... */
}

/*
 * e_nick: does the /NICK command.  Records the users current nickname and
 * sends the command on to the server 
 */
/*ARGSUSED*/
BUILT_IN_COMMAND(e_nick)
{
	char	*nick;

	context;
	if (!(nick = next_arg(args, &args)))
	{
		bitchsay("Your nickname is %s", get_server_nickname(get_window_server(0)));
		if (get_pending_nickname(get_window_server(0)))
			say("A nickname change to %s is pending.", get_pending_nickname(get_window_server(0)));

		return;
	}
	if (!(nick = check_nickname(nick)))
	{
		bitchsay("Nickname specified is illegal.");
		return;
	}
	user_changing_nickname = 1;
	change_server_nickname(from_server, nick);
	set_string_var(AUTO_RESPONSE_VAR, nick);
}

/* version: does the /VERSION command with some IRCII version stuff */
BUILT_IN_COMMAND(version1)
{
	char	*host;

	context;
	if ((host = next_arg(args, &args)) != NULL)
		send_to_server("%s %s", command, host);
	else
	{ 
		bitchsay("Client: %s (internal version %s)", irc_version, internal_version);
		send_to_server("%s", command);
	}
}

BUILT_IN_COMMAND(e_hostname)
{
	struct hostent *hp;
	context;
	if (args && *args && *args != '#')
	{
		int reconn = 0;
		if (LocalHostName && strcmp(LocalHostName, args))
			reconn = 1;
		malloc_strcpy(&LocalHostName, args);
		if ((hp = gethostbyname(LocalHostName)))
			memcpy((void *)&LocalHostAddr, hp->h_addr, sizeof(LocalHostAddr));

		bitchsay("Local host name is now %s", LocalHostName);
		if (reconn)
			reconnect_cmd(NULL, NULL, NULL, NULL);
	} 
	else
	{
#if !defined(__linux__) && !defined(BSD)
		bitchsay("Local Host Name is [%s]", (LocalHostName)? LocalHostName: hostname);
#else
		char filename[81];
		char comm[200];
		FILE *fptr;
		char *p, *q;
		unsigned long ip;
		Virtuals *new = NULL;
		struct hostent *host;
		int i;
		char *newhost = NULL;
#if defined(BSD)
		char device[80];
#endif
		
		tmpnam(filename);
#if defined(BSD)
		if (!(p = path_search("netstat", "/sbin:/usr/sbin:/bin:/usr/bin")))
		{
			yell("No Netstat to be found");
			return;
		}
		sprintf(comm, "%s -in >%s", p, filename);
#else
		sprintf(comm, "/sbin/ifconfig -a >%s", filename);
#endif
		system(comm);

		if ((fptr = fopen(filename, "r")) == NULL)
		{
			unlink(filename);
			return;
		}
#if defined(BSD)
		fgets(comm, 200, fptr);
		fgets(comm, 200, fptr);
		p = next_arg(comm, &q);
		strncpy(device, p, 79);
		bitchsay("Looking for hostnames on device %s", device);
#endif
		while((fgets(comm, 200, fptr)))
		{
#if defined(__linux__)
			if ((p = strstr(comm, "inet addr")))
			{
				p += 10;
				q = strchr(p, ' ');
				*q = 0;
				if ((p && !*p) || (p && !strcmp(p, "127.0.0.1"))) continue;
#else /*(BSD)*/
			if (!strncmp(comm, device, strlen(device)))
			{
				p = comm;
				p += 24;
				while (*p && *p == ' ') p++;
				q = strchr(p, ' ');
				*q = 0;
				if ((p && !*p) || (p && !strcmp(p, "127.0.0.1"))) continue;
#endif
				ip = inet_addr(p);
				if ((host = gethostbyaddr((char *)&ip, sizeof(ip), AF_INET)))
				{
					new = (Virtuals *) new_malloc(sizeof(Virtuals));
					new->hostname = m_strdup(host->h_name);
					add_to_list((List **)&virtuals, (List *)new);
				}

			}
		}
		fclose(fptr);
		unlink(filename);
		for (new = virtuals, i = 1; virtuals; i++)
		{
			new = virtuals;
			virtuals = virtuals->next;
			if (i == 1)
				put_it("%s", convert_output_format("$G Current hostnames available", NULL, NULL));
			put_it("%s", convert_output_format("%K[%W$[3]0%K] %B$1", "%d %s", i, new->hostname));
			if (args && *args)
			{
				if (*args == '#') args++;
				if (args && *args && (i == my_atol(args)))
					malloc_strcpy(&newhost, new->hostname);
					
			}
			new_free(&new->hostname);
			new_free(&new);			
		}
		if (newhost)
		{
			malloc_strcpy(&LocalHostName, newhost);
			if ((hp = gethostbyname(LocalHostName)))
				memcpy((void *)&LocalHostAddr, hp->h_addr, sizeof(LocalHostAddr));

			bitchsay("Local host name is now [%s]", LocalHostName);
			new_free(&newhost);
			reconnect_cmd(NULL, NULL, NULL, NULL);
		}
#endif
	}
}

/*
 * info: does the /INFO command.  I just added some credits
 * I updated most of the text -phone, feb 1993.
 */
extern void display_bitchx _((int));

BUILT_IN_COMMAND(info)
{
	context;
	if (!args || !*args)
	{
		display_bitchx(-1);
		return;
	} 
	else if (isdigit(*args))
	{
		display_bitchx(my_atol(args));
		return;
	}
	send_to_server("%s %s", command, args);
}

void ison_now(char *notused, char *nicklist)
{
	context;
	if (do_hook(current_numeric, "%s", nicklist))
		put_it("%s Currently online: %s", numeric_banner(), nicklist);
}

BUILT_IN_COMMAND(ison)
{
	context;
	if (!args[strspn(args, space)])
		args = get_server_nickname(from_server);
	add_ison_to_whois(args, ison_now);

}

BUILT_IN_COMMAND(setenvcmd)
{
	char *env_var;

	if ((env_var = next_arg(args, &args)) != NULL)
		setenv(env_var, args, 1);
	else
		say("Usage: SETENV <var-name> <value>");
}


/*
 * userhost: Does the USERHOST command.  Need to split up the queries,
 * since the server will only reply to 5 at a time.
 */
BUILT_IN_COMMAND(userhost)
{
	int	n = 0,
		total = 0,
		userhost_cmd = 0;
	char	*nick;
	char    buffer[BIG_BUFFER_SIZE+1];

	context;
	while ((nick = next_arg(args, &args)) != NULL)
	{
		int	len;
		char	*body;
		
		++total;
		len = strlen(nick);
		if ((*nick == '-' || *nick =='/') && !my_strnicmp(nick+1, "C", 1))
		{
			if (total < 2)
			{
				userage("userhost", helparg);
				return;
			}
			userhost_cmd = 1;
			while (my_isspace(*args))
				args++;
 			body = next_expr_failok(&args, '{');
			if (body)
				args = body;
			break;
		}
		else
		{
			if (n++)
				strmcat(buffer, space, BIG_BUFFER_SIZE);
			else
				*buffer = '\0';
			strmcat(buffer, nick, BIG_BUFFER_SIZE);
		}
	}
	if (n)
	{
		char	*the_list = NULL;
		char	*s, *t;
		int	i;

		malloc_strcpy(&the_list, buffer);
		s = t = the_list;
		while (n)
		{
			for (i = 5; i && *s; s++)
				if (isspace(*s))
					i--, n--;
			if (isspace(s[-1]))
				s[-1] = '\0';
			else
				n--;
			strcpy(buffer, t);
			t = s;

			if (userhost_cmd)
				add_to_whois_queue(buffer, userhost_cmd_returned, "%s", args);
			else
				add_to_whois_queue(buffer, USERHOST_USERHOST, "%s", empty_string);
		}
		new_free(&the_list);
	}
	else if (!total)
		/* Default to yourself.  */
		add_to_whois_queue(get_server_nickname(from_server), USERHOST_USERHOST, "%s", get_server_nickname(from_server));
}

/*
 * whois: the WHOIS and WHOWAS commands.  This translates the 
 * to the whois handlers in whois.c 
 */
BUILT_IN_COMMAND(whois)
{
	char *stuff = NULL;

	context;
	if (!my_stricmp(command, "WHOWAS"))
	{
		char *word_one = next_arg (args, &args);
		if (args && *args)
			malloc_sprintf(&stuff, "%s %s", word_one, args);
		else if (word_one && *word_one)
			malloc_sprintf(&stuff, "%s %d", word_one, /*get_int_var(NUM_OF_WHOWAS_VAR)*/ 4);
		else
			malloc_sprintf(&stuff, "%s %d", get_server_nickname(from_server), /*get_int_var(NUM_OF_WHOWAS_VAR)*/ 4);

		send_to_server("WHOWAS %s", stuff);
		new_free(&stuff);
	}
	else /* whois command */
		send_to_server("WHOIS %s", args && *args ? args : get_server_nickname(from_server));
}

int doing_who = 0;
/*
 * who: the /WHO command. Parses the who switches and sets the who_mask and
 * whoo_stuff accordingly.  Who_mask and who_stuff are used in whoreply() in
 * parse.c 
 */
BUILT_IN_COMMAND(who)
{
	char	*arg,
		*channel = NULL;
	int	no_args = 1,
		len;

	if (who_on_join)
		return;
	if (doing_who)
		oh_my_wait();
	context;

	doing_who = 1;

	who_mask = 0;
	new_free(&who_name);
	new_free(&who_host);
	new_free(&who_server);
	new_free(&who_file);
	new_free(&who_nick);
	new_free(&who_real);
	while ((arg = next_arg(args, &args)) != NULL)
	{
		lower(arg);
		no_args = 0;
		if ((*arg == '-' || *arg == '/') && (!isdigit(*(arg + 1))))
		{
			arg++;
			if ((len = strlen(arg)) == 0)
			{
				userage(command, helparg);
				return;
			}
			if (!strncmp(arg, "o", 1)) /* OPS */
				who_mask |= WHO_OPS;
			else if (!strncmp(arg, "l", 1)) /* LUSERS */
				who_mask |= WHO_LUSERS;
			else if (!strncmp(arg, "c", 1)) /* CHOPS */
				who_mask |= WHO_CHOPS;
			else if (!strncmp(arg, "ho", 2)) /* HOSTS */
			{
				if ((arg = next_arg(args, &args)) != NULL)
				{
					who_mask |= WHO_HOST;
					malloc_strcpy(&who_host, arg);
					channel = who_host;
				}
				else
				{
					userage(command, helparg);
					return;
				}
			}
			else if (!strncmp(arg, "he", 2)) /* here */
				who_mask |= WHO_HERE;
			else if (!strncmp(arg, "a", 1)) /* away */
				who_mask |= WHO_AWAY;
			else if (!strncmp(arg, "s", 1)) /* servers */
			{
				if ((arg = next_arg(args, &args)) != NULL)
				{
					who_mask |= WHO_SERVER;
					malloc_strcpy(&who_server, arg);
					channel = who_server;
				}
				else
				{
					userage(command, helparg);
					return;
				}
			}
			else if (!strncmp(arg, "na", 2)) /* names */
			{
				if ((arg = next_arg(args, &args)) != NULL)
				{
					who_mask |= WHO_NAME;
					malloc_strcpy(&who_name, arg);
					channel = who_name;
				}
				else
				{
					userage(command, helparg);
					return;
				}
			}
			else if (!strncmp(arg, "r", 1)) /* real name */
			{
				if ((arg = next_arg(args, &args)) != NULL)
				{
					who_mask |= WHO_REAL;
					malloc_strcpy(&who_real, arg);
					channel = who_real;
				}
				else
				{
					userage(command, helparg);
					return;
				}
			}
			else if (!strncmp(arg, "ni", 2)) /* nick */
			{
				if ((arg = next_arg(args, &args)) != NULL)
				{
					who_mask |= WHO_NICK;
					malloc_strcpy(&who_nick, arg);
					channel = who_nick;
				}
				else
				{
					userage(command, helparg);
					return;
				}
				/* WHO -FILE by Martin 'Efchen' Friedrich */
			}
			else if (!strncmp(arg, "f", 1)) /* file */
			{
				who_mask |= WHO_FILE;
				if ((arg = next_arg(args, &args)) != NULL)
				{
					malloc_strcpy(&who_file, arg);
				}
				else
				{
					userage(command, helparg);
					return;
				}
			}
			else
			{
				userage(command, helparg);
				return;
			}
		}
		else if (!strcmp(arg, "*"))
		{
			channel = get_current_channel_by_refnum(0);
			if (!channel || *channel == '0')

			{
				not_on_a_channel(curr_scr_win);
				return;
			}
		}
		else
			channel = arg;
	}
	if (no_args)
	{
		channel = get_current_channel_by_refnum(0);
		if (!channel || !*channel)
		{
			not_on_a_channel(curr_scr_win);
			return;
		}
		send_to_server("WHO %s", channel);
		return;
	}
	if (no_args)
		bitchsay("No arguements specified");
	else
	{
		if (!channel && who_mask & WHO_OPS)
			channel = "*.*";
		send_to_server("%s %s %c", "WHO", channel ? channel :
				empty_string, (who_mask & WHO_OPS) ?
					'o' : '\0');
	}
	
}

/*
 * query: the /QUERY command.  Works much like the /MSG, I'll let you figure
 * it out.
 */
BUILT_IN_COMMAND(query)
{
	char	*nick,
		*rest;
	char	*cmd = NULL;
	
	context;
	message_from(NULL, LOG_CURRENT);

	if (args && !my_strnicmp(args, "-cmd", 4))
	{
		cmd = next_arg(args, &args);
		cmd = next_arg(args, &args);
	}
	if ((nick = next_arg(args, &rest)) != NULL)
	{
		if (*nick == '.')
		{
			if (!(nick = sent_nick))
			{
				bitchsay("You have not messaged anyone yet");
				return;
			}
		}
		else if (*nick == ',')
		{
			if (!(nick = recv_nick))
			{
				bitchsay("You have not recieved a message from \
						anyone yet");
				return;
			}
		}
		else if ((*nick == '*') && !(nick = get_current_channel_by_refnum(0)))
		{
			bitchsay("You are not on a channel");
			return;
		}

		if (*nick == '%')
		{
			if (is_process(nick) == 0)
			{
				bitchsay("Invalid processes specification");
				message_from(NULL, LOG_CRAP);
				return;
			}
		}
		bitchsay("Starting conversation with %s", nick);
		set_query_nick(nick, args, cmd);
	}
	else if (cmd)
		set_query_nick(NULL, NULL, cmd);
	else
	{
		if (query_nick())
		{
			bitchsay("Ending conversation with %s", query_nick());
			set_query_nick(NULL, NULL, NULL);
		}
		else
			userage("query", helparg);
	}
	update_input(UPDATE_ALL);
	message_from(NULL, LOG_CRAP);
}

void read_away_log(char *stuff, char *line)
{
	context;
	if (!line || !*line || (line && toupper(*line) == 'Y'))
		readlog(NULL, NULL, NULL, NULL);
	update_input(UPDATE_ALL);
}

BUILT_IN_COMMAND(back)
{
char *tmp = NULL;
int minutes = 0;
int hours = 0;
int seconds = 0;

	context;
	if (!curr_scr_win || curr_scr_win->server == -1)
		return;

	if (server_list[curr_scr_win->server].awaytime)
	{
		ChannelList *chan = NULL;
		time_t current_t = time(NULL) - server_list[curr_scr_win->server].awaytime;
		int old_server = from_server;
		from_server = curr_scr_win->server;
		
		hours = current_t / 3600;
		minutes = (current_t - (hours * 3600)) / 60;
		seconds = current_t % 60;
		

		if (fget_string_var(FORMAT_BACK_FSET) && get_int_var(SEND_AWAY_MSG_VAR))
		{
			bitchsay("You were /away for %i hours %i minutes and %i seconds. [\002BX\002-MsgLog %s]",
				hours, minutes, seconds,
				on_off(get_int_var(MSGLOG_VAR)));
			if (get_int_var(MSGLOG_VAR))
				log_toggle(0, chan);
			for (chan = server_list[curr_scr_win->server].chan_list; chan; chan = chan->next)
			{
					send_to_server("PRIVMSG %s :ACTION %s", chan->channel, 
						stripansicodes(convert_output_format(fget_string_var(FORMAT_BACK_FSET), "%s %d %d %d %d %s",
							update_clock(GET_TIME),
							hours, minutes, seconds, 
						        get_int_var(MSGCOUNT_VAR), args?args:get_server_away(curr_scr_win->server)/*server_list[curr_scr_win->server].away*/)));
			}
		}
		set_server_away(curr_scr_win->server, NULL);
		from_server = old_server;
	}
	else
		set_server_away(curr_scr_win->server, NULL);
	
	server_list[curr_scr_win->server].awaytime = (time_t)0;
	if (fget_string_var(FORMAT_BACK_FSET) && get_int_var(MSGLOG_VAR))
	{
		malloc_sprintf(&tmp, " read /away msgs (%d msg%s) log [Y/n]? ", get_int_var(MSGCOUNT_VAR), plural(get_int_var(MSGCOUNT_VAR)));
		add_wait_prompt(tmp, read_away_log, empty_string, WAIT_PROMPT_LINE); 
		new_free(&tmp);
	}
	set_int_var(MSGCOUNT_VAR, 0);
	update_all_status(curr_scr_win, NULL, 0);
}

/*
 * away: the /AWAY command.  Keeps track of the away message locally, and
 * sends the command on to the server.
 */
BUILT_IN_COMMAND(away)
{
	int	len;
	char	*arg = NULL;
	int	flag = AWAY_ONE;
	int	i;

	context;
	if (*args)
	{
		if ((*args == '-') || (*args == '/'))
		{
			arg = strchr(args, ' ');
			if (arg)
				*arg++ = '\0';
			else
				arg = empty_string;
			len = strlen(args);
			if (0 == my_strnicmp(args+1, "A", 1)) /* all */
			{
				flag = AWAY_ALL;
				args = arg;
			}
			else if (0 == my_strnicmp(args+1, "O", 1))/* one */
			{
				flag = AWAY_ONE;
				args = arg;
			}
			else
			{
				userage(command?command:"Away", helparg);
				return;
			}
		}
	}
	if (flag == AWAY_ALL)
	{
		for (i = 0; i < number_of_servers; i++)
			set_server_away(i, args);
	}
	else
	{
		if (args && *args)
			set_server_away(curr_scr_win->server, args);
		else
			back(NULL, args, NULL, NULL);
	}
	update_all_status(curr_scr_win, NULL, 0);
}

#if 0
#ifndef RAND_MAX
#define RAND_MAX 213234444
#endif
#endif

static void real_quit _((char *dummy, char *ptr))
{
	context;
	if (ptr && *ptr)
	{
		if (*ptr == 'Y' || *ptr == 'y')
		{
			irc_exit(1, dummy, "%s", convert_output_format(fget_string_var(FORMAT_SIGNOFF_FSET), "%s %s %s %s", update_clock(GET_TIME), get_server_nickname(get_window_server(0)), m_sprintf("%s@%s", username, hostname), dummy));
		}
	}
	bitchsay("Excelllaaant!!");
}

/* e_quit: The /QUIT, /EXIT, etc command */
BUILT_IN_COMMAND(e_quit)
{
	int	old_server = from_server;
	char	*Reason;
	DCC_list *Client;
	int active_dcc = 0;
	
	context;
	if (args && *args)
		Reason = args;
	else
		Reason = get_signoffreason(get_server_nickname(from_server));
	if (!Reason || !*Reason)
		Reason = (char *)irc_version;
	
	for (Client = ClientList; Client; Client = Client->next)
	{
		if (((Client->flags & DCC_TYPES) ==DCC_REGETFILE) || ((Client->flags & DCC_TYPES) ==DCC_FILEREAD) || ((Client->flags & DCC_TYPES) ==DCC_FILEOFFER) || ((Client->flags & DCC_TYPES) ==DCC_RESENDOFFER))
			active_dcc++;
		if ((Client->flags & DCC_TYPES) ==DCC_FTPGET)
			active_dcc++;
	}

	if (active_dcc)
		add_wait_prompt("Active DCC's. Really Quit [y/N] ? ", real_quit, Reason, WAIT_PROMPT_LINE);
	else
	{
		from_server = old_server;
		irc_exit(1, Reason, "%s", convert_output_format(fget_string_var(FORMAT_SIGNOFF_FSET), "%s %s %s %s", update_clock(GET_TIME), get_server_nickname(get_window_server(0)), m_sprintf("%s@%s", username, hostname), Reason));
	}
}

/* flush: flushes all pending stuff coming from the server */
BUILT_IN_COMMAND(flush)
{
	context;
	if (get_int_var(HOLD_MODE_VAR))
		flush_everything_being_held(NULL);
	bitchsay("Standby, Flushing server output...");
	flush_server();
	bitchsay("Done");
}

/* e_wall: used for WALLOPS */
BUILT_IN_COMMAND(e_wall)
{
	context;
	if ((!args || !*args))
	{
		userage(command, helparg);
		return;
	}
	message_from(NULL, LOG_WALLOP);
	if (!in_on_who)
		send_to_server("%s :%s", command, args);
	if (!get_server_operator(current_screen->current_window->server))
	{
		static int warned = 0;
		if (!warned)
		{
			say("WARNING: Using the /WALLOPS command as a non-oper");
			warned = 1;
		}
	}
	if (get_server_flag(from_server, USER_MODE_W))
		put_it("!! %s", args);
	add_last_type(&last_sent_wall[0], 1, get_server_nickname(curr_scr_win->server), NULL, "*", args);

	message_from(NULL, LOG_CRAP);
}

/*
 * e_privmsg: The MSG command, displaying a message on the screen indicating
 * the message was sent.  Also, this works for the NOTICE command. 
 */
BUILT_IN_COMMAND(e_privmsg)
{
	char	*nick;

	context;
	if ((nick = next_arg(args, &args)) != NULL)
	{
		if (strcmp(nick, ".") == 0)
		{
			if (!(nick = sent_nick))
			{
				bitchsay("You have not sent a message to anyone yet");
				return;
			}
		}
		else if (strcmp(nick, ",") == 0)
		{
			if (!(nick = recv_nick))
			{
				bitchsay("You have not received a message from anyone yet");
				return;
			}
		}
		else if (!strcmp(nick, "*") && (!(nick = get_current_channel_by_refnum(0))))
				nick = "0";
		send_text(nick, args, command, window_display, 1);
	}
	else
		userage(command, helparg);
}

BUILT_IN_COMMAND(dcc_crash)
{
char *nick;
int old_server;
	context;
	if ((nick = next_arg(args, &args)) != NULL)
	{
		if (*nick == '=') /* DCC chat */
		{
			old_server = from_server;
			from_server = -1;
			dcc_chat_crash_transmit(nick + 1, args);
			from_server = old_server;
		}
	}
}

/*
 * quote: handles the QUOTE command.  args are a direct server command which
 * is simply send directly to the server 
 */
BUILT_IN_COMMAND(quote)
{
	context;
	if (!in_on_who && !doing_privmsg && args && *args)
		send_to_server("%s", args);
}

/*
 * send_comm: the generic command function.  Uses the full command name found
 * in 'command', combines it with the 'args', and sends it to the server 
 */
BUILT_IN_COMMAND(send_comm)
{
	context;
	if (args && *args)
		send_to_server("%s %s", command, args);
	else
		send_to_server("%s", command);
}

BUILT_IN_COMMAND(do_trace)
{
char *flags = NULL, *serv = NULL;
	context;
	server_list[from_server].trace_flags = 0;
	while (args && *args)
	{
		flags = next_arg(args, &args);
		if (flags && *flags=='-')
		{
			switch(toupper(*(flags+1)))
			{
				case 'O':
					server_list[from_server].trace_flags |= TRACE_OPER;
					break;
				case 'U':
					server_list[from_server].trace_flags |= TRACE_USER;
					break;
				case 'S':
					server_list[from_server].trace_flags |= TRACE_SERVER;
				default:
					break;
			}
			continue;
		}
		if (flags && *flags != '-')
			serv = flags;
		else
			serv = next_arg(args, &args);
	}
	if (server_list[from_server].trace_flags & TRACE_OPER)
		bitchsay("Tracing server %s%sfor Operators", serv?serv:empty_string,serv?" ":empty_string);
	if (server_list[from_server].trace_flags & TRACE_USER)
		bitchsay("Tracing server %s%sfor Users", serv?serv:empty_string,serv?" ":empty_string);
	if (server_list[from_server].trace_flags & TRACE_SERVER)
		bitchsay("Tracing server %s%sfor servers", serv?serv:empty_string,serv?" ":empty_string);
	send_to_server("%s%s%s", command, serv?" ":empty_string, serv?serv:empty_string);
}

BUILT_IN_COMMAND(do_stats)
{
char *flags = NULL, *serv = NULL;
char *new_flag = empty_string;
char *str = NULL;
	context;
	server_list[from_server].stats_flags = 0;
	flags = next_arg(args, &args);
	if (flags)
	{
		switch(*flags)
		{
			case 'o':
				server_list[from_server].stats_flags |= STATS_OLINE;
				str = "%GType   Host                      Nick";
				break;
			case 'y':
				server_list[from_server].stats_flags |= STATS_YLINE;
				str = "%GType   Class  PingF  ConnF  MaxLi      SendQ";
				break;
			case 'i':
				server_list[from_server].stats_flags |= STATS_ILINE;
				str = "%GType   Host                      Host                       Port Class";
				break;
			case 'h':
				server_list[from_server].stats_flags |= STATS_HLINE;
				str = "%GType    Server Mask     Server Name";
				break;
			case 'l':
				server_list[from_server].stats_flags |= STATS_LINK;
				str = "%Glinknick|server               sendq smsgs sbytes rmsgs rbytes timeopen";
				break;
			case 'c':
				server_list[from_server].stats_flags |= STATS_CLASS;
				str = "%GType   Host                      Host                       Port Class";
				break;
			case 'u':
				server_list[from_server].stats_flags |= STATS_UPTIME;
				break;
			case 'k':
				server_list[from_server].stats_flags |= STATS_KLINE;
				str = "%GType   Host                      UserName              Reason";
				break;
			case 'm':
				server_list[from_server].stats_flags |= STATS_MLINE;
			default:
				break;
		}
		new_flag = flags;
	}
	if (args && *args)
		serv = args;
	else
		serv = get_server_itsname(from_server);
	if (str)
		put_it("%s", convert_output_format(str, NULL, NULL));
	send_to_server("%s %s %s", command, new_flag, serv);
}

BUILT_IN_COMMAND(e_topic)
{
	char	*arg = NULL;
	char	*arg2;
	ChannelList *chan;
	int server;
	int unset = 0;	
	
	context;
	arg = next_arg(args, &args);
	if (!my_stricmp(command, "UNTOPIC"))
		unset = 1;
	if (!(chan = prepare_command(&server, arg?(is_channel(arg)?arg:NULL):NULL, NO_OP)))
		return;
	if (unset)
	{
		my_send_to_server(server, "TOPIC %s :", chan->channel);
		return;
	}
	if (arg && (!(chan->mode&MODE_TOPIC) || chan->chop))
	{
		if (is_channel(arg))
		{
			add_last_type(&last_sent_topic[0], 1, NULL, NULL, chan->channel, args);
			if ((arg2 = next_arg(args, &args)))
				my_send_to_server(server, "%s %s :%s %s", command, chan->channel, arg2, args);
			else
				my_send_to_server(server, "%s %s", command, chan->channel);
		} 
		else
		{
			char *p = NULL;
			p = m_sprintf("%s%s%s", arg, arg?space:empty_string, args?args:empty_string);
			my_send_to_server(server, "%s %s :%s%s%s", command, chan->channel, arg, args?space:empty_string, args?args:empty_string);
			add_last_type(&last_sent_topic[0], 1, NULL, NULL, chan->channel, p);
			new_free(&p);
		}
	}
	else
		my_send_to_server(server, "%s %s", command, chan->channel);
}

BUILT_IN_COMMAND(send_2comm)
{
	char	*reason = NULL;

	context;
#ifdef CDE
	args = next_arg(args, &reason);
	if (!args)
		args = empty_string;
#endif
	if (!(args = next_arg(args, &reason)))
		args = empty_string;
	if (!reason || !*reason)
		reason = empty_string;

	if (!args || !*args || !strcmp(args, "*"))
	{
		args = get_current_channel_by_refnum(0);
		if (!args || !*args)
			args = "*";     /* what-EVER */
	}
	if (reason && *reason)
		send_to_server("%s %s :%s", command, args, reason);
	else
		send_to_server("%s %s", command, args);
}

BUILT_IN_COMMAND(send_kill)
{
	char	*reason = NULL;
	char	*r_file;

	context;
#ifdef CDE
	args = next_arg(args, &reason);
	if (!args)
		args = empty_string;
#endif
#ifdef WINNT
	r_file = m_sprintf("%s/BitchX.kil",get_string_var(CTOOLZ_DIR_VAR));
#else
	r_file = m_sprintf("%s/BitchX.kill",get_string_var(CTOOLZ_DIR_VAR));
#endif
	if ((reason = strchr(args, ' ')) != NULL)
		*reason++ = '\0';
	else
		if ((reason = get_reason(args, r_file)))
			reason = empty_string;
	new_free(&r_file);
	
	if (!strcmp(args, "*"))
	{
		args = get_current_channel_by_refnum(0);
		if (!args || !*args)
			args = "*";     /* what-EVER */
	}
	if (reason && *reason)
		send_to_server("%s %s :%s", command, args, reason);
	else
		send_to_server("%s %s", command, args);
}

/*
 * send_kick: sends a kick message to the server.  Works properly with
 * kick comments.
 */

BUILT_IN_COMMAND(send_kick)
{
	char	*kickee,
		*comment,
		*channel = NULL;
ChannelList *chan;
int server = from_server;
 	
	context;
        if (!(channel = next_arg(args, &args)))
        {
		userage("kick", helparg);
		return;
        }
        
        if (!(kickee = next_arg(args, &args)))
        {
		userage("kick", helparg);
 		return;
        }

        comment = args?args:get_reason(kickee, NULL);

	if (!(chan = prepare_command(&server, (channel&&!strcmp(channel, "*"))?NULL:channel, NEED_OP)))
		return;

	my_send_to_server(server, "%s %s %s :%s", command, chan->channel, kickee, comment);
}

BUILT_IN_COMMAND(send_mode)
{
char *channel;
	context;
	if ((channel = get_current_channel_by_refnum(0)))
		send_to_server("%s %s %s", command, channel, args? args: empty_string);
	else if (args && *args)
		send_to_server("%s %s", command, args);
}

/*
 * send_channel_com: does the same as send_com for command where the first
 * argument is a channel name.  If the first argument is *, it is expanded
 * to the current channel (a la /WHO).
 */
BUILT_IN_COMMAND(send_channel_com)
{
	char	*ptr,
		*s;
	context;
        ptr = next_arg(args, &args);

	if (ptr && !strcmp(ptr, "*"))
	{
		if ((s = get_current_channel_by_refnum(0)) != NULL)
			send_to_server("%s %s %s", command, s, args?args:empty_string);
		else
			say("%s * is not valid since you are not on a channel", command);
	}
	else if (ptr)
			send_to_server("%s %s %s", command, ptr, args?args:empty_string);
	else
		userage(command, helparg);
}

struct target_type
{
	char *nick_list;
	char *message;
	int  hook_type;
	char *command;
	char *format;
	int  level;
	char *output;
	char *other_output;
};

int current_target = 0;

/*
 * The whole shebang.
 *
 * The message targets are parsed and collected into one of 4 buckets.
 * This is not too dissimilar to what was done before, except now i 
 * feel more comfortable about how the code works.
 *
 * Bucket 0 -- Unencrypted PRIVMSGs to nicknames
 * Bucket 1 -- Unencrypted PRIVMSGs to channels
 * Bucket 2 -- Unencrypted NOTICEs to nicknames
 * Bucket 3 -- Unencrypted NOTICEs to channels
 *
 * All other messages (encrypted, and DCC CHATs) are dispatched 
 * immediately, and seperately from all others.  All messages that
 * end up in one of the above mentioned buckets get sent out all
 * at once.
 */
void 	send_text(char *nick_list, char *text, char *command, int hook, int log)
{
	int i, old_server;
	int lastlog_level;
	int not_done = 1;
	int is_current = 0;
static	int recursion = 0;	
	char *current_nick, *next_nick, *free_nick;
	char *line, *key;
	int old_window_display = window_display;
	        
struct target_type target[4] = 
{	
	{NULL, NULL, SEND_MSG_LIST,     "PRIVMSG", "*%s*> %s" , LOG_MSG, NULL, NULL },
	{NULL, NULL, SEND_PUBLIC_LIST,  "PRIVMSG", "%s> %s"   , LOG_PUBLIC, NULL, NULL },
	{NULL, NULL, SEND_NOTICE_LIST,  "NOTICE",  "-%s-> %s" , LOG_NOTICE, NULL, NULL }, 
	{NULL, NULL, SEND_NOTICE_LIST,  "NOTICE",  "-%s-> %s" , LOG_NOTICE, NULL, NULL }
};

	context;

	target[0].output = fget_string_var(FORMAT_SEND_MSG_FSET);
	target[1].output = fget_string_var(FORMAT_SEND_PUBLIC_FSET);
	target[1].other_output = fget_string_var(FORMAT_SEND_PUBLIC_OTHER_FSET);
	target[2].output = fget_string_var(FORMAT_SEND_NOTICE_FSET);
	target[3].output = fget_string_var(FORMAT_SEND_NOTICE_FSET);
	target[3].other_output = fget_string_var(FORMAT_SEND_NOTICE_FSET);

	if (recursion)
		hook = 0;
	window_display = hook;
	recursion++;
	free_nick = next_nick = m_strdup(nick_list);

	while ((current_nick = next_nick))
	{
		if ((next_nick = strchr(current_nick, ',')))
			*next_nick++ = 0;

		if (!*current_nick)
			continue;

		if (*current_nick == '%')
		{
			if ((i = get_process_index(&current_nick)) == -1)
				say("Invalid process specification");
			else
				text_to_process(i, text, 1);
		}
		/*
		 * This test has to be here because it is legal to
		 * send an empty line to a process, but not legal
		 * to send an empty line anywhere else.
		 */
		else if (!text || !*text)
			;
		else if (*current_nick == '-')
			dcc_ftpcommand(current_nick+1, text);
		else if (*current_nick == '"')
			send_to_server("%s", text);
		else if (*current_nick == '/')
		{
			line = m_opendup(current_nick, space, text, NULL);
			parse_command(line, 0, empty_string);
			new_free(&line);
		}
		else if (*current_nick == '=')
		{
			char *copy = NULL;
			if (!dcc_active(current_nick + 1) && !dcc_activebot(current_nick+1) && !dcc_activeraw(current_nick+1))
			{
				yell("No DCC CHAT connection open to %s", current_nick + 1);
				continue;
			}
			if ((key = is_crypted(current_nick)) != 0)
			{
				copy = m_strdup(text);
				line = crypt_msg(copy, key);
			}
			else
				line = m_strdup(text);

			old_server = from_server;
			from_server = -1;
			if (dcc_active(current_nick+1))
			{
				dcc_chat_transmit(current_nick + 1, line, copy ? copy : line, command);
				if (hook)
					addtabkey(current_nick, "msg", 0);
			}
			else if (dcc_activebot(current_nick + 1))
				dcc_bot_transmit(current_nick + 1, line, command);
			else 
				dcc_raw_transmit(current_nick + 1, line, command);

			from_server = old_server;
			new_free(&line);
			new_free(&copy);
		}
		else
		{
			char *copy = NULL;
			if (doing_notice)
			{
				say("You cannot send a message from within ON NOTICE");
				continue;
			}

			copy = m_strdup(text);

			if (!(i = is_channel(current_nick)) && hook)
				addtabkey(current_nick, "msg", 0);
				
			if (doing_notice || (command && !strcmp(command, "NOTICE")))
				i += 2;

			if ((key = is_crypted(current_nick)))
			{
				lastlog_level = set_lastlog_msg_level(target[i].level);
				message_from(current_nick, target[i].level);

				line = crypt_msg(copy, key);
				if (hook && do_hook(target[i].hook_type, "%s %s", current_nick, copy))
					put_it("%s", convert_output_format(fget_string_var(target[i].hook_type == SEND_MSG_LIST?FORMAT_ENCRYPTED_PRIVMSG_FSET:FORMAT_ENCRYPTED_NOTICE_FSET),
					"%s %s %s %s",update_clock(GET_TIME), get_server_nickname(from_server),current_nick, copy));

				send_to_server("%s %s :%s", target[i].command, current_nick, line);
				new_free(&line);
				new_free(&copy);
				set_lastlog_msg_level(lastlog_level);
			}
			else
			{
				if (target[i].nick_list)
					malloc_strcat(&target[i].nick_list, ",");
				malloc_strcat(&target[i].nick_list, current_nick);
				if (!target[i].message)
					target[i].message = text;
			}
		}
	}
	
	for (i = 0; i < 4; i++)
	{
		char *copy = NULL;
		if (!target[i].message)
			continue;

		lastlog_level = set_lastlog_msg_level(target[i].level);
		message_from(target[i].nick_list, target[i].level);

		copy = m_strdup(target[i].message);
		/* do we forward this?*/

		if (forwardnick && not_done)
		{
			send_to_server("NOTICE %s :-> *%s* %s", forwardnick, nick_list, copy);
			not_done = 0;
		}

		/* log it if logging on */
		if (log)
			logmsg(LOG_SEND_MSG, target[i].nick_list, target[i].message, 0);
		/* save this for /oops */
	
		malloc_strcpy(&last_sent_msg_body, target[i].message);

		if (i == 1 || i == 3)
		{
			char *channel;
			is_current = is_current_channel(target[i].nick_list, from_server, 0);
			if ((channel = get_current_channel_by_refnum(0)))
			{
				ChannelList *chan;
				NickList *nick = NULL;
				if ((chan = lookup_channel(channel, from_server, 0)))
				{
					chan->stats_pubs++;
					if ((nick = find_nicklist_in_channellist(get_server_nickname(from_server), chan, 0)))
						nick->stat_pub++;
				}
			}
		}
		else
			is_current = 1;
			
		if (hook && do_hook(target[i].hook_type, "%s %s", target[i].nick_list, target[i].message))
			if (is_current)
				put_it("%s", convert_output_format(target[i].output,
					"%s %s %s %s",update_clock(GET_TIME), target[i].nick_list, get_server_nickname(from_server), copy));
			else
				put_it("%s", convert_output_format(target[i].other_output,
					"%s %s %s %s",update_clock(GET_TIME), target[i].nick_list, get_server_nickname(from_server), copy));
		if ((i == 0))
		{
			malloc_strcpy(&sent_nick, target[0].nick_list);
			add_last_type(&last_sent_msg[0], MAX_LAST_MSG, NULL, NULL, target[i].nick_list, copy);
		}
		else if ((i == 2) || (i == 3))
			add_last_type(&last_sent_notice[0], MAX_LAST_MSG, target[i].nick_list, NULL, get_server_nickname(from_server), copy);
		new_free(&copy);
		send_to_server("%s %s :%s", target[i].command, target[i].nick_list, target[i].message);
		new_free(&target[i].nick_list);
		target[i].message = NULL;
		set_lastlog_msg_level(lastlog_level);
		message_from(NULL, LOG_CRAP);
	}
	
	if (hook && get_server_away(curr_scr_win->server) && get_int_var(AUTO_UNMARK_AWAY_VAR))
		parse_line(NULL, "AWAY", empty_string, 0, 0);

	new_free(&free_nick);
	window_display = old_window_display;
	recursion--;
}

char *function_eleet _((char *));
char *function_annoy _((char *));
char *function_cool _((char *));
char *function_cool2 _((char *));

BUILT_IN_COMMAND(do_send_text)
{
	char	*tmp;
	char	*text = args;
	char	*new_text = NULL;	
	char	*cmd = NULL;
	context;
	if (command)
	{
		tmp = get_current_channel_by_refnum(0);
		if (args && *args == '-' && *(args+1))
		{
			char *comm;
			switch(*(args+1))
			{
				case 'e':
					comm = next_arg(args, &args);
					new_text = function_eleet(args);
					break;
				case 'c':
					comm = next_arg(args, &args);
					new_text = function_cool(args);
					break;
				case 'l':
					comm = next_arg(args, &args);
					new_text = function_cool2(args);
					break;
				case 'a':
					comm = next_arg(args, &args);
					new_text = function_annoy(args);
				default:
					break;
			}
		}
	}
	else
	{
		tmp = get_target_by_refnum(0);
		cmd = get_target_cmd_by_refnum(0);
	}
	if (cmd)
	{
#ifdef WANT_DLL
		int dllcnt = -1;
		IrcCommandDll *dll = NULL;
		dll = find_dll_command(cmd, &dllcnt);
		if (dll && (dllcnt == -1 || dllcnt == 1))
			dll->func (dll_commands, dll->server_func, new_text?new_text:text, NULL, dll->help);
		else 
#endif
		if (!my_stricmp(cmd, "SAY") || !my_stricmp(cmd, "PRIVMSG") || !my_stricmp(cmd, "MSG"))
			send_text(tmp, new_text? new_text:text, NULL, 1, 1);
		else if (!my_stricmp(cmd, "NOTICE"))
			send_text(tmp, new_text? new_text:text, "NOTICE", 1, 1);
		else if (!my_stricmp(cmd, "CSAY"))
			csay(NULL, new_text?new_text:text, NULL, NULL);
		else if (!my_stricmp(cmd, "CMSG"))
			cmsg(NULL, new_text?new_text:text, NULL, NULL);
					
	}
	else
		send_text(tmp, new_text? new_text:text, NULL, 1, 1);
	new_free(&new_text);
}

BUILT_IN_COMMAND(do_msay)
{
	char *channels = NULL;
	int i = get_window_server(0);
	ChannelList *chan;
	context;
	if (i != -1)
	{
		int old_from_server = from_server;
		for (chan  = server_list[i].chan_list; chan; chan = chan->next)
		{
			malloc_strcat(&channels, chan->channel);
			if (chan->next)
				malloc_strcat(&channels, ",");
		}
		from_server = i;
		if (channels)
			send_text(channels, args, NULL, 1, 1);
		new_free(&channels);
		from_server = old_from_server;
	} else
		bitchsay("No server for this window");
}

/*
 * command_completion: builds lists of commands and aliases that match the
 * given command and displays them for the user's lookseeing 
 */
char **get_builtins _((char *, int *));
void command_completion(char unused, char *not_used)
{
	int	do_aliases = 1, 
		do_functions = 1;
	int	cmd_cnt = 0,
		alias_cnt = 0,
		function_cnt = 0,
		i,
		c;
	char	**aliases = NULL;
	char	**functions = NULL;
	
	char	*line = NULL,
		*com,
		*cmdchars,
		*rest,
		firstcmdchar[2] = "/";
	IrcCommand	*command;
#ifdef WANT_DLL
	IrcCommandDll	*dll = NULL;
#endif
	char	buffer[BIG_BUFFER_SIZE + 1];
	
	context;
	malloc_strcpy(&line, get_input());
	if ((com = next_arg(line, &rest)) != NULL)
	{
		if (!(cmdchars = get_string_var(CMDCHARS_VAR)))
			cmdchars = DEFAULT_CMDCHARS;
		if (strchr(cmdchars, *com))
		{
			*firstcmdchar = *cmdchars;
			com++;
			if (*com && strchr(cmdchars, *com))
			{
				do_aliases = 0;
				do_functions = 0;
				alias_cnt = 0;
				function_cnt = 0;
				com++;
			}
			else if (*com && strchr("$", *com))
			{
				do_aliases = 0;
				alias_cnt = 0;
				do_functions = 1;
				com++;
			} else
				do_aliases = do_functions = 1;

			upper(com);
			if (do_aliases)
				aliases = glob_cmd_alias(com, &alias_cnt);

			if (do_functions)
				functions = get_builtins(com, &function_cnt);

			if ((command = find_command(com, &cmd_cnt)) != NULL)
			{
				if (cmd_cnt < 0)
					cmd_cnt *= -1;
				/* special case for the empty string */
				if (!*command->name)
				{
					command++;
					cmd_cnt = NUMBER_OF_COMMANDS;
				}
			}
			if ((alias_cnt == 1) && (cmd_cnt == 0))
			{
				snprintf(buffer, BIG_BUFFER_SIZE, "%s%s %s", firstcmdchar, *aliases, rest);
				set_input(buffer);
				new_free((char **)aliases);
				new_free((char **)&aliases);
				update_input(UPDATE_ALL);
			}
			else if (((cmd_cnt == 1) && (alias_cnt == 0)) ||
			    ((cmd_cnt == 1) && (alias_cnt == 1) &&
			    !strcmp(aliases[0], command[0].name)))
			{
				snprintf(buffer, BIG_BUFFER_SIZE, "%s%s%s %s", firstcmdchar,
					do_aliases ? empty_string : firstcmdchar,
					command[0].name, rest);
				set_input(buffer);
				update_input(UPDATE_ALL);
			}
			else
			{
				*buffer = 0;
				if (command)
				{
					bitchsay("Commands:");
					*buffer = 0;
					c = 0;
					for (i = 0; i < cmd_cnt; i++)
					{
						strmcat(buffer, command[i].name, BIG_BUFFER_SIZE);
						strmcat(buffer, " ", BIG_BUFFER_SIZE);
						if (++c == 4)
						{
							put_it("%s", convert_output_format("$G $[15]0 $[15]1 $[15]2 $[15]3", "%s", buffer));
							*buffer = 0;
							c = 0;
						}
					}
					if (c)
						put_it("%s", convert_output_format("$G $[15]0 $[15]1 $[15]2 $[15]3", "%s", buffer));
#ifdef WANT_DLL
					if (dll_commands)
						bitchsay("DLL Commands:");
					*buffer = 0;
					c = 0;
					for (dll = dll_commands; dll; dll = dll->next)
					{
						strmcat(buffer, dll->name, BIG_BUFFER_SIZE);
						strmcat(buffer, " ", BIG_BUFFER_SIZE);
						if (++c == 4)
						{
							put_it("%s", convert_output_format("$G $[15]0 $[15]1 $[15]2 $[15]3", "%s", buffer));
							*buffer = 0;
							c = 0;
						}
					}
					if (c)
						put_it("%s", convert_output_format("$G $[15]0 $[15]1 $[15]2 $[15]3", "%s", buffer));
#endif
				}
				if (aliases)
				{
					bitchsay("Aliases:");
					*buffer = 0;
					c = 0;
					for (i = 0; i < alias_cnt; i++)
					{
						strmcat(buffer, aliases[i], BIG_BUFFER_SIZE);
						strmcat(buffer, " ", BIG_BUFFER_SIZE);
						if (++c == 4)
						{
							put_it("%s", convert_output_format("$G $[15]0 $[15]1 $[15]2 $[15]3", "%s", buffer));
							*buffer = 0;
							c = 0;
						}
						new_free(&(aliases[i]));
					}
					if (strlen(buffer) > 1)
						put_it("%s", convert_output_format("$G $[15]0 $[15]1 $[15]2 $[15]3", "%s", buffer));
					new_free((char **)&aliases);
				}
				if (functions)
				{
					bitchsay("Functions:");
					*buffer = 0;
					c = 0;
					for (i = 0; i < function_cnt; i++)
					{
						strmcat(buffer, functions[i], BIG_BUFFER_SIZE);
						strmcat(buffer, " ", BIG_BUFFER_SIZE);
						if (++c == 4)
						{
							put_it("%s", convert_output_format("$G $[15]0 $[15]1 $[15]2 $[15]3", "%s", buffer));
							*buffer = 0;
							c = 0;
						}
						new_free(&(functions[i]));
					}
					if (strlen(buffer) > 1)
						put_it("%s", convert_output_format("$G $[15]0 $[15]1 $[15]2 $[15]3", "%s", buffer));
					new_free((char **)&functions);
				}
				if (!*buffer)
					term_beep();
			}
		}
		else
			term_beep();
	}
	else
		term_beep();
	new_free(&line);
}


BUILT_IN_COMMAND(e_call)
{
	dump_call_stack();
}


/* parse_line: This is the main parsing routine.  It should be called in
 * almost all circumstances over parse_command().
 *
 * parse_line breaks up the line into commands separated by unescaped
 * semicolons if we are in non interactive mode. Otherwise it tries to leave
 * the line untouched.
 *
 * Currently, a carriage return or newline breaks the line into multiple
 * commands too. This is expected to stop at some point when parse_command
 * will check for such things and escape them using the ^P convention.
 * We'll also try to check before we get to this stage and escape them before
 * they become a problem.
 *
 * Other than these two conventions the line is left basically untouched.
 */
/* Ideas on parsing: Why should the calling function be responsible
 *  for removing {} blocks?  Why cant this parser cope with and {}s
 *  that come up?
 */
extern void parse_line (char *name, char *org_line, char *args, int hist_flag, int append_flag)
{
	char	*line = NULL,
		*free_line,
		*stuff,
		*s,
		*t;
	int	args_flag = 0;

	context;

	make_local_stack(name);

#ifdef __GNUC__
	line = alloca(strlen(org_line) + 1);
	strcpy(line, org_line);
#else
  	malloc_strcpy(&line, org_line);
#endif
	free_line = line;

	if (!*org_line)
		do_send_text(NULL, empty_string, empty_string, NULL);
	else if (args) do
	{
       	        while (*line == '{') 
               	{
                       	if ((stuff = next_expr(&line, '{')) == NULL) 
			{
       	                        yell("Unmatched {");
                       	        new_free(&free_line);
				destroy_local_stack();
                               	return;
                        }
       	                parse_line(name, stuff, args, hist_flag, append_flag);
			while (line && *line && ((*line == ';') || (my_isspace(*line))))
				*line++ = '\0';
		}

		if (!line || !*line)
		{
			destroy_local_stack();
#ifndef __GNUC__
			new_free(&free_line);
#endif
			return;
		}
		stuff = expand_alias(line, args, &args_flag, &line);
		if (!line && append_flag && !args_flag && args && *args)
			m_3cat(&stuff, " ", args);
			
		parse_command(stuff, hist_flag, args);
#ifndef __GNUC__
		if (!line) 
			new_free(&free_line);
#endif
       	        new_free(&stuff);
	} while (line && *line);
        else
	{
		if (load_depth)
			parse_command(line, hist_flag, args);
		else
		{
			while ((s = line))
			{
				if ((t = sindex(line, "\r\n")))
				{
					*t++ = '\0';
					line = t;
				}
				else
					line = NULL;
				parse_command(s, hist_flag, args);
			}
		}
	}
#ifndef __GNUC__
	new_free(&free_line);
#endif
	destroy_local_stack();
	return;
}


/*
 * parse_command: parses a line of input from the user.  If the first
 * character of the line is equal to irc_variable[CMDCHAR_VAR].value, the
 * line is used as an irc command and parsed appropriately.  If the first
 * character is anything else, the line is sent to the current channel or to
 * the current query user.  If hist_flag is true, commands will be added to
 * the command history as appropriate.  Otherwise, parsed commands will not
 * be added. 
 *
 * Parse_command() only parses a single command.  In general, you will want
 * to use parse_line() to execute things.Parse command recognized no quoted
 * characters or anything (beyond those specific for a given command being
 * executed). 
 */
int
parse_command(line, hist_flag, sub_args)
	char	*line;
	int	hist_flag;
	char	*sub_args;
{
static	unsigned int	 level = 0;
	unsigned int	display,
			old_display_var;
		char	*cmdchars,
			*com,
			*this_cmd = NULL;
		int	args_flag = 0,
			add_to_hist,
			cmdchar_used = 0;
		int	noisy = 1;
		
	context;
	if (!line || !*line)
		return 0;

	if (get_int_var(DEBUG_VAR) & DEBUG_COMMANDS)
		yell("Executing [%d] %s", level, line);
	level++;

	if (!(cmdchars = get_string_var(CMDCHARS_VAR)))
		cmdchars = DEFAULT_CMDCHARS;


#ifdef __GNUC__
	this_cmd = alloca(strlen(line) + 1);
	strcpy(this_cmd, line);
#else
	this_cmd = m_strdup(line);
#endif

	set_current_command(this_cmd);
	add_to_hist = 1;
	display = window_display;
	old_display_var = (unsigned) get_int_var(DISPLAY_VAR);
	for ( ;*line;line++)
	{
		if (*line == '^' && (!hist_flag || cmdchar_used))
		{
			if (!noisy)
				break;
			noisy = 0;
		}
		else if (strchr(cmdchars, *line))
		{
			cmdchar_used++;
			if (cmdchar_used > 2)
				break;
		}
		else
			break;
	}

	if (!noisy)
		window_display = 0;
	com = line;

	/*
	 * always consider input a command unless we are in interactive mode
	 * and command_mode is off.   -lynx
	 */
	if (hist_flag && !cmdchar_used && !get_int_var(COMMAND_MODE_VAR))
	{
		do_send_text(NULL, line, empty_string, NULL);
		if (hist_flag && add_to_hist)
		{
			add_to_history(this_cmd);
			set_input(empty_string);
		}
		/* Special handling for ' and : */
	}
	else if (*com == '\'' && get_int_var(COMMAND_MODE_VAR))
	{
		do_send_text(NULL, line+1, empty_string, NULL);
		if (hist_flag && add_to_hist)
		{
			add_to_history(this_cmd);
			set_input(empty_string);
		}
	}
	else if ((*com == '@') || (*com == '('))
	{
		/* This kludge fixes a memory leak */
		char	*tmp, *l_ptr;
		/*
		 * This new "feature" masks a weakness in the underlying
		 * grammar that allowed variable names to begin with an
		 * lparen, which inhibited the expansion of $s inside its
		 * name, which caused icky messes with array subscripts.
		 *
		 * Anyhow, we now define any commands that start with an
		 * lparen as being "expressions", same as @ lines.
		 */
		if (*com == '(')
		{
			if ((l_ptr = MatchingBracket(line + 1, '(', ')')))
				*l_ptr++ = 0;
		}

		if ((tmp = parse_inline(line + 1, sub_args, &args_flag)))
			new_free(&tmp);

		if (hist_flag && add_to_hist)
		{
			add_to_history(this_cmd);
			set_input(empty_string);
		}
	}
	else
	{
		char	*rest,
			*alias = NULL,
			*alias_name = NULL;
		int	cmd_cnt,
#ifdef WANT_DLL
			dll_cnt,
#endif
			alias_cnt = 0;
		IrcCommand	*command = NULL; 
#ifdef WANT_DLL
		IrcCommandDll	*dll = NULL;
#endif		
		context;
		if ((rest = (char *) strchr(com, ' ')) != NULL)
			*(rest++) = (char) 0;
		else
			rest = empty_string;

		upper(com);

		if (cmdchar_used < 2)
			alias = get_cmd_alias(com, &alias_cnt, &alias_name);

		if (alias && alias_cnt < 0)
		{
			if (hist_flag && add_to_hist)
			{
				add_to_history(this_cmd);
				set_input(empty_string);
			}
			parse_line(alias_name, alias, rest, 0, 1);
			new_free(&alias_name);
		}
		else
		{
			/* History */
			if (*com == '!')
			{
				if ((com = do_history(com + 1, rest)) != NULL)
				{
					if (level == 1)
					{
						set_input(com);
						update_input(UPDATE_ALL);
					}
					else
						parse_command(com, 0, sub_args);

					new_free(&com);
				}
				else
					set_input(empty_string);
			}
			else
			{
				char unknown[] = "Unknown command:";
				
				if (hist_flag && add_to_hist)
				{
					add_to_history(this_cmd);
					set_input(empty_string);
				}
				command = find_command(com, &cmd_cnt);
#ifdef WANT_DLL
				dll = find_dll_command(com, &dll_cnt);
				if ((dll && (dll_cnt < 0)) || ((alias_cnt == 0) && (dll_cnt == 1)))
				{
					context;
					strcpy(cx_function, dll->name);
					if (dll->func)
					{
						dll->func(dll_commands, dll->server_func, rest, sub_args, dll->help);
						if (dll->result)
							put_it("%s", dll->result);
					}
					else
						bitchsay("%s: command disabled", dll->name);
					*cx_function = 0;
				}
				else 
#endif
				if (cmd_cnt < 0 || (alias_cnt == 0 && cmd_cnt == 1))
				{
					context;
					strcpy(cx_function, command->name);
					if (command->func)
						command->func(command->server_func, rest, sub_args, command->help);
					else
						bitchsay("%s: command disabled", command->name);
					*cx_function = 0;
				}
				else if (alias_cnt == 1 && cmd_cnt == 1 && 
				    (!strcmp(alias_name, command->name)))
					parse_line(alias_name, alias, rest, 0, 1);
				else if (alias_cnt == 1 && cmd_cnt == 0)
					parse_line(alias_name, alias, rest, 0, 1);
				else if (!my_stricmp(com, get_server_nickname(from_server)))
					me(NULL, rest, empty_string, NULL);
#ifdef WANT_TCL
				else
				{
					context;
					if (interp)
					{
						int err;
						err = Tcl_Eval(interp, this_cmd+1);
						if (err == TCL_OK)
						{
							if (interp->result && *interp->result)
								bitchsay("%s %s", *interp->result?empty_string:unknown, *interp->result?interp->result:empty_string);
						}
						else
						{
							if (alias_cnt + cmd_cnt > 1)
								bitchsay("Ambiguous command: %s", com);
							else if (get_int_var(DISPATCH_UNKNOWN_COMMANDS_VAR))
								send_to_server("%s %s", com, rest);
							else if (interp->result && *interp->result)
							{
								if (check_help_bind(com))
									bitchsay("%s", interp->result);
							}
							else
								bitchsay("%s %s", unknown, com);
								
						}
					} else if (get_int_var(DISPATCH_UNKNOWN_COMMANDS_VAR))
						send_to_server("%s", this_cmd);
					else if (alias_cnt + cmd_cnt > 1)
						bitchsay("Ambiguous command: %s", com);
					else
						bitchsay("%s %s", unknown, com);
				}
#else
				else if (get_int_var(DISPATCH_UNKNOWN_COMMANDS_VAR))
					send_to_server("%s", this_cmd);
				else if (alias_cnt + cmd_cnt > 1)
					bitchsay("Ambiguous command: %s", com);
				else
					bitchsay("%s %s", unknown, com);
#endif
			}
			if (alias)
				new_free(&alias_name);
		}
	}
	if (old_display_var != get_int_var(DISPLAY_VAR))
		window_display = get_int_var(DISPLAY_VAR);
	else
		window_display = display;

#ifndef __GNUC__
	new_free(&this_cmd);
#endif
	level--;
	unset_current_command();
	return 0;
}


/*
 * load: the /LOAD command.  Reads the named file, parsing each line as
 * though it were typed in (passes each line to parse_command). 
	Right now, this is broken, as it doesnt handle the passing of
	the '-' flag, which is meant to force expansion of expandos
	with the arguments after the '-' flag.  I think its a lame
	feature, anyhow.  *sigh*.
 */

BUILT_IN_COMMAND(load)
{
	FILE	*fp;
	char	*filename,
		*expanded = NULL;
	int	flag = 0;
        int     paste_level = 0;
	char	*start,
		*current_row = NULL,
		buffer[BIG_BUFFER_SIZE + 1];
	int	no_semicolon = 1;
	char	*irc_path;
	int	display;
        int     ack = 0;

	context;
	irc_path = get_string_var(LOAD_PATH_VAR);
	if (!irc_path)
	{
		bitchsay("LOAD_PATH has not been set");
		return;
	}

	if (load_depth == MAX_LOAD_DEPTH)
	{
		bitchsay("No more than %d levels of LOADs allowed", MAX_LOAD_DEPTH);
		return;
	}
	load_depth++;
	status_update(0);

	/* 
	 * We iterate over the whole list -- if we use the -args flag, the
	 * we will make a note to exit the loop at the bottom after we've
	 * gone through it once...
	 */
	while (args && *args && (filename = next_arg(args, &args)))
	{
		/* 
		   If we use the args flag, then we will get the next
		   filename (via continue) but at the bottom of the loop
		   we will exit the loop 
		 */
		if (my_strnicmp(filename, "-args", strlen(filename)) == 0)
		{
			flag = 1;
			continue;
		}
		else if ((expanded = expand_twiddle(filename)))
		{
			if (!(fp = uzfopen(&expanded, irc_path)))
			{
				/* uzfopen emits an error if the file
				 * is not found, so we dont have to. */
				status_update(1);
				load_depth--;
				new_free(&expanded);
				return;
			}
			if (command && *command == 'W')
			{
				bitchsay ("%s", expanded);
				if (fp)
					fclose (fp);
				status_update(1);
				load_depth--;
				new_free(&expanded);
				return;
			}
/* Reformatted by jfn */
/* *_NOT_* attached, so dont "fix" it */
{
	int	in_comment 	= 0;
	int	comment_line 	= -1;
	int 	line 		= 1;
	int 	paste_line	= -1;

	display = window_display;
	window_display = 0;
	current_row = NULL;

	for (;;line++)
	{
	       if (fgets(buffer,BIG_BUFFER_SIZE / 2, fp))
	       {
			int     len;
			char    *ptr;

			for (start = buffer; my_isspace(*start); start++)
				;
			if (!*start || *start == '#')
				continue;

			len = strlen(start);
			/*
			 * this line from stargazer to allow \'s in scripts for continued
			 * lines <spz@specklec.mpifr-bonn.mpg.de>
			 */
			/* 
			   If we have \\ at the end of the line, that
			   should indicate that we DONT want the slash to 
			   escape the newline (hop)

			   We cant just do start[len-2] because we cant say
			   what will happen if len = 1... (a blank line)

			   SO.... 
			   If the line ends in a newline, and
			   If there are at least 2 characters in the line
				and the 2nd to the last one is a \ and,
			   If there are EITHER 2 characters on the line or
				the 3rd to the last character is NOT a \ and,
			   If the line isnt too big yet and,
			   If we can read more from the file,
			   THEN -- adjust the length of the string
			*/
			while ( (start[len-1] == '\n') && 
				(len >= 2 && start[len-2] == '\\') &&
				(len < 3 || start[len-3] != '\\') && 
				(len < (BIG_BUFFER_SIZE / 2)) && 
				(fgets(&(start[len-2]), (BIG_BUFFER_SIZE / 2) - len, fp)))
			{
				len = strlen(start);
				line++;
			}

			if (start[len - 1] == '\n')
				start[--len] = '\0';

			while (start && *start)
			{
				char    *optr = start;

				/* Skip slashed brackets */
				while ((ptr = sindex(optr, "{};/")) && 
				      ptr != optr && ptr[-1] == '\\')
					optr = ptr+1;

				/* 
				 * if no_semicolon is set, we will not attempt
				 * to parse this line, but will continue
				 * grabbing text
				 */
				if (no_semicolon)
					no_semicolon = 0;
				else if ((!ptr || (ptr != start || *ptr == '/')) && current_row)
				{
					if (!paste_level)
					{
						parse_line(NULL, current_row, flag ? args :get_int_var(INPUT_ALIASES_VAR) ?empty_string: NULL, 0, 0);
						new_free(&current_row);
					}
					else if (!in_comment)
						malloc_strcat(&current_row, ";");
				}

				if (ptr)
				{
					char    c = *ptr;

					*ptr = '\0';
					if (!in_comment)
						malloc_strcat(&current_row, start);
					*ptr = c;

					switch (c)
					{
		/* switch statement tabbed back */
case '/' :
{
	/* If we're in a comment, any slashes that arent preceeded by
	   a star is just ignored (cause its in a comment, after all >;) */
	if (in_comment)
	{
		/* ooops! cant do ptr[-1] if ptr == optr... doh! */
		if ((ptr > start) && (ptr[-1] == '*'))
		{
			in_comment = 0;
			comment_line = -1;
		}
		else
			break;
	}
	/* We're not in a comment... should we start one? */
	else
	{
		/* COMMENT_BREAKAGE_VAR */
		if ((ptr[1] == '*') && !get_int_var(COMMENT_BREAKAGE_VAR) && (ptr == start))
			/* This hack (at the request of Kanan) makes it
			   so you can only have comments at the BEGINNING
			   of a line, and any midline comments are ignored.
			   This is required to run lame script packs that
			   do ascii art crap (ie, phoenix, textbox, etc) */
		{
			/* Yep. its the start of a comment. */
			in_comment = 1;
			comment_line = line;
		}
		else
		{
			/* Its NOT a comment. Tack it on the end */
			malloc_strcat(&current_row, "/");

			/* Is the / is at the EOL? */
			if (ptr[1] == '\0')
			{
				/* If we are NOT in a block alias, */
				if (!paste_level)
				{
					/* Send the line off to parse_line */
					parse_line(NULL, current_row, flag ? args : get_int_var(INPUT_ALIASES_VAR) ? empty_string : NULL, 0, 0);
					new_free(&current_row);
					ack = 0; /* no semicolon.. */
				}
				else
					/* Just add a semicolon and keep going */
					ack = 1; /* tack on a semicolon */
			}
		}


	}
	no_semicolon = 1 - ack;
	ack = 0;
	break;
}
case '{' :
{
	if (in_comment)
		break;

	/* If we are opening a brand new {} pair, remember the line */
	if (!paste_level)
		paste_line = line;
		
	paste_level++;
	if (ptr == start)
		malloc_strcat(&current_row, " {");
	else
		malloc_strcat(&current_row, "{");
	no_semicolon = 1;
	break;
}
case '}' :
{
	if (in_comment)
		break;
        if (!paste_level)
		yell("Unexpected } in %s, line %d", expanded, line);
	else 
	{
        	--paste_level;

		if (!paste_level)
			paste_line = -1;
		malloc_strcat(&current_row, "}"); /* } */
		no_semicolon = ptr[1]? 1: 0;
	}
	break;
}
case ';' :
{
	if (in_comment)
		break;
	if ((*(ptr+1) == '\0') && (!paste_level))
	{
		parse_line(NULL, current_row, flag ? args : get_int_var(INPUT_ALIASES_VAR) ? empty_string : NULL, 0, 0);
		new_free(&current_row);
	}
	else
		malloc_strcat(&current_row, ";");
	no_semicolon = 1;
	break;
}
	/* End of reformatting */
					}
					start = ptr+1;
				}
				else
				{
					if (!in_comment)
						malloc_strcat(&current_row, start);
					start = NULL;
				}
			}
		}
		else
			break;
	}
	if (in_comment)
		yell("File %s ended with an unterminated comment in line %d", expanded, comment_line);
	if (current_row)
	{
		if (paste_level)
			yell("Unexpected EOF in %s trying to match '{' at line %d",
					expanded, paste_line);
		else
			parse_line(NULL,current_row, flag ? args: get_int_var(INPUT_ALIASES_VAR) ? empty_string : NULL,0,0);
		new_free(&current_row);
	}
	new_free(&expanded);
	fclose(fp);
	if (get_int_var(DISPLAY_VAR))
	       window_display = display;
}
/* End of reformatting */
		}
		else
			bitchsay("Unknown user");
		/* 
		 * brute force -- if we are using -args, load ONLY one file
		 * then exit the while loop.  We could have done this
		 * by assigning args to NULL, but that would only waste
		 * cpu cycles by relooping...
		 */
		if (flag)
			break;
	}	/* end of the while loop that allows you to load
		   more then one file at a time.. */
	status_update(1);
	load_depth--;
/*	send_to_server("%s", "WAITFORNOTIFY");*/
}



/* The SENDLINE command.. */
BUILT_IN_COMMAND(sendlinecmd)
{
	int	server;
	int	display;

	context;
	server = from_server;
	display = window_display;
	window_display = 1;
	parse_line(NULL, args, get_int_var(INPUT_ALIASES_VAR) ?empty_string : NULL, 1, 0);
	update_input(UPDATE_ALL);
	window_display = display;
	from_server = server;
}

/*
 * irc_clear_screen: the CLEAR_SCREEN function for BIND.  Clears the screen and
 * starts it if it is held 
 */
/*ARGSUSED*/
void irc_clear_screen(char key, char *ptr)
{
	context;
	hold_mode(NULL, OFF, 1);
	my_clear(NULL, empty_string, empty_string, NULL);
}

BUILT_IN_COMMAND(cd)
{
	char	*arg,
		*expand;
	char buffer[BIG_BUFFER_SIZE + 1];

	context;
	if ((arg = next_arg(args, &args)) != NULL)
	{
		if ((expand = expand_twiddle(arg)) != NULL)
		{
			if (chdir(expand))
				bitchsay("CD: %s", strerror(errno));
			new_free(&expand);
		}
		else
			bitchsay("CD No such dir");
	}
	getcwd(buffer, BIG_BUFFER_SIZE+1);
	bitchsay("Current directory: %s", buffer);
}

BUILT_IN_COMMAND(exec_cmd)
{
	char	buffer[BIG_BUFFER_SIZE + 1];
	context;
	snprintf(buffer, BIG_BUFFER_SIZE, "%s %s", command, args);
	execcmd(NULL, buffer, subargs, helparg);
}

BUILT_IN_COMMAND(describe)
{
	char	*target;

	context;
	target = next_arg(args, &args);
	if (target && args && *args)
	{
		int	old;
		int	from_level;
		char	*message;

		message = args;
		send_ctcp(CTCP_PRIVMSG, target, CTCP_ACTION, "%s", message);

		old = set_lastlog_msg_level(LOG_ACTION);
		from_level = message_from_level(LOG_ACTION);
		if (do_hook(SEND_ACTION_LIST, "%s %s", target, message))
	put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_ACTION_OTHER_FSET), 
		"%s %s %s %s", update_clock(GET_TIME), get_server_nickname(from_server), target, message));
		set_lastlog_msg_level(old);
		message_from_level(from_level);

	}
	else if (target && (!args || !*args))
		userage("describe", helparg);
	else
		userage("describe", helparg);
}

/*
 * New 'me' command.
 */

BUILT_IN_COMMAND(me)
{
	context;
	if (args && *args)
	{
		char	*target = NULL;
		if (!command)
			target = get_current_channel_by_refnum(0);
		else if (!my_stricmp(command, "dme"))
		{
			DCC_list *Client = NULL;
			for (Client = ClientList; Client; Client = Client->next)
				if ((Client->flags & DCC_TYPES) == DCC_CHAT)
				{
					target = Client->user;
					break;
				}
		}
		else if (!my_stricmp(command, "qme"))
		{
			target = query_nick();
			if (!target)
			{
				Window *tmp; int flag = 1;
				while ((tmp = traverse_all_windows(&flag)))
					if ((target = tmp->query_nick))
						break;
			}
		}		
		else
			target = get_target_by_refnum(0);
		if (target)
		{
			int	old;
			char	*message;
			message = args;
			send_ctcp(CTCP_PRIVMSG, target, CTCP_ACTION, "%s", message);

			message_from(target, LOG_ACTION);
			old = set_lastlog_msg_level(LOG_ACTION);
			if (do_hook(SEND_ACTION_LIST, "%s %s", target, message))
			{
				if (strchr("&#", *target))
					put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_ACTION_FSET), 
						"%s %s %s %s", update_clock(GET_TIME), get_server_nickname(from_server), target, message));
				else
					put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_ACTION_OTHER_FSET), 
						"%s %s %s %s", update_clock(GET_TIME), get_server_nickname(from_server), target, message));
			}
			set_lastlog_msg_level(old);
			message_from(NULL, LOG_CRAP);
		}
		else
			userage(command ? command: "me", helparg);
	}
	else
		userage(command ? command: "me", helparg);
}

/*
 * eval_inputlist:  Cute little wrapper that calls parse_line() when we
 * get an input prompt ..
 */

void eval_inputlist(char *args, char *line)
{
	context;
	parse_line(NULL, args, line ? line : empty_string, 0, 0);
}

BUILT_IN_COMMAND(evalcmd)
{
	context;
	parse_line(NULL, args, subargs ? subargs : empty_string, 0, 0);
}

/*
 * inputcmd:  the INPUT command.   Takes a couple of arguements...
 * the first surrounded in double quotes, and the rest makes up
 * a normal ircII command.  The command is evalutated, with $*
 * being the line that you input.  Used add_wait_prompt() to prompt
 * the user...  -phone, jan 1993.
 */

BUILT_IN_COMMAND(inputcmd)
{
	char	*prompt;
	int	wait_type;
	
	context;
	if (!args || !*args)
		return;

	if (*args++ != '\"')
	{
		bitchsay("Need \" to begin prompt for INPUT");
		return;
	}

	prompt = args;
	if ((args = strchr(prompt, '"')) != NULL)
		*args++ = '\0';
	else
	{
		bitchsay("Missing \" in INPUT");
		return;
	}

	for (; *args == ' '; args++)
		;
	if (!my_stricmp(command, "INPUT"))
		wait_type = WAIT_PROMPT_LINE;
	else
		wait_type = WAIT_PROMPT_KEY;

	add_wait_prompt(prompt, eval_inputlist, args, wait_type);
}

BUILT_IN_COMMAND(xtypecmd)
{
	char	*arg;

	context;
	if (args && (*args == '-' || *args == '/'))
	{
		char saved = *args;
		args++;
		if ((arg = next_arg(args, &args)) != NULL)
		{
			if (!my_strnicmp(arg, "L", 1))
			{
				for (; *args; args++)
					input_add_character(*args, empty_string);
			}
			else
				bitchsay ("Unknown flag -%s to XTYPE", arg);
			return;
		}
		input_add_character(saved, empty_string);
	}
	else
		type(command, args, empty_string, helparg);
	return;
}

BUILT_IN_COMMAND(beepcmd)
{
	term_beep();
}

BUILT_IN_COMMAND(e_debug)
{
	int x;
	char buffer[BIG_BUFFER_SIZE + 1];

	*buffer = 0;
	for (x = 0; x < FD_SETSIZE; x++)
	{
		if (FD_ISSET(x, &readables))
		{
			strcat(buffer, space);
			strcat(buffer, ltoa(x));
		}
	}
	yell(buffer);
}
BUILT_IN_COMMAND(pretend_cmd)
{
	parse_server(args);
}

/*
 * This is a quick and dirty hack (emphasis on dirty) that i whipped up
 * just for the heck of it.  I feel really dirty about using the add_timer
 * call (bletch!) to fake a timeout for io().  The better answer would be
 * for io() to take an argument specifying the maximum threshold for a
 * timeout, but i didnt want to deal with that here.  So i just add a
 * dummy timer event that does nothing (wasting two function calls and
 * about 20 bytes of memory), and call io() until the whole thing blows
 * over.  Nice and painless.  You might want to try this instead of /sleep,
 * since this is (obviously) non-blocking.  This also calls time() for every
 * io event, so that might also start adding up.  Oh well, TIOLI.
 *
 * Without an argument, it waits for the user to press a key.  Any key.
 * and the key is accepted.  Thats probably not right, ill work on that.
 */
BUILT_IN_COMMAND(e_pause)
{
	char *sec;
	int seconds;
	time_t start;

	if (!(sec = next_arg(args, &args)))
	{
		cursor_to_input();
		get_a_char();
		input_backspace(0, NULL);
		return;
	}

	seconds = atoi(sec);
	time(&start);

	/* 
	 * I use comment here simply becuase its not going to mess
	 * with the arguments.
	 */
	add_timer("", seconds, 1, (int (*)(void *))comment, NULL, NULL);
	while (time(NULL) < start + seconds)
		io("e_pause");
}

#ifndef WANT_TCL

char tcl_versionstr[] = "";
BUILT_IN_COMMAND(tcl_command)
{
	bitchsay("This Client is not compiled with tcl support.");
}

BUILT_IN_COMMAND(tcl_load)
{
	tcl_command(NULL, NULL, NULL, NULL);
}

#else
BUILT_IN_COMMAND(tcl_load)
{
char *filename = NULL;
int result = 0;
	if ((filename = next_arg(args, &args)))
	{
		char *bla = NULL;
		if (get_string_var(LOAD_PATH_VAR))
			bla = path_search(filename, get_string_var(LOAD_PATH_VAR));
		if ((result = Tcl_EvalFile(interp, bla?bla:filename)) != TCL_OK)
			put_it("Tcl:  [%s]",interp->result);
		else if (*interp->result)
			put_it("Tcl:  [%s]", interp->result);
	}
	else
		userage("loadtcl", helparg);
}


#endif

BUILT_IN_COMMAND(help)
{
int cnt = 1;
IrcCommand *cmd = NULL;
int c, i;
char buffer[BIG_BUFFER_SIZE+1];
char *comm = NULL;

	if (args && *args)
	{
		comm = next_arg(args, &args);
		cmd = find_command(upper(comm), &cnt);
		if (!cmd)
		{
			bitchsay("No such command [%s]", comm);
			return;
		}
		if (cmd == irc_command)
		{
			cmd = NULL;
			cnt = 0;
		}
		if (cnt < 0)
		{
			userage(cmd->name, cmd->help ? cmd->help : "No Help available");
			return;
		}
	}
	if (!cmd)
	{
		cmd = irc_command; 
		cmd++; 
		cnt = NUMBER_OF_COMMANDS;
	} 
	else if (comm && *comm) 
		put_it("%s", convert_output_format("$G Found %K[%W$0%K]%n matches for %K[%W$1%K]", "%d %s", cnt, comm));
	*buffer = 0;
	c = 0;
	for (i = 0; i < cnt; i++)
	{
		strmcat(buffer, cmd[i].name, BIG_BUFFER_SIZE);
		strmcat(buffer, " ", 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;
		}
	}
	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 to get help on specific commands");
}


