/*
 * edit.c: This is really a mishmash of function and such that deal with IRCII
 * commands (both normal and keybinding commands) 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <stdio.h>
#ifdef ESIX
#include <lan/net_types.h>
#endif
#include <netinet/in.h>
#include "irc.h"
#include "term.h"
#include "server.h"
#include "edit.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 "translat.h"
#include "output.h"
#include "exec.h"
#include "notify.h"
#include "numbers.h"
#include "status.h"

/*
 * current_exec_timer - used to make sure we don't remove a timer
 * from within itself.
 */
static	int	current_exec_timer = -1;

static	void	show_timer();
static	int	create_timer_ref();

TimerList *PendingTimers = null(TimerList *);

/* used with input_move_cursor */
#define RIGHT 1
#define LEFT 0

/* 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(char *);

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

/* 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;

/* declared in parse.c */
extern	int	doing_privmsg;

/* Used to prevent global messaging */
extern	int	in_on_who;

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

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

char	lame_wait_nick[] = "1#LAME";

/* a few advance declarations */
static	void	sendlinecmd();
extern	void	ifcmd();
extern	void	do_send_text();
static	void	funny_stuff();
extern	void	parse_command();
static	void	cd();
extern	void	edit_char();
extern	void	foreach();
extern	void	whilecmd();
static	void	quit();
static	void	wall();
static	void	send_2comm();
static	void	send_comm();
static	void	send_topic();
static	void	send_kick();
static	void	send_channel_com();
static	void	clear();
static	void	quote();
static	void	privmsg();
static	void	flush();
extern	void	server();
static	void	away();
extern	void	query();
static	void	oper();
static	void	channel();
extern	void	execcmd();
extern	void	ignore();
static	void	who();
static	void	whois();
static	void	ison();
static	void	userhost();
static	void	info();
static	void	nick();
static	void	comment();
extern	void	bindcmd();
extern	void	parsekeycmd();
static	void	sleepcmd();
extern	void	history();
static	void	version();
extern	void	on();
static	void	ctcp();
static	void	dcc();
static	void	deop();
extern	void	echo();
static	void	save_settings();
static	void	redirect();
extern	void	type();
extern	void	notify();
extern	void	waitcmd();
extern	void	parse_line();
static	void	describe();
static	void	me();
static	void	mload();
static	void	mlist();
static	void	evalcmd();
extern	void	direct_expr();
static	void	hook();
static	void	timercmd();
static	void	inputcmd();
static	void	pingcmd();
static	void	xtypecmd();

#ifdef ALLOC_DEBUG
extern	void	alloc_cmd();
#endif
/* IrcCommand: structure for each command in the command table */
typedef	struct
{
	char	*name;			/* what the user types */
	char	*server_func;		/* what gets sent to the server
					 * (if anything) */
	void	(*func) ();		/* function that is the command */
	unsigned	flags;
}	IrcCommand;

#define NONOVICEABBREV 0x0001
#define	NOINTERACTIVE  0x0002
#define	NOSIMPLESCRIPT 0x0004
#define	NOCOMPLEXSCRIPT 0x0008

/* number of entries in irc_command array */
#ifdef ALLOC_DEBUG
# define	NUMBER_OF_COMMANDS 104
#else
# define	NUMBER_OF_COMMANDS 103
#endif

/*
 * 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
 * make "/ blah blah blah" always get sent to a channel, bypassing queries,
 * etc.  Neato.  This list MUST be sorted and the NUMBER_OF_COMMANDS define
 * must represent the number of commands in this list 
 */
static	IrcCommand irc_command[NUMBER_OF_COMMANDS * 2] =
{
	{ "",		empty_string,	do_send_text,		NOSIMPLESCRIPT|
		NOCOMPLEXSCRIPT },
/*
 * I really want to remove #, but it will break a lot of scripts.  - mycroft
 *
 * I agree - it should be converted to a special character in parse_line.
 *								- Troy
 */
	{ "#",		NULL,		comment, 		0 },
	{ ":",		NULL,		comment, 		0 },
	{ "ADMIN",	"ADMIN",	send_comm, 		0 },
	{ "ALIAS",	"0",		alias,			0 },
#ifdef ALLOC_DEBUG
	{ "ALLOC",	NULL,		alloc_cmd,		0 },
#endif
	{ "ASSIGN",	"1",		alias,			0 },
	{ "AWAY",	"AWAY",		away,			0 },
	{ "BIND",	NULL,		bindcmd,		0 },
	{ "BYE",	"QUIT",		quit,			NONOVICEABBREV},
	{ "CD",		NULL,		cd,			0 },
	{ "CHANNEL",	"JOIN",		channel,		0 },
	{ "CLEAR",	NULL,		clear,			0 },
	{ "COMMENT",	NULL,		comment,		0 },
	{ "CONNECT",	"CONNECT",	send_comm,		0 },
	{ "CTCC",	NULL,		dcc,			0 },
	{ "CTCP",	NULL,		ctcp,			0 },
	{ "DATE",	"TIME",		send_comm,		0 },
	{ "DCC",	NULL,		dcc,			0 },
	{ "DEOP",	NULL,		deop,			0 },
	{ "DESCRIBE",	NULL,		describe,		0 },
	{ "DIE",	"DIE",		send_comm,		0 },
	{ "DIGRAPH",	NULL,		digraph,		0 },
	{ "ECHO",	NULL,		echo,			0 },
	{ "ENCRYPT",	NULL,		encrypt_cmd,		0 },
	{ "EVAL",	NULL,		evalcmd,		0 },
	{ "EXEC",	NULL,		execcmd,		0 },
	{ "EXIT",	"QUIT",		quit,			NONOVICEABBREV},
	{ "FLUSH",	NULL,		flush,			0 },
	{ "FOREACH",	NULL,		foreach,		0 },
	{ "HASH",	"HASH",		send_comm,		0 },
	{ "HELP",	NULL,		help,			0 },
	{ "HISTORY",	NULL,		history,		0 },
	{ "HOOK",	NULL,		hook,			0 },
	{ "HOST",	"USERHOST",	userhost,		0 },
	{ "IF",		NULL,		ifcmd,			0 },
	{ "IGNORE",	NULL,		ignore,			0 },
	{ "INFO",	"INFO",		info,			0 },
	{ "INPUT",	NULL,		inputcmd,		0 },
	{ "INVITE",	"INVITE",	send_comm,		0 },
	{ "ISON",	"ISON",		ison,			0 },
	{ "JOIN",	"JOIN",		channel,		0 },
	{ "KICK",	"KICK",		send_kick,		0 },
	{ "KILL",	"KILL",		send_2comm,		0 },
	{ "LASTLOG",	NULL,		lastlog,		0 },
	{ "LEAVE",	"PART",		send_channel_com,	0 },
	{ "LINKS",	"LINKS",	send_comm,		NONOVICEABBREV},
	{ "LIST",	"LIST",		funny_stuff,		0 },
	{ "LOAD",	"LOAD",		load,			0 },
	{ "LUSERS",	"LUSERS",	send_comm,		0 },
	{ "ME",		NULL,		me,			0 },
	{ "MLIST",	NULL,		mlist,			0 },
	{ "MLOAD",	NULL,		mload,			0 },
	{ "MODE",	"MODE",		send_channel_com,	0 },
	{ "MOTD",	"MOTD",		send_comm,		0 },
	{ "MSG",	"PRIVMSG",	privmsg,		0 },
	{ "NAMES",	"NAMES",	funny_stuff,		0 },
	{ "NICK",	"NICK",		nick,			0 },
	{ "NOTE",	"NOTE",		send_comm,		0 },
	{ "NOTICE",	"NOTICE",	privmsg,		0 },
	{ "NOTIFY",	NULL,		notify,			0 },
	{ "ON",		NULL,		on,			0 },
	{ "OPER",	"OPER",		oper,			0 },
	{ "PARSEKEY",	NULL,		parsekeycmd,		0 },
	{ "PART",	"PART",		send_channel_com,	0 },
	{ "PING",	NULL, 		pingcmd,		0 },
	{ "QUERY",	NULL,		query,			0 },
	{ "QUIT",	"QUIT",		quit,			NONOVICEABBREV},
	{ "QUOTE",	NULL,		quote,			0 },
	{ "REDIRECT",	NULL,		redirect,		0 },
	{ "REHASH",	"REHASH",	send_comm,		0 },
	{ "REQUEST",	NULL,		ctcp,			0 },
	{ "RESTART",	"RESTART",	send_comm,		0 },
	{ "SAVE",	NULL,		save_settings,		0 },
	{ "SAY",	empty_string,	do_send_text,		0 },
	{ "SEND",	NULL,		do_send_text,		0 },
	{ "SENDLINE",	empty_string,	sendlinecmd,		0 },
	{ "SERVER",	NULL,		server,			0 },
	{ "SET",	NULL,		set_variable,		0 },
	{ "SIGNOFF",	"QUIT",		quit,			NONOVICEABBREV},
	{ "SLEEP",	NULL,		sleepcmd,		0 },
	{ "SQUIT",	"SQUIT",	send_2comm,		0 },
	{ "STATS",	"STATS",	send_comm,		0 },
	{ "SUMMON",	"SUMMON",	send_comm,		0 },
	{ "TIME",	"TIME",		send_comm,		0 },
	{ "TIMER",	"TIMER",	timercmd,		0 },
	{ "TOPIC",	"TOPIC",	send_topic,		0 },
	{ "TRACE",	"TRACE",	send_comm,		0 },
	{ "TYPE",	NULL,		type,			0 },
	{ "USERHOST",	NULL,		userhost,		0 },
	{ "USERS",	"USERS",	send_comm,		0 },
	{ "VERSION",	"VERSION",	version,		0 },
	{ "VOICE",	"VOICE",	privmsg,		0 },
	{ "WAIT",	NULL,		waitcmd,		0 },
	{ "WALL",	"WALL",		wall,			0 },
	{ "WALLOPS",	"WALLOPS",	wall,			0 },
	{ "WHICH",	"WHICH",	load,			0 },
	{ "WHILE",	NULL,		whilecmd,		0 },
	{ "WHO",	"WHO",		who,			0 },
	{ "WHOIS",	"WHOIS",	whois,			0 },
	{ "WHOWAS",	"WHOWAS",	whois,			0 },
	{ "WINDOW",	NULL,		window,			0 },
	{ "XECHO",	"XECHO",	echo,			0 },
	{ "XTRA",	"XTRA",		privmsg,		0 },
	{ "XTYPE",	NULL,		xtypecmd,		0 },
	{ null(char *),	null(char *),	NULL,			0 }
};

/*
 * 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.
 */
static	IrcCommand *find_command(com, cnt)
char	*com;
int	*cnt;
{
	int	len;

	if (com && (len = strlen(com)))
	{
		int	min,
		max,
		pos,
		old_pos = -1,
		c;

		min = 1;
		max = NUMBER_OF_COMMANDS + 1;
		while (1)
		{
			pos = (max + min) / 2;
			if (pos == old_pos)
			{
				*cnt = 0;
				return (null(IrcCommand *));
			}
			old_pos = pos;
			c = strncmp(com, irc_command[pos].name, len);
			if (c == 0)
				break;
			else if (c > 0)
				min = pos;
			else
				max = pos;
		}
		*cnt = 0;
		(*cnt)++;
		min = pos - 1;
		while ((min > 0) && (strncmp(com, irc_command[min].name,
				len) == 0))
		{
			(*cnt)++;
			min--;
		}
		min++;
		max = pos + 1;
		while ((max < NUMBER_OF_COMMANDS + 1) && (strncmp(com,
				irc_command[max].name, len) == 0))
		{
			(*cnt)++;
			max++;
		}
		if (*cnt)
		{
			if (strlen(irc_command[min].name) == len)
				*cnt *= -1;
			else if (*cnt == 1 && 
					irc_command[min].flags&NONOVICEABBREV &&
					get_int_var(NOVICE_VAR))
			{
				say("As a novice you may not abbreviate the \
%s command",
						irc_command[min].name);
				*cnt=0;
				return (null(IrcCommand *));
			}
			return (&(irc_command[min]));
		}
		else
			return (null(IrcCommand *));
	}
	else
	{
		*cnt = -1;
		return (irc_command);
	}
}

/*ARGSUSED*/
static	void	ctcp(command, args)
char	*command,
	*args;
{
	char	*to,
		*tag;
	int	type;

	if (to = next_arg(args, &args))
	{
		if (!strcmp(to, "*"))
			if ((to=get_channel_by_refnum(0)) == null(char *))
				to = "0";
		if (tag = next_arg(args, &args))
			upper(tag);
		else
			tag = "VERSION";
		if ((type = in_ctcp()) == -1)
			echo(null(char *), "*** You may not use the CTCP \
command in an ON CTCP_REPLY!");
		else
		{
			if (args && *args)
				send_ctcp(ctcp_type[type], to, tag, "%s", args);
			else
				send_ctcp(ctcp_type[type], to, tag,
					null(char *));
		}
	}
	else
		say("Request from whom?");
}

/*ARGSUSED*/
static	void	hook(command, args)
char	*command,
	*args;
{
	if (*args)
		do_hook(HOOK_LIST, "%s", args);
	else
		say("Must supply an argument to HOOK");
}

/*ARGSUSED*/
static	void	dcc(command, args)
char	*command,
	*args;
{
	if (*args)
		process_dcc(args);
	else
		dcc_list();
}

/*ARGSUSED*/
static	void	deop(command, args)
char	*command,
	*args;
{
	send_to_server("MODE %s -o", get_server_nickname(from_server));
}

static	void	funny_stuff(command, args)
char	*command,
	*args;
{
	char	*arg,
		*stuff;
	int	min = 0,
		max = 0,
		flags = 0,
		len;

	stuff = empty_string;
	while (arg = next_arg(args, &args))
	{
		len = strlen(arg);
		if (strnicmp(arg, "-MAX", len) == 0)
		{
			if (arg = next_arg(args, &args))
				max = atoi(arg);
		}
		else if (strnicmp(arg, "-MIN", len) == 0)
		{
			if (arg = next_arg(args, &args))
				min = atoi(arg);
		}
		else if (strnicmp(arg, "-ALL", len) == 0)
		{
			flags &= ~(FUNNY_PUBLIC | FUNNY_PRIVATE);
		}
		else if (strnicmp(arg, "-PUBLIC", len) == 0)
		{
			flags |= FUNNY_PUBLIC;
			flags &= ~FUNNY_PRIVATE;
		}
		else if (strnicmp(arg, "-PRIVATE", len) == 0)
		{
			flags |= FUNNY_PRIVATE;
			flags &= ~FUNNY_PUBLIC;
		}
		else if (strnicmp(arg, "-TOPIC", len) == 0)
			flags |= FUNNY_TOPIC;
		else if (strnicmp(arg, "-WIDE", len) == 0)
			flags |= FUNNY_WIDE;
		else if (strnicmp(arg, "-USERS", len) == 0)
			flags |= FUNNY_USERS;
		else if (strnicmp(arg, "-NAME", len) == 0)
			flags |= FUNNY_NAME;
		else
			stuff = arg;
	}
	set_funny_flags(min, max, flags);
	if (strcmp(stuff, "*") == 0)
		if ((stuff = get_channel_by_refnum(0)) == null(char *))
			stuff = empty_string;
	if (index(stuff, '*'))
	{
		funny_match(stuff);
		send_to_server("%s %s", command, empty_string);
	}
	else{
		funny_match(null(char *));
		send_to_server("%s %s", command, stuff);
	}
}

/*ARGSUSED*/
void	waitcmd(command, args)
char	*command,
	*args;
{
	int	index;
	char	*flag;
	char	*procindex;
	int	cmd = 0,
		len;

	while (args && *args == '-')
	{
		flag = next_arg(args, &args);
		len = strlen(++flag);
		if (!strnicmp("CMD", flag, len))
		{
			cmd = 1;
			break;
		}
		else
			yell("Unknown argument to WAIT: %s", flag);
	}
	if ((procindex = next_arg(args, &args)) && *procindex == '%' &&
			(index = get_process_index(&procindex)) != -1)
	{
		if (is_process_running(index))
		{
			if (cmd)
			{
				add_process_wait(index, args?args:empty_string);
				return;
			}
			else
				set_wait_process(index);
		}
		else
		{
			say("Not a valid process!");
			return;
		}
	}
	else if (cmd)
	{
		WaitCmd	*new;

		sprintf(buffer, "%s %s", procindex, args);
		new = (WaitCmd *) new_malloc(sizeof(WaitCmd));
		new->stuff = NULL;
		malloc_strcpy(&new->stuff, buffer);
		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);
		return;
	}
	else
		send_to_server("%s", lame_wait_nick);
	if (waiting)
		yell("WAIT has been called recursively.");

	waiting = 1;
	irc_io(null(char *), NULL, 0, 1);
	waiting = 0;
}

int	check_wait_command(nick)
char 	*nick;
{
	if (waiting && !strcmp(nick, lame_wait_nick))
	{
		irc_io_loop = 0;
		return 1;
	}
	if (start_wait_list && !strcmp(nick, wait_nick))
	{
		if (start_wait_list->stuff)
		{
			parse_command(start_wait_list->stuff, 0, empty_string);
			new_free(&start_wait_list->stuff);
		}
		start_wait_list = start_wait_list->next;
		return 1;
	}
	return 0;
}

/*ARGSUSED*/
static	void	redirect(command, args)
char	*command,
	*args;
{
	char	*who;

	if (who = next_arg(args, &args))
	{
		if (!strcmp(who, "*"))
			if ((who = get_channel_by_refnum(0)) == null(char *))
			{
				say("Must be on a channel to redirect to '*'");
				return;
			}
		if (!stricmp(who, get_server_nickname(from_server)))
		{
			say("You may not redirect output to yourself");
			return;
		}
		window_redirect(who, from_server);
		parse_line(null(char *), args, null(char *), 0, 0);
		send_to_server("%s", current_screen->redirect_token,
			current_screen->screennum);
	}
	else
		say("Usage: REDIRECT <nick|channel|%process> <cmd>");
}

/*ARGSUSED*/
static	void	sleepcmd(command, args)
char	*command,
	*args;
{
	char	*arg;

	if (arg = next_arg(args, &args))
		sleep(atoi(arg));
	else
		say("SLEEP: you must specify the amount of time to sleep \
(in seconds)");
}

/*
 * echo: simply displays the args to the screen, or, if it's XECHO,
 * processes the flags first, then displays the text on
 * the screen
 */
void	echo(command, args)
char	*command,
	*args;
{
	unsigned int	display;
	int	lastlog_level = 0;
	int	from_level = 0;
	char	*flag_arg;
	int	temp;
	Window *old_to_window;

	old_to_window = to_window;
	if (command && *command == 'X')
	{
		while (args && *args == '-')
		{
			flag_arg = next_arg(args, &args);
			switch(toupper(flag_arg[1]))
			{
			case 'L':
				if (!(flag_arg = next_arg(args, &args)))
					break;
				if (temp = parse_lastlog_level(flag_arg))
				{
					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;
			}
			if (!args)
				args = empty_string;
		}
	}
	display = window_display;
	window_display = 1;
	put_it("%s", args);
	window_display = display;
	if (lastlog_level)
	{
		set_lastlog_msg_level(lastlog_level);
		message_from_level(from_level);
	}
	to_window = old_to_window;
}

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

/* oper: the OPER command.  */
/*ARGSUSED*/
static	void	oper(command, args)
char	*command,
	*args;
{
	char	*password;
	char	*nick;

	oper_command = 1;
	if ((nick = next_arg(args, &args)) == null(char *))
		nick = nickname;
	if ((password = next_arg(args, &args)) == null(char *))
	{
		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 */
static	void	really_save(ircrc_file, line)
char	*ircrc_file;
char	*line;
{
	FILE *fp;

	if (*line != 'y' && *line != 'Y')
		return;
	if (fp = fopen(ircrc_file, "w"))
	{
		fprintf(fp, "SET DISPLAY OFF\n");
		save_bindings(fp);
		save_hooks(fp);
		save_notify(fp);
		save_variables(fp);
		save_aliases(fp);
		save_digraphs(fp);
		fprintf(fp, "SET DISPLAY ON\n");
		fclose(fp);
		say("IRCII settings saved to %s", ircrc_file);
	}
	else
		say("Error opening %s: %s", ircrc_file, sys_errlist[errno]);
}

/* save_settings: saves the current state of IRCII to a file */
/*ARGSUSED*/
static	void	save_settings(command, args)
char	*command,
	*args;
{
	char	*file;

	if (file = next_arg(args, &args))
	{
#ifdef DAEMON_UID
		if (getuid() == DAEMON_UID)
		{
			say("You may only use the default value");
			return;
		}
#endif /* DAEMON_UID */
		if ((ircrc_file = expand_twiddle(file)) == null(char *))
		{
			say("Unknown user");
			return;
		}
	}
	if (dumb)
		really_save(ircrc_file, "y"); /* REAL dumb!  -lynx */
	else
	{
		sprintf(buffer, "Really write %s? ", ircrc_file);
		add_wait_prompt(buffer, really_save, ircrc_file,
				WAIT_PROMPT_LINE);
	}
}

/*
 * channel: does the channel command.  I just added displaying your current
 * channel if none is given 
 */
static	void	channel(command, args)
char	*command,
	*args;
{
	char	*chan;
	char	*old;
	int	len;

	if (get_server_version(from_server) > Server2_5)
		command = "JOIN";
	else
		command = "CHANNEL";
	message_from(null(char *), LOG_CURRENT);
	if (chan = next_arg(args, &args))
	{
		if (get_int_var(NOVICE_VAR))
		{
			if ((old = get_channel_by_refnum(0)) && strcmp(old,
					"0"))
				send_to_server("PART %s", old);
		}
		len = strlen(chan);
		if (strnicmp(chan, "-invite", len) == 0)
		{
			if (invite_channel)
				send_to_server("%s %s",command, invite_channel);
			else
				say("You have not been invited to a channel!");
		}
#if 0
		else if (strnicmp(chan, "-nick", len) == 0)
		{
			if (chan = next_arg(args, &args))
			{
				if (index(chan, '*'))
					say("Wildcards not allowed here!");
				else
					add_to_whois_queue(chan, whois_join,
						null(char *));
			}
			else
				say("You must specify a nickname!");
		}
#endif
		else
		{
			if (is_on_channel(chan,
					get_server_nickname(from_server)))
			{
				is_current_channel(chan, 1);
				say("You are now talking to channel %s",
					set_channel_by_refnum(0, chan));
				update_all_windows();
			}
			else
			{
				send_to_server("%s %s %s", command, chan, args);
				malloc_strcpy(&curr_scr_win->waiting_channel,
					chan);
			}
		}
	}
	else
		list_channels();
	message_from(null(char *), LOG_CRAP);
}

/* comment: does the /COMMENT command, useful in .ircrc */
/*ARGSUSED*/
static	void	comment(command, args)
char	*command,
	*args;
{
	/* nothing to do... */
}

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

	if ((nick = next_arg(args, &args)) == null(char *))
	{
		say("Your nickname is %s",
			get_server_nickname(get_window_server(0)));
		return;
	}
	if (check_nickname(nick))
	{
		if (strcmp(nick, get_server_nickname(from_server)))
		{
			send_to_server("NICK %s", nick);
			if (get_server_version(from_server) == Server2_5)
				add_to_whois_queue(nick, whois_nickname,
					null(char *));
		}
	}
	else
		say("Bad nickname");
}

/* version: does the /VERSION command with some IRCII version stuff */
static	void	version(command, args)
char	*command,
	*args;
{
	char	*host;

	if (host = next_arg(args, &args))
		send_to_server("%s %s", command, host);
	else
	{ 
		say("Client: ircII %s", irc_version);
		send_to_server("%s", command);
	}
}

/*
 * info: does the /INFO command.  I just added some credits
 * I updated most of the text -phone, feb 1993.
 */
static	void	info(command, args)
char	*command,
	*args;
{
	say("ircII: Originally written by Michael Sandrof");
	say("       Versions 2.1 to 2.2pre7 by Troy Rollo");
	say("       Development continued by Matthew Green");
	say("       E-Mail: phone@coombs.anu.edu.au  IRC: phone");
	say("       Copyright 1990");
	say("       Do a /HELP IRCII COPYRIGHT for the full copyright");
	say("       ircII includes software developed by the University");
	say("       of California, Berkeley and its contributors");
	say("");
	say("       Major 2.2 debugging provided by");
	say("");
	say("       \tMycroft   Charles Hannum         mycroft@gnu.ai.mit.edu");
	say("       \tDaemon    Ian Frechette          frechett@spot.colorado.edu");
	say("       \tpoxaV     Christopher Williams   cgw@unt.edu");
	say("       \tjlemon    Jonathan Lemon         jlemon@amdahl.com");
	say("       \tbpk       Brian Koehmstedt       bpk@gnu.ai.mit.edu");
	say("");
	say("ircII: Contributors");
	say("");
	say("       \tMichael Sandrof       Mark T. Dameu");
	say("       \tStellan Klebom        Carl v. Loesch");
	say("       \tTroy Rollo            Martin  Friedrich");
	say("       \tMichael Weber         Bill Wisner");
	say("       \tRiccardo Facchetti    Stephen van den Berg");
	say("       \tVolker Paulsen        Kare Pettersson");
	say("       \tIan Frechette         Charles Hannum");
	say("       \tMatthew Green         Christopher Williams");
	say("       \tJonathan Lemon        Brian Koehmstedt");
	say("       \tNicolas Pioch         Brian Fehdrau");
	send_to_server("%s %s", command, args);
}

extern	void	ison_ison(askedfor, nicklist)
char	*askedfor;
char	*nicklist;
{
	if (do_hook(current_numeric, "%s", nicklist))
		put_it("%s Currently online: %s", numeric_banner(), nicklist);
}

static	void	ison(command, args)
char	*command;
char	*args;
{
	if (!args[strspn(args, " ")])
		args = get_server_nickname(from_server);
	add_ison_to_whois(args, ison_ison);

}

/*
 * userhost: Does the USERHOST command.  Need to split up the queries,
 * since the server will only reply to 5 at a time.
 */
static	void	userhost(command, args)
char	*command;
char	*args;
{
	int	n = 0,
		total = 0;
	char	*nick;

	while (nick = next_arg(args, &args))
	{
		int	len;

		++total;
		len = strlen(nick);
	/*
	 * Here, if total = 2, then we have only had one nick, and
	 * this second one could be the -cmd arguement.  *ONLY* allow
	 * this form USERHOST NICK -CMD COMMAND.  Don't allow more
	 * than one nick for this case... -phone, jan 1993.
	 */
		if (total == 2 && !strnicmp(nick, "-CMD", len))
		{
			add_to_whois_queue(buffer, userhost_cmd_returned, "%s",
				args);
			n = 0;
			break;
		}
		else
		{
			if (n++)
				strmcat(buffer, " ", BIG_BUFFER_SIZE);
			else
				*buffer = null(char);
			strmcat(buffer, nick, BIG_BUFFER_SIZE);
			if (n >= 5)
			{
				add_to_whois_queue(buffer, USERHOST_USERHOST,
					null(char *));
				n = 0;
			}
		}
	}
	if (n)
		add_to_whois_queue(buffer, USERHOST_USERHOST, null(char *));
	else if (!total)
		/* Default to yourself.  */
		add_to_whois_queue(get_server_nickname(from_server),
				USERHOST_USERHOST, null(char *));
}

/*
 * whois: the WHOIS and WHOWAS commands.  This translates the 
 * to the whois handlers in whois.c 
 */
static	void	whois(command, args)
char	*command,
	*args;
{
	char	*nick;

	if (nick = next_arg(args, &args))
	{
		send_to_server("%s %s %s", command, nick, args);
#if 0
		/* This part is done by ircd anyway -lynx */
		while (nick)
		{
			char	*ptr;

			if (ptr = index(nick, ','))
				*(ptr++) = null(char);
			if (*nick)
			{
				if (args) /*support /whois nick server  -lynx */
					send_to_server("%s %s %s", command,
							args, nick);
				else
					send_to_server("%s %s", command, nick);
			}
			nick = ptr;
		}
#endif
	}
	else /* Default to yourself  -lynx */
		send_to_server("%s %s", command,
			get_server_nickname(from_server));
}

/*
 * 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 
 */
static	void	who(command, args)
char	*command,
	*args;
{
	char	*arg,
		*channel = null(char *);
	int	no_args = 1,
		len;

	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))
	{
		no_args = 0;
		if ((*arg == '-') && (!isdigit(*(arg + 1))))
		{
			arg++;
			if ((len = strlen(arg)) == 0)
			{
				say("Unknown or missing flag");
				return;
			}
			if (strnicmp(arg, "operators", len) == 0)
				who_mask |= WHO_OPS;
			else if (strnicmp(arg, "lusers", len) == 0)
				who_mask |= WHO_LUSERS;
			else if (strnicmp(arg, "chops", len) == 0)
				who_mask |= WHO_CHOPS;
			else if (strnicmp(arg, "hosts", len) == 0)
			{
				if (arg = next_arg(args, &args))
				{
					who_mask |= WHO_HOST;
					malloc_strcpy(&who_host, arg);
					channel = who_host;
				}
				else
				{
					say("WHO -HOSTS: missing arguement");
					return;
				}
			}
			else if (strnicmp(arg, "here", len) ==0)
				who_mask |= WHO_HERE;
			else if (strnicmp(arg, "away", len) ==0)
				who_mask |= WHO_AWAY;
			else if (strnicmp(arg, "servers", len) == 0)
			{
				if (arg = next_arg(args, &args))
				{
					who_mask |= WHO_SERVER;
					malloc_strcpy(&who_server, arg);
					channel = who_server;
				}
				else
				{
					say("WHO -SERVERS: missing arguement");
					return;
				}
			}
			else if (strnicmp(arg, "name", len) == 0)
			{
				if (arg = next_arg(args, &args))
				{
					who_mask |= WHO_NAME;
					malloc_strcpy(&who_name, arg);
					channel = who_name;
				}
				else
				{
					say("WHO -NAME: missing arguement");
					return;
				}
			}
			else if (strnicmp(arg, "realname", len) == 0)
			{
				if (arg = next_arg(args, &args))
				{
					who_mask |= WHO_REAL;
					malloc_strcpy(&who_real, arg);
					channel = who_real;
				}
				else
				{
					say("WHO -REALNAME: missing arguement");
					return;
				}
			}
			else if (strnicmp(arg, "nick", len) == 0)
			{
				if (arg = next_arg(args, &args))
				{
					who_mask |= WHO_NICK;
					malloc_strcpy(&who_nick, arg);
					channel = who_nick;
				}
				else
				{
					say("WHO -NICK: missing arguement");
					return;
				}
				/* WHO -FILE by Martin 'Efchen' Friedrich */
			}
			else if (strnicmp(arg, "file", len) == 0)
			{
				who_mask |= WHO_FILE;
				if (arg = next_arg(args, &args))
				{
					malloc_strcpy(&who_file, arg);
				}
				else
				{
					say("WHO -FILE: missing arguement");
					return;
				}
			}
			else
			{
				say("Unknown or missing flag");
				return;
			}
		}
		else if (strcmp(arg, "*") == 0)
		{
			channel = get_channel_by_refnum(0);
			if (!channel || *channel == '0')

			{
				say("I wouldn't do that if I were you");
				return;
			}
		}
		else
			channel = arg;
	}
	if (no_args)
		say("No argument specified");
	else
	{
		if (!channel && (who_mask&WHO_OPS))
			channel = "*.*";
		send_to_server("%s %s %c", command, 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.
 */
/*ARGSUSED*/
void	query(command, args)
char	*command,
	*args;
{
	char	*nick,
		*rest;

	message_from(null(char *), LOG_CURRENT);
	if (nick = next_arg(args, &rest))
	{
		if (strcmp(nick, ".") == 0)
		{
			if ((nick = sent_nick) == null(char *))
			{
				say("You have not messaged anyone yet");
				return;
			}
		}
		else if (strcmp(nick, ",") == 0)
		{
			if ((nick = recv_nick) == null(char *))
			{
				say("You have not recieved a message from \
						anyone yet");
				return;
			}
		}
		else if (strcmp(nick, "*") == 0)
			if ((nick=get_channel_by_refnum(0)) == null(char *))
			{
				say("You are not on a channel");
				return;
			}

		if (*nick == '%')
		{
			if (is_process(nick) == 0)
			{
				say("Invalid processes specification");
				message_from(null(char *), LOG_CRAP);
				return;
			}
		}
		say("Starting conversation with %s", nick);
		set_query_nick(nick);
	}
	else
	{
		if (query_nick())
		{
			say("Ending conversation with %s", query_nick());
			set_query_nick(null(char *));
		}
		else
			say("You aren't querying anyone!");
	}
	update_input(UPDATE_ALL);
	message_from(null(char *), LOG_CRAP);
}

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

	if (*args)
	{
		if (*args == '-')
		{
			arg = index(args, ' ');
			if (arg)
				*arg++ = '\0';
			else
				arg = "";
			len = strlen(args);
			if (!strnicmp(args, "-ALL", len))
			{
				flag = AWAY_ALL;
				args = arg;
			}
			else if (strnicmp(args, "-ONE", len) == 0)
			{
				flag = AWAY_ONE;
				args = arg;
			}
			else
			{
				say("%s: %s unknown flag", command, args);
				return;
			}
		}
	}
	if (flag == AWAY_ALL)
		if (*args)
		{
			away_set = 1;
			MarkAllAway(command, args);
		}
		else
		{
			away_set = 0;
			for(i = 0; (i < number_of_servers); i++)
				if (server_list[i].whois_stuff.away)
					new_free(&(server_list[i].away));
		}
	else
	{
		send_to_server("%s :%s", command, args);
		if (*args)
		{
			away_set = 1;
			malloc_strcpy(&(server_list[
				curr_scr_win->server].away), args);
		}
		else
		{
			new_free(&(server_list[
				curr_scr_win->server].away));
			away_set = 0;
			for(i = 0; (i < number_of_servers) && !away_set ; i++)
				if (server_list[i].read != -1 &&
						server_list[i].away)
					away_set = 1;
		}
	}
	update_all_status();
}

/* quit: The /QUIT, /EXIT, etc command */
/*ARGSUSED*/
static	void	quit(command, args)
char	*command,
	*args;
{
	int	max;
	int	i;
	char	*Reason;

	Reason = ((*args) ? args : "Leaving");
	max = server_list_size();
	for (i = 0; i < max; i++)
		if (is_server_connected(i))
		{
			from_server = i;
			send_to_server("QUIT :%s", Reason);
		}
	irc_exit();
}

/* flush: flushes all pending stuff coming from the server */
/*ARGSUSED*/
static	void	flush(command, args)
char	*command,
	*args;
{
	if (get_int_var(HOLD_MODE_VAR))
	{
		while (curr_scr_win->held_lines)
			remove_from_hold_list(curr_scr_win);
		hold_mode(null(Window *), OFF, 1);
	}
	say("Standby, Flushing server output...");
	flush_server();
	say("Done");
}

/* wall: used for WALL and WALLOPS */
static	void	wall(command, args)
char	*command,
	*args;
{
	if (strcmp(command, "WALL") == 0)
	{	/* I hate this */
		message_from(null(char *), LOG_WALL);
		if (!get_server_operator(from_server))
			put_it("## %s", args);
	}
	else
	{
		message_from(null(char *), LOG_WALLOP);
		if (!get_server_flag(from_server, USER_MODE_W))
			put_it("!! %s", args);
	}
	if (!in_on_who)
		send_to_server("%s :%s", command, args);
	message_from(null(char *), LOG_CRAP);
}

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

	if (nick = next_arg(args, &args))
	{
			/*
			 * I removed this because it just isn't valid
			 * in a world for multi-channels where you can
			 * be on a channel with more than one person
			 * phone/Daemon, feb 1993.
			 */
#if 0
		if (strnicmp(nick, "-channel", strlen(nick)) == 0)
		{
			if (nick = next_arg(args, &args))
			{
				if (index(nick, '*'))
					say("Wildcards not allowed here!");
				else
					add_to_whois_queue(nick,
						whois_privmsg, "%s %%s %s",
						command, args);
			}
			else
				say("You must specify a nickname or channel!");
		}
		else
#endif
		if (strcmp(nick, ".") == 0)
		{
			if ((nick = sent_nick) == null(char *))
			{
				say("You have not sent a \
message to anyone yet");
				return;
			}
		}

		else if (strcmp(nick, ",") == 0)
		{
			if ((nick = recv_nick) == null(char *))
			{
				say("You have not received a message \
from anyone yet");
				return;
			}
		}
		else if (!strcmp(nick, "*"))
			if ((nick = get_channel_by_refnum(0)) ==
					null(char *))
				nick = "0";
		send_text(nick, args, command);
	}
	else
		say("You must specify a nickname or channel!");
}

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

/* clear: the CLEAR command.  Figure it out */
/*ARGSUSED*/
static	void	clear(command, args)
char	*command,
	*args;
{
	char	*arg;

	if (arg = next_arg(args, &args))
	{
	/* -ALL and ALL here becuase the help files used to be wrong */
		if (!strnicmp(arg, "ALL", strlen(arg))
				|| !strnicmp(arg, "-ALL", strlen(arg)))
			clear_all_windows();
		else
			say("Unknown flag: %s", arg);
	}
	else
		clear_window_by_refnum(0);
	update_input(UPDATE_JUST_CURSOR);
}

/*
 * 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 
 */
static	void	send_comm(command, args)
char	*command,
	*args;
{
	if (args && *args)
		send_to_server("%s %s", command, args);
	else
		send_to_server("%s", command);
}


static	void	send_topic(command, args)
char	*command,
	*args;
{
	char	*arg;
	char	*arg2;

	if (arg = next_arg(args, &args))

	{
		if (is_channel(arg))
		{
			if (arg2 = next_arg(args, &args))
				send_to_server("%s %s :%s %s", command, arg,
						arg2, args);
			else
				send_to_server("%s %s", command, arg);
		}
		else
			send_to_server("%s :%s %s", command, arg, args);
	}
	else
		send_to_server("%s", command);
}

static	void	send_2comm(command, args)
char	*command,
	*args;
{
	char	*reason;

	if (reason = index(args, ' '))
		*reason++ = '\0';
	else
		reason = empty_string;

	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.
 */

static	void	send_kick(command, args)
char	*command,
	*args;
{
	char	*kickee,
		*comment;

	if (kickee = index(args, ' '))
		*(kickee++) = null(char);
	else 
		kickee = index(args, null(char));

	if (comment = index(kickee, ' '))
		*(comment++) = null(char);
	else 
		comment = index(kickee, null(char));

	if (strcmp(args, "*") == 0)
		send_to_server("%s %s %s :%s", command,
				get_channel_by_refnum(0), kickee, comment);
	else
		send_to_server("%s %s %s :%s", command, args, kickee, comment);
}

/*
 * 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).
 */
static	void	send_channel_com(command, args)
char	*command,
	*args;
{
	char	*ptr;

	if (ptr = index(args, ' '))
		*(ptr++) = null(char);
	else
		ptr = index(args, null(char));

	if (strcmp(args, "*") == 0)
		send_to_server("%s %s %s", command, get_channel_by_refnum(0),
			ptr);
	else
		send_to_server("%s %s %s", command, args, ptr);
}

/*
 * send_text: Sends the line of text to whomever the user is currently
 * talking.  If they are quering someone, it goes to that user, otherwise
 * it's the current channel.  Some tricky parameter business going on. If
 * nick is null (as if you had typed "/ testing" on the command line) the
 * line is sent to your channel, and the command parameter is never used. If
 * nick is not null, and command is null, the line is sent as a PRIVMSG.  If
 * nick is not null and command is not null, the message is sent using
 * command.  Currently, only NOTICEs and PRIVMSGS work. 
 */
void	send_text(org_nick, line, command)
char	*org_nick;
char	*line;
char	*command;
{
	char	*key,
		*ptr,
		*free_nick,
		*nick = null(char *);
	int	lastlog_level,
		list_type,
		old_server;
	char	the_thing;
	char	*query_command = null(char *);

	malloc_strcpy(&nick, org_nick);
	free_nick = ptr = nick;
	while (nick = ptr)
	{
		if (ptr = index(nick, ','))
			*(ptr++) = null(char);
		if (!*nick)
			continue;
		if (is_process(nick))
		{
			int	i;

			if ((i = get_process_index(&nick)) == -1)
				say("Invalid process specification");
			else
				text_to_process(i, line, 1);
			continue;
		}
		if (!*line)
			continue; /* lynx */
		if (in_on_who)
		{
			say("You may not send messages from ON WHO or \
ON WHOIS");
			continue;
		}
		/* stop the naughties - phone */
		if (doing_privmsg)
			command	= "NOTICE";
		/* Query quote -lynx */
		if (strcmp(nick, "\"") == 0) /* quote */
		{
			send_to_server("%s", line);
			continue;
		}
		if (*nick == '=') /* DCC chat */
		{
			old_server = from_server;
			from_server = -1;
			dcc_chat_transmit(nick + 1, line);
			from_server = old_server;
			continue;
		}
		if (*nick == '@') /* DCC talk */
		{
			old_server = from_server;
			from_server = -1;
			dcc_message_transmit(nick + 1, line, DCC_TALK);
			from_server = old_server;
			continue;
		}
		if (*nick == '/') /* Command */
		{
			malloc_strcpy(&query_command, nick);
			malloc_strcat(&query_command, " ");
			malloc_strcat(&query_command, line);
			parse_command(query_command, 0, empty_string);
			new_free(&query_command);
			continue;
		}
		switch (send_text_flag)
		{
		case MSG_LIST:
			command = "NOTICE";
			break;
		case NOTICE_LIST:
			echo(null(char *), "*** You cannot send a message  \
from a NOTICE");
			new_free(&free_nick);
			return;
		}
		if (is_channel(nick))
		{
			int	current;

			current = is_current_channel(nick, 0);
			if ((command == null(char *)) || strcmp(command,
					"NOTICE"))
			{
				command = "PRIVMSG";
				lastlog_level =
					set_lastlog_msg_level(LOG_PUBLIC);
				message_from(nick, LOG_PUBLIC);
				list_type = SEND_PUBLIC_LIST;
				the_thing = '>';
			}
			else
			{
				lastlog_level =
					set_lastlog_msg_level(LOG_NOTICE);
				message_from(nick, LOG_NOTICE);
				list_type = SEND_NOTICE_LIST;
				the_thing = '-';
			}
			if (do_hook(list_type, "%s %s", nick, line))
			{
				if (current)
					put_it("%c %s", the_thing, line);
				else
					put_it("%c%s> %s", the_thing, nick,
						line);
			}
			if (key = is_crypted(nick))
			{
				if ((line = crypt_msg(line, key, 1)) ==
						null(char *))
					continue;
			}
			set_lastlog_msg_level(lastlog_level);
			if (!in_on_who)
			{
				if (get_int_var(USE_OLD_MSG_VAR) &&
						(*nick != '#') && current &&
						(list_type == SEND_PUBLIC_LIST))
					send_to_server("MSG :%s", line);
				else
					send_to_server("%s %s :%s", command,
						nick, line);
			}
		}
		else
		{
			if ((command == null(char *)) || strcmp(command,
					"NOTICE"))
			{
				lastlog_level = set_lastlog_msg_level(LOG_MSG);
				command = "PRIVMSG";
				message_from(nick, LOG_MSG);
				list_type = SEND_MSG_LIST;
				the_thing = '*';
			}
			else
			{
				lastlog_level =
					set_lastlog_msg_level(LOG_NOTICE);
				message_from(nick, LOG_NOTICE);
				list_type = SEND_NOTICE_LIST;
				the_thing = '-';
			}
			if (window_display && do_hook(list_type, "%s %s",
					nick, line))
				put_it("-> %c%s%c %s", the_thing, nick,
					the_thing, line);
			if (key = is_crypted(nick))
			{
				if ((line = crypt_msg(line, key, 1)) ==
						null(char *))
					continue;
			}
			set_lastlog_msg_level(lastlog_level);
			if (!in_on_who)

			{
				if (command)
					send_to_server("%s %s :%s", command,
						nick, line);
				else
					send_to_server("PRIVMSG %s :%s", nick,
						line);
			}
			if (get_int_var(WARN_OF_IGNORES_VAR) &&
			    (is_ignored(nick, IGNORE_MSGS) == IGNORED))
				say("Warning: You are ignoring private \
messages from %s", nick);
			malloc_strcpy(&sent_nick, nick);
			malloc_strcpy(&sent_body, line);
			if (server_list[curr_scr_win->server].away
					&& get_int_var(AUTO_UNMARK_AWAY_VAR))
				away("AWAY", empty_string);
		}
	}
	message_from(null(char *), LOG_CRAP);
	new_free(&free_nick);
}

void	do_send_text(command, args)
char	*command,
	*args;
{
	char	*tmp;

	if (command)
		tmp = get_channel_by_refnum(0);
	else
		tmp = get_target_by_refnum(0);
	send_text(tmp, args, null(char *));
}

/*
 * command_completion: builds lists of commands and aliases that match the
 * given command and displays them for the user's lookseeing 
 */
void	command_completion()
{
	int	do_aliases;
	int	cmd_cnt,
		alias_cnt,
		i,
		c,
		len;
	char	**aliases = null(char **);
	char	*line = null(char *),
		*com,
		*cmdchars,
		*rest;
	IrcCommand	*command;

	malloc_strcpy(&line, get_input());
	if (com = next_arg(line, &rest))
	{
		if ((cmdchars = get_string_var(CMDCHARS_VAR)) == null(char *))
			cmdchars = DEFAULT_CMDCHARS;
		if (index(cmdchars, *com))
		{
			com++;
			if (*com && index(cmdchars, *com))
			{
				do_aliases = 0;
				alias_cnt = 0;
				com++;
			}
			else
				do_aliases = 1;
			upper(com);
			if (do_aliases)
				aliases = match_alias(com, &alias_cnt,
					COMMAND_ALIAS);
			if (command = find_command(com, &cmd_cnt))
			{
				if (cmd_cnt < 0)
					cmd_cnt *= -1;
				/* special case for the empty string */

				if (*(command[0].name) == null(char))
				{
					command++;
					cmd_cnt = NUMBER_OF_COMMANDS;
				}
			}
			if ((alias_cnt == 1) && (cmd_cnt == 0))
			{
				sprintf(buffer, "/%s %s", aliases[0], rest);
				set_input(buffer);
				new_free(&(aliases[0]));
				new_free(&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) == 0)))
			{
				sprintf(buffer, "/%s%s %s", (do_aliases ? "" :
						"/"), command[0].name, rest);
				set_input(buffer);
				update_input(UPDATE_ALL);
			}
			else
			{
				*buffer = null(char);
				if (command)
				{
					say("Commands:");
					strmcpy(buffer, "\t", BIG_BUFFER_SIZE);
					c = 0;
					for (i = 0; i < cmd_cnt; i++)
					{
						strmcat(buffer, command[i].name,
							BIG_BUFFER_SIZE);
						for (len =
						    strlen(command[i].name);
						    len < 15; len++)
							strmcat(buffer, " ",
							    BIG_BUFFER_SIZE);
						if (++c == 4)
						{
							say("%s", buffer);
							strmcpy(buffer, "\t",
							    BIG_BUFFER_SIZE);
							c = 0;
						}
					}
					if (c)
						say("%s", buffer);
				}
				if (aliases)
				{
					say("Aliases:");
					strmcpy(buffer, "\t", BIG_BUFFER_SIZE);
					c = 0;
					for (i = 0; i < alias_cnt; i++)
					{
						strmcat(buffer, aliases[i],
							BIG_BUFFER_SIZE);
						for (len = strlen(aliases[i]);
								len < 15; len++)
							strmcat(buffer, " ",
							    BIG_BUFFER_SIZE);
						if (++c == 4)
						{
							say("%s", buffer);
							strmcpy(buffer, "\t",
							    BIG_BUFFER_SIZE);
							c = 0;
						}
						new_free(&(aliases[i]));
					}
					if (strlen(buffer) > 1)
						say("%s", buffer);
					new_free(&aliases);
				}
				if (*buffer == null(char))
					term_beep();
			}
		}
		else
			term_beep();
	}
	else
		term_beep();
	new_free(&line);
}

/*
 * 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.
 */
void	parse_line(name, org_line, args, hist_flag, append_flag)
char	*name,
	*org_line,
	*args;
int	hist_flag,
	append_flag;
{
	char	*line = null(char *),
		*ptr,
		*start,
		*free_line,
		*stuff,
		*buffer,
		*s,
		*t;
	int	args_flag;

	malloc_strcpy(&line, org_line);
	start = free_line = line;
	args_flag = 0;
	ptr = line;
	if (!*line)
		do_send_text(NULL, empty_string);
	else if (args)
		do
		{
			stuff = expand_alias(name, line, args, &args_flag,
					&line);
			if (!line && append_flag && !args_flag && args && *args)
			{
				buffer = (char *) new_malloc(BIG_BUFFER_SIZE+1);
				strmcpy(buffer, stuff, BIG_BUFFER_SIZE);
				strmcat(buffer, " ", BIG_BUFFER_SIZE);
				strmcat(buffer, args, BIG_BUFFER_SIZE);
				new_free(&stuff);
				stuff = buffer;
			}
			parse_command(stuff, hist_flag, args);
			new_free(&stuff);
		} while(line);
	else
	{
		if (load_depth)
			parse_command(line, hist_flag, args);
		else
			for (;s = line;)
			{
				if (t = sindex(line, "\r\n"))
				{
					*t++ = '\0';
					line = t;
				}
				else
					line = NULL;
				parse_command(s, hist_flag, args);
			}
	}
	new_free(&free_line);
	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). 
 */
void	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(char *);
	int	args_flag,
		add_to_hist,
		cmdchar_used;

	if (!line || !*line) return;
	if (get_int_var(DEBUG_VAR) & DEBUG_COMMANDS)
		yell("Executing [%d] %s", level, line);
	level++;
	if ((cmdchars = get_string_var(CMDCHARS_VAR)) == null(char *))
		cmdchars = DEFAULT_CMDCHARS;
	malloc_strcpy(&this_cmd, line);
	add_to_hist = 1;
	if (index(cmdchars, *line))
	{
		cmdchar_used = 1;
		com = line + 1;
	}
	else
	{
		cmdchar_used = 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(char *), line);
		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(char *), line+1);
		if (hist_flag && add_to_hist)
		{
			add_to_history(this_cmd);
			set_input(empty_string);
		}
	}
#if 0 /* ':' is the no-op command. */
	else if (*com == ':' && get_int_var(COMMAND_MODE_VAR))
	{
		me(null(char *), line+1);
		if (hist_flag && add_to_hist)
		{
			add_to_history(this_cmd);
			set_input(empty_string);
		}
	}
#endif
	else if (*com == '@')
	{
		/* This kludge fixes a memory leak */
		char	*tmp;

		tmp = parse_inline(line + 1, sub_args, &args_flag);
		if (tmp)
			new_free(&tmp);
		if (hist_flag && add_to_hist)
		{
			add_to_history(this_cmd);
			set_input(empty_string);
		}
	}
	else
	{
		char	*rest,
			*alias = null(char *),
			*alias_name;
		int	cmd_cnt,
			alias_cnt;
		IrcCommand	*command; /* = null(IrcCommand *) */

		display = window_display;
		old_display_var = (unsigned) get_int_var(DISPLAY_VAR);
		if (rest = index(com, ' '))
			*(rest++) = null(char);
		else
			rest = empty_string;
		upper(com);

		/* first, check aliases */
		if (*com && index(cmdchars, *com))
		{
			alias_cnt = 0;
			com++;
			if (*com == '^')
			{
				com++;
				window_display = 0;
			}
		}
		else{
			if (*com == '^')
			{
				com++;
				window_display = 0;
			}
			alias = get_alias(COMMAND_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);
			}
			execute_alias(alias_name, alias, rest);
			new_free(&alias_name);
		}
		else
		{
			/* History */
			if (*com == '!')
			{
				if (com = do_history(com + 1, rest))
				{
					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);
				/* add_to_hist = 0;   -- unused anyway */
			}
			else
			{
				if (hist_flag && add_to_hist)
				{
					add_to_history(this_cmd);
					set_input(empty_string);
				}
				command = find_command(com, &cmd_cnt);
				if ((command && (cmd_cnt < 0)) ||
						((alias_cnt == 0) && (cmd_cnt
							== 1)))
				{
					if (command->func)
						command->func(
							command->server_func,
							rest, sub_args);
					else
						say("%s: command disabled",
							command->name);
				}
				else if (alias && (alias_cnt == 1) &&
				    (cmd_cnt == 1) &&
				    (strcmp(alias_name, command[0].name) == 0))
					execute_alias(alias_name, alias, rest);
				else if ((alias_cnt + cmd_cnt) > 1)
					say("Ambiguous command: %s", com);
				else if (alias && (alias_cnt == 1))
					execute_alias(alias_name, alias, rest);
				else if (!stricmp(com, nickname))
						/* nick = /me  -lynx */
					me(null(char *), rest);
				else
					say("Unknown command: %s", com);
			}
			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;
	}
	new_free(&this_cmd);
	level--;
}

/*
 * load: the /LOAD command.  Reads the named file, parsing each line as
 * though it were typed in (passes each line to parse_command). 
 */
/*ARGSUSED*/
void	load(command, args)
char	*command,
	*args;
{
	FILE	*fp;
	char	*filename,
		*expanded = null(char *);
	int	flag = 0;
	struct	stat	stat_buf;
	int	paste_level = 0;
	char	*start,
		*current_row = null(char *),
		buffer[BIG_BUFFER_SIZE + 1];
	int	no_semicolon = 1;
	char	*irc_path;
	int	display;
#ifdef ZCAT
	char	*temp;
	char	*expand_z = null(char *);
	int	exists;
#endif

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

	if (load_depth == MAX_LOAD_DEPTH)
	{
		say("No more than %d levels of LOADs allowed", MAX_LOAD_DEPTH);
		return;
	}
	load_depth++;
	status_update(0);
#ifdef DAEMON_UID
	if (getuid() == DAEMON_UID)
	{
		say("You may only load your SAVED file");
		filename = ircrc_file;
	}
	else
#endif /* DAEMON_UID */
		while (filename = next_arg(args, &args))
		{
			if (strnicmp(filename, "-args", strlen(filename)) == 0)
				flag = 1;
			else
				break;
		}
	if (filename)
	{
		if (expanded = expand_twiddle(filename))
		{
#ifdef ZCAT
			/* Handle both <expanded> and <expanded>.Z */
			temp = &(expanded[strlen(expanded) - 2]);
			if (strcmp(temp, ZSUFFIX))
			{
				malloc_strcpy(&expand_z, expanded);
				malloc_strcat(&expand_z, ZSUFFIX);
			}
#endif /*ZCAT*/
			if (*expanded != '/')
			{
#ifdef ZCAT
				if ((filename = path_search(expanded, irc_path))
						== null(char *))
					filename = path_search(expand_z,
						irc_path);
				if (filename == null(char *))
#else
					if ((filename = path_search(expanded,
						irc_path)) == null(char *))
#endif /*ZCAT*/
					{
						say("%s: File not found",
							expanded);
						status_update(1);
						load_depth--;
#ifdef ZCAT
						new_free(&expand_z);
#endif /* ZCAT */
						new_free(&expanded);
						return;
					}
					else
					    malloc_strcpy(&expanded, filename);
			}
#ifdef ZCAT
			if ((exists = stat_file(expanded, &stat_buf)) == -1)
				if (!(exists = stat_file(expand_z, &stat_buf)))
					expanded = expand_z;
				else
					new_free(&expand_z);
			if (exists == 0)
#else
				if (!stat_file(expanded, &stat_buf))
#endif /*ZCAT*/
				{
					if (stat_buf.st_mode & S_IFDIR)
					{
						say("%s is a directory",
							expanded);
						status_update(1);
						load_depth--;
#ifdef ZCAT
						new_free(&expand_z);
#endif /* ZCAT */
						new_free(&expanded);
						return;
					}
					if (stat_buf.st_mode & 0111)
					{
						say("%s is executable and may \
not be loaded", expanded);
						status_update(1);
						load_depth--;
#ifdef ZCAT
						new_free(&expand_z);
#endif /* ZCAT */
						new_free(&expanded);
						return;
					}
				}
			if (command && *command == 'W')
			{
				say("%s", expanded);
				status_update(1);
				load_depth--;
				new_free(&expanded);
				return;
			}
#ifdef ZCAT
			/* Open if uncompressed, zcat if compressed */
			temp = &(expanded[strlen(expanded)-2]);
			if (!strcmp(temp, ZSUFFIX))
				fp = zcat(expanded);
			else
				fp = fopen(expanded, "r");
			if (fp != NULL)
#else
				if (fp = fopen(expanded, "r"))
#endif /*ZCAT*/
				{
					display = window_display;
					window_display = 0;
					current_row = NULL;
					if (!flag)
						args = null(char *);
					for (;;)
					{
						if (fgets(buffer,
						    BIG_BUFFER_SIZE / 2, fp))
/*
 * We move this section of code over some distance, to get the whole
 * lot under 80 columns - phone, jan 1993.
 */
	{
		int	len;
		char	*ptr;

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

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

		while (start && *start)
		{
			char	*optr = start;
			while ((ptr = sindex(optr, "{};")) &&
					ptr != optr &&
					ptr[-1] == '\\')
				optr = ptr+1;

			if (no_semicolon)
			{
#if 0
				while (isspace(*start))
					start++;
#endif
				no_semicolon = 0;
			}
			else if ((!ptr || ptr != start) && current_row)
			{
				if (!paste_level)
				{
					parse_line(null(char *), current_row,
						args, 0, 0);
					new_free(&current_row);
				}
				else
					malloc_strcat(&current_row, ";");
			}

			if (ptr)
			{
				char	c = *ptr;

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

				switch (c)
				{
				case '{' :
					paste_level++;
					if (ptr == start)
					    malloc_strcat(&current_row, " {");
					else
					    malloc_strcat(&current_row, "{");
					no_semicolon = 1;
					break;

				case '}' :
					if (!paste_level)
						yell("Unexpected }");
					else
					{
					    --paste_level;
					    malloc_strcat(&current_row, "}");
					    no_semicolon = ptr[1] ? 1 : 0;
					}
					break;

				case ';' :
					malloc_strcat(&current_row, ";");
					no_semicolon = 1;
					break;
				}

				start = ptr+1;
			}
			else
			{
				malloc_strcat(&current_row, start);
				start = null(char *);
			}
		}
	}
/*
 * Until here - phone..  now isn't that much more beautiful ?
 */
						else
							break;
					}
					if (current_row)
					{
						if (paste_level)
							yell("Unexpected EOF");
						else
							parse_line(null(char *),
								current_row, 
								args, 0, 0);
						new_free(&current_row);
					}
					window_display = display;
					fclose(fp);
				}
				else
					say("Couldn't open %s: %s", expanded,
						sys_errlist[errno]);
			new_free(&expanded);
		}
		else
			say("Unknown user");
	}
	else
		say("No filename specified");
	status_update(1);
	load_depth--;
}

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

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

/* BIND function: */
void	forward_character() { input_move_cursor(RIGHT); }
void	backward_character() { input_move_cursor(LEFT); }
void	backward_history() { get_history(PREV); }
void	forward_history() { get_history(NEXT); }
void	toggle_insert_mode() { set_var_value(INSERT_MODE_VAR, "TOGGLE"); }

/*ARGSUSED*/
void	send_line(key, ptr)
char	*key;
void	(*ptr)();
{
	int	server;
	WaitPrompt	*OldPrompt;

	server = from_server;
	from_server = get_window_server(0);
	reset_hold(null(Window *));
	hold_mode(null(Window *), OFF, 1);
	if (current_screen->promptlist && current_screen->promptlist->type ==
			WAIT_PROMPT_LINE)
	{
		OldPrompt = current_screen->promptlist;
		(*OldPrompt->func)(OldPrompt->data, get_input());
		set_input(empty_string);
		current_screen->promptlist = OldPrompt->next;
		new_free(&OldPrompt->data);
		new_free(&OldPrompt->prompt);
		new_free(&OldPrompt);
		change_input_prompt(-1);
	}
	else
	{
		char	*line,
			*tmp = null(char *);

		line = get_input();
		malloc_strcpy(&tmp, line);

		if (do_hook(INPUT_LIST, "%s", tmp))
		{
			if (get_int_var(INPUT_ALIASES_VAR))
				parse_line(null(char *), tmp, empty_string,
					1, 0);
			else
				parse_line(null(char *), tmp, null(char *),
					1, 0);
		}
		update_input(UPDATE_ALL);
		new_free(&tmp);
	}
	from_server = server;
}

/* The SENDLINE command.. */
static	void	sendlinecmd(command, args)
char	*command,
	*args;
{
	int	server;
	int	display;

	server = from_server;
	display = window_display;
	window_display = 1;
	if (get_int_var(INPUT_ALIASES_VAR))
		parse_line(null(char *), args, empty_string, 1, 0);
	else
		parse_line(null(char *), args, null(char *), 1, 0);
	update_input(UPDATE_ALL);
	window_display = display;
	from_server = server;
}

/*ARGSUSED*/
void	meta4_char(key, ptr)
char	*key;
void	(*ptr)();
{
	current_screen->meta4_hit = 1 - current_screen->meta4_hit;
}

/*ARGSUSED*/
void	meta3_char(key, ptr)
char	*key;
void	(*ptr)();
{
	current_screen->meta3_hit = 1;
}

/*ARGSUSED*/
void	meta2_char(key, ptr)
char	*key;
void	(*ptr)();
{
	current_screen->meta2_hit = 1;
}

/*ARGSUSED*/
void	meta1_char(key, ptr)
char	*key;
void	(*ptr)();
{
	current_screen->meta1_hit = 1;
}

void	quote_char(c)
char	c;
{
	current_screen->quote_hit = 1;
}

/* type_text: the BIND function TYPE_TEXT */
/*ARGSUSED*/
void	type_text(c, str)
char	c,
	*str;
{
	for (; *str; str++)
		input_add_character(*str);
}

/*
 * clear_screen: the CLEAR_SCREEN function for BIND.  Clears the screen and
 * starts it if it is held 
 */
/*ARGSUSED*/
void	clear_screen(c, str)
char	c,
	*str;
{
	hold_mode(null(Window *), OFF, 1);
	clear(null(char *), empty_string);
}

/* parse_text: the bindable function that executes its string */
void	parse_text(c, str)
char	c,
	*str;
{
	parse_line(null(char *), str, empty_string, 0, 0);
}

/*
 * edit_char: handles each character for an input stream.  Not too difficult
 * to work out.
 */
void	edit_char(key)
u_char	key;
{
	void	(*func) ();
	char	*ptr;
	u_char	extended_key;
	WaitPrompt *oldprompt;

	if (current_screen->promptlist &&
			current_screen->promptlist->type == WAIT_PROMPT_KEY)
	{
		oldprompt = current_screen->promptlist;
		(*oldprompt->func)(oldprompt->data, &key);
		set_input(empty_string);
		current_screen->promptlist = oldprompt->next;
		new_free(&oldprompt->data);
		new_free(&oldprompt->prompt);
		new_free(&oldprompt);
		change_input_prompt(-1);
		return;
	}
	if (translation)
		extended_key = transFromClient[key];
	else
		extended_key = key;

	if (!get_int_var(EIGHT_BIT_CHARACTERS_VAR) || !term_eight_bit())
		key &= 0x7f;			/* mask out non-ascii crap */

	if (current_screen->meta1_hit)
	{
		func = key_names[meta1_keys[key].index].func;
		ptr = meta1_keys[key].stuff;
		current_screen->meta1_hit = 0;
	}
	else if (current_screen->meta2_hit)
	{
		func = key_names[meta2_keys[key].index].func;
		ptr = meta2_keys[key].stuff;
		current_screen->meta2_hit = 0;
	}
	else if (current_screen->meta3_hit)
	{
		func = key_names[meta3_keys[key].index].func;
		ptr = meta3_keys[key].stuff;
		current_screen->meta3_hit = 0;
	}
	else if (current_screen->meta4_hit)
	{
		func = key_names[meta4_keys[key].index].func;
		ptr = meta4_keys[key].stuff;
	}
	else
	{
		func = key_names[keys[key].index].func;
		ptr = keys[key].stuff;
	}
	if (!current_screen->meta1_hit && !current_screen->meta2_hit &&
			!current_screen->meta3_hit)
	{
		if (current_screen->inside_menu == 1)
			menu_key(key);
		else if (current_screen->digraph_hit)
		{
			if (extended_key == 0x08 || extended_key == 0x7f)
				current_screen->digraph_hit = 0;
			else if (current_screen->digraph_hit == 1)
			{
				current_screen->digraph_first = extended_key;
				current_screen->digraph_hit = 2;
			}
			else if (current_screen->digraph_hit == 2)
			{
				if (extended_key = get_digraph(extended_key))
					input_add_character(extended_key);
				else
					term_beep();
			}
		}
		else if (current_screen->quote_hit)
		{
			current_screen->quote_hit = 0;
			input_add_character(extended_key);
		}
		else if (func)
			func(extended_key, ptr ? ptr : empty_string);
	}
	else
		term_beep();
}

/*ARGSUSED*/
static	void	cd(command, args)
char	*command,
	*args;
{
	char	*arg,
		*expand;

#ifdef DAEMON_UID
	if (getuid() == DAEMON_UID)
	{
		say("You are not permitted to use this command");
		return;
	}
#endif /* DAEMON_UID */
	if (arg = next_arg(args, &args))
	{
		if (expand = expand_twiddle(arg))
		{
			if (chdir(expand))
				say("CD: %s", sys_errlist[errno]);
			new_free(&expand);
		}
		else
			say("CD: No such user");
	}
#if defined(ISC22) || defined(SVR4) || defined(_IBMR2) || \
	defined(POSIX) || defined(linux)
	getcwd(buffer, BIG_BUFFER_SIZE);
#else
	getwd(buffer);
#endif
	say("Current directory: %s", buffer);
}

static	void	send_action(target, text)
char	*target, *text;
{
	send_ctcp(ctcp_type[CTCP_PRIVMSG], target, "ACTION", text);
}

#if 0
static	char*	prepare_action(string)
char*	string;
{
	short	last;
	char	*message;

	last = strlen(string) - 1;
	while(string[last] == ' ')
		if (--last < 0) return NULL;

	if ((string[last] > 'a' && string[last] < 'z') ||
			(string[last] > 'A' && string[last] < 'Z'))
	{
		message = new_malloc(last + 2);
		strmcpy (message, string, last+1);
		message[last + 1] = '.';
		message[last + 2] = '\0';
		return message;
	}
	else
		return NULL;
}
#endif

static	void	describe(command, args)
char	*command,
	*args;
{
	char	*target;

	target = next_arg(args, &args);
	if (target && args && *args)
	{
		int	old;
		int	from_level;
#if 0
		char	*result;
#endif
		char	*message;

#if 0
		if (result = prepare_action(args))
			message = result;
		else
#endif
			message = args;
		send_action(target, 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: %s %s", target,
				get_server_nickname(from_server), message);
		set_lastlog_msg_level(old);
		message_from_level(from_level);

#if 0
		if (result)
			new_free(&result);
#endif
	}
	else
		say("Usage: /DESCRIBE <target> <action description>");
}

/*
 * New 'me' command - now automatically appends period.
 * Necessary for new 'action' script.   -lynx'92
 * Hardly, removed this as it was generally considered offensive
 */
static	void	me(command, args)
char	*command,
	*args;
{
	if (args && *args)
	{
		char	*target;

		if (target = get_target_by_refnum(0))
		{
			int	old;
			int	from_level;
#if 0
			char	*result;
#endif
			char	*message;

			if (*target == '=' || !strncmp(target,
					get_string_var(CMDCHARS_VAR), 1))
				if (!(target = get_channel_by_refnum(0)))
				{
					say("No target, neither channel nor \
query");
					return;
				}
#if 0
			if (result = prepare_action(args))
				message = result;
			else
#endif
				message = args;
			send_action(target, 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 %s",
				    get_server_nickname(from_server), message);
			set_lastlog_msg_level(old);
			message_from_level(from_level);

#if 0
			if (result)
				new_free(&result);
#endif
		}
		else
			say("No target, neither channel nor query");
	}
	else
		say("Usage: /ME <action description>");
}

static	void	mload(command, args)
char	*command,
	*args;
{
	char	*file;

	while (file = next_arg(args, &args))
		load_menu(file);
}

static	void	mlist(command, args)
char	*command,
	*args;
{
	char	*menu;

	while (menu = new_next_arg(args, &args))
		(void) ShowMenu(menu, 0);
}

static	void	evalcmd(command, args, subargs)
char	*command,
	*args,
	*subargs;
{
	parse_line(null(char *), args, subargs ? subargs : empty_string, 0, 0);
}

/*
 * ExecuteTimers:  checks to see if any currently pending timers have
 * gone off, and if so, execute them, delete them, etc, setting the
 * current_exec_timer, so that we can't remove the timer while its
 * still executing.
 */
extern	void	ExecuteTimers()
{
	Long	current;
	TimerList *next;

	time(&current);
	while (PendingTimers && PendingTimers->time <= current)
	{
		current_exec_timer = PendingTimers->ref;
		parse_command(PendingTimers->command, 0, empty_string);
		current_exec_timer = -1;
		new_free(&PendingTimers->command);
		next = PendingTimers->next;
		new_free(&PendingTimers);
		PendingTimers = next;
	}
}

/*
 * timercmd: the bit that handles the TIMER command.  If there are no
 * arguements, then just list the currently pending timers, if we are
 * give a -DELETE flag, attempt to delete the timer from the list.  Else
 * consider it to be a timer to add, and add it.
 */
static	void	timercmd(command, args)
char	*command;
char	*args;
{
	char	*waittime,
		*flag;
	Long	current;
	TimerList	**slot,
			*ntimer;
	int	want = -1,
		refnum;;

	if (*args == '-')
	{
		int	len;

		flag = next_arg(args, &args);
		len = strlen(flag);

		/* first check for the -DELETE flag */

		if (!strnicmp(flag, "-DELETE", len))
		{
			char	*ptr;
			int	ref;
			TimerList	*tmp,
					*prev;

			if (current_exec_timer != -1)
			{
				say("You may not remove a TIMER from itself");
				return;
			}
			if (!(ptr = next_arg(args, &args)))
			{
				say("%s: Need a timer reference number for \
-DELETE", command);
				return;
			}
			ref = atoi(ptr);
			for (prev = tmp = PendingTimers; tmp; prev = tmp,
					tmp = tmp->next)
			{
				if (tmp->ref == ref)
				{
					if (tmp == prev)
						PendingTimers =
							PendingTimers->next;
					else
						prev->next = tmp->next;
					new_free(&tmp->command);
					new_free(&tmp);
					return;
				}
			}
			say("%s: Can't delete %d, no such refnum",
				command, ref);
			return;
		}
		else if (!strnicmp(flag, "-REFNUM", len))
		{
			char	*ptr;

			ptr = next_arg(args, &args);
			want = atoi(ptr);
			if (want < 0)
			{
				say("%s: Illegal refnum %d", command, want);
				return;
			}
		}
		else
		{
			say("%s: %s no such flag", command, flag);
			return;
		}
	}

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

	if (!(waittime = next_arg(args, &args)))
	{
		show_timer(command);
		return;
	}

	/* must be something to add */

	if ((refnum = create_timer_ref(want)) == -1)
	{
		say("%s: Refnum %d already exists", command, want);
		return;
	}
	time(&current);
	ntimer = (TimerList *) new_malloc(sizeof(TimerList));
	ntimer->time = current + atol(waittime);
	ntimer->ref = refnum;
	ntimer->command = null(char *);
	malloc_strcpy(&ntimer->command, args);

	/* we've created it, now put it in order */

	for (slot = &PendingTimers; *slot; slot = &(*slot)->next)
	{
		if ((*slot)->time > ntimer->time)
			break;
	}
	ntimer->next = *slot;
	*slot = ntimer;
}

/*
 * show_timer:  Display a list of all the TIMER commands that are
 * pending to be executed.
 */
static	void	show_timer(command)
char	*command;
{
	TimerList	*tmp;
	Long	current,
		time_left;

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

	time(&current);
	say("Timer Seconds   Command");
	for (tmp = PendingTimers; tmp; tmp = tmp->next)
	{
		time_left = tmp->time - current;
		if (time_left < 0)
			time_left = 0;
		say("%-5d %-10d %s", tmp->ref, time_left, tmp->command);
	}
}

/*
 * create_timer_ref:  returns the lowest unused reference number for
 * a timer
 */
static	int	create_timer_ref(want)
int	want;
{
	TimerList	*tmp;
	int	ref = 0;
	int	done = 0;

	if (want == -1)
		while (!done)
		{
			done = 1;
			for (tmp = PendingTimers; tmp; tmp = tmp->next)
				if (ref == tmp->ref)
				{
					ref++;
					done = 0;
					break;
				}
		}
	else
	{
		ref = want;
		for (tmp = PendingTimers; tmp; tmp = tmp->next)
			if (ref == tmp->ref)
			{
				ref = -1;
				break;
			}
	}

	return (ref);
}
/*
 * 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.
 */

static	void	inputcmd(command, args)
char	*command,
	*args;
{
	char	*prompt;

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

	prompt = args;
	if (args = index(prompt, '"'))
		*args++ = '\0';
	else
	{
		say("Missing \" in INPUT");
		return;
	}

	for (; *args == ' '; args++)
		;

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

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

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

/* pingcmd: ctcp ping, duh - phone, jan 1993. */
static	void    pingcmd(command, args)
char    *command,
	*args;
{
	sprintf(buffer, "%s PING %ld", args, time(NULL));
	ctcp(command, buffer);
}

static	void	xtypecmd(command, args)
char	*command,
	*args;
{
	char	*arg;
	int	len;

	if (*args == '-')
	{
		args++;
		if (arg = next_arg(args, &args))
		{
			len = strlen(arg);
			if (!strnicmp(arg, "LITERAL", len))
			{
				for (; *args; args++)
					input_add_character(*args);
			}
			else
				say ("Unknown flag -%s to XTYPE", arg);
			return;
		}
		input_add_character('-');
	}
	else
		type(command, args);
	return;
}
