/*
 * alias.c Handles command aliases for irc.c 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/param.h>
#include "irc.h"
#include "alias.h"
#include "status.h"
#include "edit.h"
#include "history.h"
#include "vars.h"
#include "ircaux.h"
#include "server.h"
#include "screen.h"
#include "window.h"
#include "input.h"
#include "names.h"
#include "server.h"
#include "output.h"
#include "names.h"

extern	char	*dcc_raw_listen();
extern	char	*dcc_raw_connect();

extern	int	strnicmp();
extern	int	stricmp();

extern	void	strmcat();
extern	void	strmcat_ue();

extern	void	say();
extern	void	yell();
extern	void	new_free();
extern	void	malloc_stcpy();

extern	char	*FromUserHost;

static	char	*alias_detected();
static	char	*alias_sent_nick();
static	char	*alias_recv_nick();
static	char	*alias_msg_body();
static	char	*alias_joined_nick();
static	char	*alias_public_nick();
static	char	*alias_dollar();
static	char	*alias_channel();
static	char	*alias_server();
static	char	*alias_query_nick();
static	char	*alias_target();
static	char	*alias_nick();
static	char	*alias_invite();
static	char	*alias_cmdchar();
static	char	*alias_line();
static	char	*alias_away();
static	char	*alias_oper();
static	char	*alias_chanop();
static	char	*alias_modes();
static	char	*alias_buffer();
static	char	*alias_time();
static	char	*alias_version();
static	char	*alias_currdir();
static	char	*alias_current_numeric();
static	char	*alias_server_version();

typedef struct
{
	char	name;
	char	*(*func)();
}	BuiltIns;

static	BuiltIns built_in[] =
{
	{ '.',		alias_sent_nick },
	{ ',',		alias_recv_nick },
	{ ':',		alias_joined_nick },
	{ ';',		alias_public_nick },
	{ '$',		alias_dollar },
	{ 'A',		alias_away },
	{ 'B',		alias_msg_body },
	{ 'C',		alias_channel },
	{ 'D',		alias_detected },
/*
	{ 'E' },
	{ 'F' },
	{ 'G' },
*/
	{ 'H', 		alias_current_numeric },
	{ 'I',		alias_invite },
/*
	{ 'J' },
*/
	{ 'K',		alias_cmdchar },
	{ 'L',		alias_line },
	{ 'M',		alias_modes },
	{ 'N',		alias_nick },
	{ 'O',		alias_oper },
	{ 'P',		alias_chanop },
	{ 'Q',		alias_query_nick },
	{ 'R',		alias_server_version },
	{ 'S',		alias_server },
	{ 'T',		alias_target },
	{ 'U',		alias_buffer },
	{ 'V',		alias_version },
	{ 'W',		alias_currdir },
/*
	{ 'X' },
	{ 'Y' },
*/
	{ 'Z',		alias_time },
	{ null(char),	 NULL }
};

char	command_line[BIG_BUFFER_SIZE+1];

char	*function_left();
char	*function_right();
char	*function_mid();
char	*function_rand();
char	*function_srand();
char	*function_time();
char	*function_stime();
char	*function_index();
char	*function_rindex();
char	*function_match();
char	*function_rmatch();
char	*function_userhost();
char	*function_strip();
char	*function_encode();
char	*function_decode();
char	*function_ischannel();
char	*function_ischanop();
char	*function_word();
char	*function_winnum();
char	*function_winnam();
char	*function_connect();
char	*function_listen();
char	*function_tdiff();
char	*function_toupper();
char	*function_tolower();
char	*function_channels();
char	*function_servers();
char	*function_curpos();

typedef struct
{
	char	*name;
	char	*(*func)();
}	BuiltInFunctions;

static BuiltInFunctions	built_in_functions[] =
{
	{ "LEFT",		function_left },
	{ "RIGHT",		function_right },
	{ "MID",		function_mid },
	{ "RAND",		function_rand },
	{ "SRAND",		function_srand },
	{ "TIME",		function_time },
	{ "TDIFF",		function_tdiff },
	{ "STIME",		function_stime },
	{ "INDEX",		function_index },
	{ "RINDEX",		function_rindex },
	{ "MATCH",		function_match },
	{ "RMATCH",		function_rmatch },
	{ "USERHOST",		function_userhost },
	{ "STRIP",		function_strip },
	{ "ENCODE",		function_encode },
	{ "DECODE",		function_decode },
	{ "ISCHANNEL",		function_ischannel },
	{ "ISCHANOP",		function_ischanop },
	{ "WORD",		function_word },
	{ "WINNUM",		function_winnum },
	{ "WINNAM",		function_winnam },
	{ "CONNECT",		function_connect },
	{ "LISTEN",		function_listen },
	{ "TOUPPER",		function_toupper },
	{ "TOLOWER",		function_tolower },
	{ "MYCHANNELS",		function_channels },
	{ "MYSERVERS",		function_servers },
	{ "CURPOS",		function_curpos },
	{ null(char *),		NULL }
};

/* alias_illegals: characters that are illegal in alias names */
char	alias_illegals[] = " #+-*/\\()={}[]<>!@$%^~`,?;:|'\"";

/* Alias: structure of each alias entry */
typedef	struct	AliasStru
{
	char	*name;			/* name of alias */
	char	*stuff;			/* what the alias is */
	int	mark;			/* used to prevent recursive aliasing */
	struct	AliasStru	*next;	/* pointer to next alias in list */
}	Alias;

static	Alias	*alias_list[] =
{
	null(Alias *),
	null(Alias *)
};

/* alias_string: the thing that gets replaced by the $"..." construct */
static	char	*alias_string = null(char *);

static	int	eval_args;

/* function_stack and function_stkptr - hold the return values from functions */
static	char	*function_stack[128] =
{ 
	null(char *)
};
static	int	function_stkptr = 0;

extern	char	*MatchingBracket();

/*
 * find_alias: looks up name in in alias list.  Returns the Alias	entry if
 * found, or null if not found.   If unlink is set, the found entry is
 * removed from the list as well.  If match is null, only perfect matches
 * will return anything.  Otherwise, the number of matches will be returned. 
 */
static	Alias	*find_alias(list, name, unlink, match)
Alias	**list;
char	*name;
int	unlink;
int	*match;
{
	Alias	*tmp,
		*last = null(Alias *);
	int	cmp,
		len;
	int	(*cmp_func)();

	if (match)
	{
		*match = 0;
		cmp_func = strnicmp;
	}
	else
		cmp_func = stricmp;
	if (name)
	{
		len = strlen(name);
		for (tmp = *list; tmp; tmp = tmp->next)
		{
			if ((cmp = cmp_func(name, tmp->name, len)) == 0)
			{
				if (unlink)
				{
					if (last)
						last->next = tmp->next;
					else
						*list = tmp->next;
				}
				if (match)
				{
					(*match)++;
					if (strlen(tmp->name) == len)
					{
						*match = 0;
						return (tmp);
					}
				}
				else
					return (tmp);
			}
			else if (cmp < 0)
				break;
			last = tmp;
		}
	}
	if (match && (*match == 1))
		return (last);
	else
		return (null(Alias *));
}

/*
 * insert_alias: adds the given alias to the alias list.  The alias list is
 * alphabetized by name 
 */
static	void	insert_alias(list, alias)
Alias	**list;
Alias	*alias;
{
	Alias	*tmp,
		*last,
		*foo;

	last = null(Alias *);
	for (tmp = *list; tmp; tmp = tmp->next)
	{
		if (strcmp(alias->name, tmp->name) < 0)
			break;
		last = tmp;
	}
	if (last)
	{
		foo = last->next;
		last->next = alias;
		alias->next = foo;
	}
	else
	{
		alias->next = *list;
		*list = alias;
	}
}

/*
 * add_alias: given the alias name and "stuff" that makes up the alias,
 * add_alias() first checks to see if the alias name is already in use... if
 * so, the old alias is replaced with the new one.  If the alias is not
 * already in use, it is added. 
 */
void	add_alias(type, name, stuff)
int	type;
char	*name,
	*stuff;
{
	Alias	*tmp;
	char	*ptr;

	upper(name);
	if (type == COMMAND_ALIAS)
		say("Alias	%s added", name);
	else
	{
		if (!strcmp(name, "FUNCTION_RETURN"))
		{
			if (function_stack[function_stkptr])
				new_free(&function_stack[function_stkptr]);
			malloc_strcpy(&function_stack[function_stkptr], stuff);
			return;
		}
		if (ptr = sindex(name, alias_illegals))
		{
			yell("Assign names may not contain '%c'", *ptr);
			return;
		}
		say("Assign %s added", name);
	}
	if ((tmp = find_alias(&(alias_list[type]), name, 1, null(int *))) ==
			null(Alias *))
	{
		tmp = (Alias *) new_malloc(sizeof(Alias));
		if (tmp == null(Alias *))
		{
			yell("Couldn't allocate memory for new alias!");
			return;
		}
		tmp->name = null(char *);
		tmp->stuff = null(char *);
	}
	malloc_strcpy(&(tmp->name), name);
	malloc_strcpy(&(tmp->stuff), stuff);
	tmp->mark = 0;
	insert_alias(&(alias_list[type]), tmp);
}

/* alias_arg: a special version of next_arg for aliases */
static	char	*alias_arg(str, pos)
char	**str;
unsigned int	*pos;
{
	char	*ptr;

	*pos = 0;
	ptr = *str;
	while ((*ptr == ' '))
	{
		ptr++;
		(*pos)++;
	}
	if (*ptr == null(char))
	{
		*str = empty_string;
		return (null(char *));
	}
	if (*str = sindex(ptr, " "))
		*((*str)++) = null(char);
	else
		*str = empty_string;
	return (ptr);
}

/* word_count: returns the number of words in the given string */
static	int	word_count(str)
char	*str;
{
	int	cnt = 0;
	char	*ptr;

	while (1)
	{
		if (ptr = sindex(str, "^ "))
		{
			cnt++;
			if ((str = sindex(ptr, " ")) == null(char *))
				return (cnt);
		}
		else
			return (cnt);
	}
}

static	char	*built_in_alias(c)
char	c;
{
	BuiltIns	*tmp;
	char	*ret = null(char *);

	for(tmp = built_in;tmp->name;tmp++)
		if (c == tmp->name)
		{
			malloc_strcpy(&ret, tmp->func());
			break;
		}
	return(ret);
}

/*
 * find_inline: This simply looks up the given str.  It first checks to see
 * if its a user variable and returns it if so.  If not, it checks to see if
 * it's an IRC variable and returns it if so.  If not, it checks to see if
 * its and environment variable and returns it if so.  If not, it returns
 * null.  It mallocs the returned string 
 */
static	char	*find_inline(str)
char	*str;
{
	Alias	*alias;
	char	*ret = null(char *);

	if (alias = find_alias(&(alias_list[VAR_ALIAS]), str, 0, null(int *)))
	{
		malloc_strcpy(&ret, alias->stuff);
		return (ret);
	}
	if ((strlen(str) == 1) && (ret = built_in_alias(*str)))
		return(ret);
	if (ret = make_string_var(str))
		return (ret);
#ifdef DAEMON_UID
	if (getuid() != DAEMON_UID)
		malloc_strcpy(&ret, getenv(str));
#endif
	return (ret);
}

static	char	*call_function(name, f_args, args, args_flag)
char	*name,
	*f_args,
	*args;
int	*args_flag;
{
	char	*tmp;
	char	*result = null(char *);
	char	*sub_buffer = null(char *);
	int	builtnum;
	char	*debug_copy = null(char *);

	tmp = expand_alias(null(char *), f_args, args, args_flag, NULL);
	if (get_int_var(DEBUG_VAR) & DEBUG_FUNCTIONS)
		malloc_strcpy(&debug_copy, tmp);
	for (builtnum = 0; built_in_functions[builtnum].name != NULL &&
			stricmp(built_in_functions[builtnum].name, name);
			builtnum++)
		;
	if (built_in_functions[builtnum].name)
		result = built_in_functions[builtnum].func(tmp);
	else
	{
		sub_buffer = new_malloc(strlen(name)+strlen(tmp)+2);
		strcpy(sub_buffer, name);
		strcat(sub_buffer, " ");
		strcat(sub_buffer, tmp);
		function_stack[++function_stkptr] = null(char *);
		parse_command(sub_buffer, 0, empty_string);
		new_free(&sub_buffer);
		eval_args=1;
		result = function_stack[function_stkptr];
		function_stack[function_stkptr] = null(char *);
		if (!result)
			malloc_strcpy(&result, empty_string);
		function_stkptr--;
	}
	if (debug_copy)
	{
		yell("Function %s(%s) returned %s",
		    name, debug_copy, result);
		new_free(&debug_copy);
	}
	new_free(&tmp);
	return result;
}


/* Given a pointer to an operator, find the last operator in the string */
char	*lastop(ptr)
char	*ptr;
{
	while (ptr[1] && index("!=<>&^|#+/-*", ptr[1]))
		ptr++;
	return ptr;
}

#define	NU_EXPR	0
#define NU_CONJ 0
#define	NU_ASSN	1
#define	NU_COMP 2
#define	NU_ADD  3
#define NU_MULT 4
#define	NU_UNIT 5

static	char	*next_unit(str, args, arg_flag, stage)
char	*str,
	*args;
int	*arg_flag,
	stage;
{
	char	*ptr,
		*ptr2,
		*right;
	int	got_sloshed = 0;
	char	*lastc;
	char	tmp[40];
	char	*result1 = null(char *),
		*result2 = null(char *);
	Long	value1 = 0,
		value2,
		value3;
	char	op;
	int	display;
	char	*ArrayIndex,
		*EndIndex;

	while (isspace(*str))
		++str;
	if (!*str)
	{
		malloc_strcpy(&result1, empty_string);
		return result1;
	}
	lastc = str+strlen(str)-1;
	while (isspace(*lastc))
		*lastc-- = '\0';
	if (stage == NU_UNIT && *lastc == ')' && *str == '(')
	{
		str++, *lastc-- = '\0';
		return next_unit(str, args, arg_flag, NU_EXPR);
	}
	if (!*str)
	{
		malloc_strcpy(&result1, empty_string);
		return result1;
	}
	for (ptr = str; *ptr; ptr++)
	{
		if (got_sloshed) /* Help! I'm drunk! */
		{
			got_sloshed = 0;
			continue;
		}
		switch(*ptr)
		{
		case '\\':
			got_sloshed = 1;
			continue;
		case '(':
			if (stage != NU_UNIT || ptr == str)

			{
				if (!(ptr2 = MatchingBracket(ptr+1, '(', ')')))
					ptr = ptr+strlen(ptr)-1;
				else
					ptr = ptr2;
				break;
			}
			*ptr++ = '\0';
			right = ptr;
			ptr = MatchingBracket(right, LEFT_PAREN, RIGHT_PAREN);
			if (ptr)
				*ptr++ = '\0';
			result1 = call_function(str, right, args, arg_flag);
			if (ptr && *ptr)

			{
				malloc_strcat(&result1, ptr);
				result2 = next_unit(result1, args, arg_flag,
						stage);
				new_free(&result1);
				result1 = result2;
			}
			return result1;
		case '[':
			if (stage != NU_UNIT)

			{
				if (!(ptr2 = MatchingBracket(ptr+1, '[', ']')))
					ptr = ptr+strlen(ptr)-1;
				else
					ptr = ptr2;
				break;
			}
			*ptr++ = '\0';
			right = ptr;
			ptr = MatchingBracket(right, LEFT_BRACKET, RIGHT_BRACKET);
			if (ptr)
				*ptr++ = '\0';
			result1 = expand_alias(null(char *), right, args, arg_flag, NULL);
			if (*str)

			{
				result2 = new_malloc(strlen(str)+
						(result1?strlen(result1):0)+
						(ptr?strlen(ptr):0) + 2);
				strcpy(result2, str);
				strcat(result2, ".");
				strcat(result2, result1);
				new_free(&result1);
				if (ptr && *ptr)
				{
					strcat(result2, ptr);
					result1 = next_unit(result2, args,
						arg_flag, stage);
				}
				else
				{
					result1 = find_inline(result2);
					if (!result1)
						malloc_strcpy(&result1,
							empty_string);
				}
				new_free(&result2);
			}
			else if (ptr && *ptr)

			{
				malloc_strcat(&result1, ptr);
				result2 = next_unit(result1, args, arg_flag,
					stage);
				new_free(&result1);
				result1 = result2;
			}
			return result1;
		case '-':
		case '+':
			if (ptr == str) /* It's unary..... do nothing */
				break;
			if (stage != NU_ADD)

			{
				ptr = lastop(ptr);
				break;
			}
			op = *ptr;
			*ptr++ = '\0';
			result1 = next_unit(str, args, arg_flag, stage);
			result2 = next_unit(ptr, args, arg_flag, stage);
			value1 = atol(result1);
			value2 = atol(result2);
			new_free(&result1);
			new_free(&result2);
			if (op == '-')
				value3 = value1 - value2;
			else
				value3 = value1 + value2;
			sprintf(tmp, "%ld", value3);
			malloc_strcpy(&result1, tmp);
			return result1;
		case '/':
		case '*':
			if (stage != NU_MULT)

			{
				ptr = lastop(ptr);
				break;
			}
			op = *ptr;
			*ptr++ = '\0';
			result1 = next_unit(str, args, arg_flag, stage);
			result2 = next_unit(ptr, args, arg_flag, stage);
			value1 = atol(result1);
			value2 = atol(result2);
			new_free(&result1);
			new_free(&result2);
			if (op == '/')

			{
				if (value2)
					value3 = value1 / value2;
				else
				{
					value3 = 0;
					say("Division by zero");
				}
			}
			else
				value3 = value1 * value2;
			sprintf(tmp, "%ld", value3);
			malloc_strcpy(&result1, tmp);
			return result1;
		case '#':
			if (stage != NU_ADD || ptr[1] != '#')

			{
				ptr = lastop(ptr);
				break;
			}
			*ptr = '\0';
			ptr += 2;
			result1 = next_unit(str, args, arg_flag, stage);
			result2 = next_unit(ptr, args, arg_flag, stage);
			malloc_strcat(&result1, result2);
			new_free(&result2);
			return result1;
		case '&':
		case '|':
		case '^':
			if (stage != NU_CONJ || ptr[0] != ptr[1])

			{
				ptr = lastop(ptr);
				break;
			}
			op = *ptr;
			*ptr = '\0';
			ptr += 2;
			result1 = next_unit(str, args, arg_flag, stage);
			result2 = next_unit(ptr, args, arg_flag, stage);
			value1 = atol(result1);
			value2 = atol(result2);
			new_free(&result1);
			new_free(&result2);
			if (op == '|')
				value3 = value1 || value2;
			else if (op == '^')
		/*
		 * C doesn't have a ^^ (why not?), so we have to make sure
		 * that value1 and value2 conform.
		 */
			{
				value1 = (value1 ? 1 : 0);
				value2 = (value2 ? 1 : 0);
				value3 = value1 ^ value2;
			}
			else
				value3 = value1 && value2;
			tmp[0] = '0' + (value3 ? 1 : 0);
			tmp[1] = '\0';
			malloc_strcpy(&result1, tmp);
			return result1;
		case '=':
			if (ptr[1] != '=')
			{
				if (stage != NU_ASSN)
				{
					ptr = lastop(ptr);
					break;
				}
				*ptr++ = '\0';
				result1 = expand_alias(null(char *), str,
					args, arg_flag, NULL);
				result2 = next_unit(ptr, args, arg_flag, stage);
				display = window_display;
				window_display = 0;
				lastc = result1 + strlen(result1) - 1;
				while (lastc > result1 && *lastc == ' ')
					*lastc-- = '\0';
				for (ptr = result1; *ptr == ' '; ptr++);
				while (ArrayIndex = index(ptr, '['))
				{
					*ArrayIndex++='.';
					if (EndIndex=MatchingBracket(ArrayIndex,
						LEFT_BRACKET, RIGHT_BRACKET))
					{
						*EndIndex++='\0';
						strcat(ptr, EndIndex);
					}
					else
						break;
				}
				if (*ptr)
					add_alias(VAR_ALIAS, ptr, result2);
				else
					yell("Invalid assignment expression");
				window_display = display;
				new_free(&result1);
				return result2;
			}
			if (stage != NU_COMP)

			{
				ptr = lastop(ptr);
				break;
			}
			*ptr = '\0';
			ptr += 2;
			result1 = next_unit(str, args, arg_flag, stage);
			result2 = next_unit(ptr, args, arg_flag, stage);
			if (!stricmp(result1, result2))
				malloc_strcpy(&result1, "1");
			else
				malloc_strcpy(&result1, "0");
			new_free(&result2);
			return result1;
		case '>':
		case '<':
			if (stage != NU_COMP)
			{
				ptr = lastop(ptr);
				break;
			}
			op = *ptr;
			if (ptr[1] == '=')
				value3 = 1, *ptr++ = '\0';
			else
				value3 = 0;
			*ptr++ = '\0';
			result1 = next_unit(str, args, arg_flag, stage);
			result2 = next_unit(ptr, args, arg_flag, stage);
			if (isdigit(*result1) && isdigit(*result2))
			{
				value1 = atol(result1);
				value2 = atol(result2);
				value1 = (value1 == value2) ? 0 : ((value1 <
					value2) ? -1 : 1);
			}
			else
				value1 = stricmp(result1, result2);
			if (value1)
			{
				value2 = (value1 > 0) ? 1 : 0;
				if (op == '<')
					value2 = 1 - value2;
			}
			else
				value2 = value3;
			new_free(&result2);
			sprintf(tmp, "%ld", value2);
			malloc_strcpy(&result1, tmp);
			return result1;
		case '!':
			if (ptr == str)
			{
				if (stage != NU_UNIT)
					break;
				result1 = next_unit(str+1, args, arg_flag,
					stage);
				if (isdigit(*result1))
				{
					value1 = atol(result1);
					value2 = value1 ? 0 : 1;
				}
				else
				{
					value2 = ((*result1)?0:1);
				}
				sprintf(tmp, "%ld", value2);
				malloc_strcpy(&result1, tmp);
				return result1;
			}
			if (stage != NU_COMP || ptr[1] != '=')
			{
				ptr = lastop(ptr);
				break;
			}
			*ptr = '\0';
			ptr += 2;
			result1 = next_unit(str, args, arg_flag, stage);
			result2 = next_unit(ptr, args, arg_flag, stage);
			if (!stricmp(result1, result2))
				malloc_strcpy(&result1, "0");
			else
				malloc_strcpy(&result1, "1");
			new_free(&result2);
			return result1;
		}
	}
	if (stage != NU_UNIT)
		return next_unit(str, args, arg_flag, stage+1);
	if (isdigit(*str) || *str == '+' || *str == '-')
		malloc_strcpy(&result1, str);
	else
	{
		if (*str == '#' || *str=='@')
			op = *str++;
		else
			op = '\0';
		result1 = find_inline(str);
		if (!result1)
			malloc_strcpy(&result1, empty_string);
		if (op)
		{
			if (op == '#')
				value1 = word_count(result1);
			else if (op == '@')
				value1 = strlen(result1);
			sprintf(tmp, "%ld", value1);
			malloc_strcpy(&result1, tmp);
		}
	}
	return result1;
}

/*
 * parse_inline:  This evaluates user-variable expression.  I'll talk more
 * about this at some future date. The ^ function and some fixes by
 * troy@cbme.unsw.EDU.AU (Troy Rollo) 
 */
char	*parse_inline(str, args, args_flag)
char	*str;
char	*args;
int	*args_flag;
{
	return next_unit(str, args, args_flag, NU_EXPR);
}

/*
 * arg_number: Returns the argument 'num' from 'str', or, if 'num' is
 * negative, returns from argument 'num' to the end of 'str'.  You might be
 * wondering what's going on down there... here goes.  First we copy 'str' to
 * malloced space.  Then, using next_arg(), we strip out each argument ,
 * putting them in arg_list, and putting their position in the original
 * string in arg_list_pos.  Anyway, once parsing is done, the arguments are
 * returned directly from the arg_list array... or in the case of negative
 * 'num', the arg_list_pos is used to return the postion of the rest of the
 * args in the original string... got it?  Anyway, the bad points of the
 * routine:  1) Always parses out everything, even if only one arg is used.
 * 2) The malloced stuff remains around until arg_number is called with a
 * different string. Even then, some malloced stuff remains around.  This can
 * be fixed. 
 */

#define	LAST_ARG 8000

static	char	*arg_number(lower_lim, upper_lim, str)
int	lower_lim,
	upper_lim;
char	*str;
{
	char	*ptr,
		*arg,
		c;
	int	use_full = 0;
	unsigned int	pos,
		start_pos;
	static	char	*last_args = null(char *);
	static	char	*last_range = null(char *);
	static	char	**arg_list = null(char **);
	static	unsigned int	*arg_list_pos = null(unsigned int *);
	static	unsigned int	*arg_list_end_pos = null(unsigned int *);
	static	int	arg_list_size;

	if (eval_args)
	{
		int	arg_list_limit;

		eval_args = 0;
		new_free(&arg_list);
		new_free(&arg_list_pos);
		new_free(&arg_list_end_pos);
		arg_list_size = 0;
		arg_list_limit = 10;
		arg_list = (char **) new_malloc(sizeof(char *) *
			arg_list_limit);
		arg_list_pos = (unsigned int *) new_malloc(sizeof(unsigned int)
			* arg_list_limit);
		arg_list_end_pos = (unsigned int *) new_malloc(sizeof(unsigned
			int) * arg_list_limit);
		malloc_strcpy(&last_args, str);
		ptr = last_args;
		pos = 0;
		while (arg = alias_arg(&ptr, &start_pos))
		{
			arg_list_pos[arg_list_size] = pos;
			pos += start_pos + strlen(arg);
			arg_list_end_pos[arg_list_size] = pos++;
			arg_list[arg_list_size++] = arg;
			if (arg_list_size == arg_list_limit)
			{
				arg_list_limit += 10;
				arg_list = (char **) new_realloc(arg_list,
					sizeof(char *) * arg_list_limit);
				arg_list_pos = (unsigned int *)
					new_realloc(arg_list_pos,
					sizeof(unsigned int) * arg_list_limit);
				arg_list_end_pos = (unsigned int *)
					new_realloc(arg_list_end_pos,
					sizeof(unsigned int) *
					arg_list_limit);
			}
		}
	}
	if (upper_lim == LAST_ARG && lower_lim == LAST_ARG)

	{
		upper_lim = lower_lim = arg_list_size - 1;
	}
	if (arg_list_size == 0)
		return (empty_string);
	if ((upper_lim >= arg_list_size) || (upper_lim < 0))
	{
		use_full = 1;
		upper_lim = arg_list_size - 1;
	}
	if (upper_lim < lower_lim)
		return (empty_string);
	if (lower_lim >= arg_list_size)
		lower_lim = arg_list_size - 1;
	else if (lower_lim < 0)
		lower_lim = 0;
	if ((use_full == 0) && (lower_lim == upper_lim))
		return (arg_list[lower_lim]);
	c = *(str + arg_list_end_pos[upper_lim]);
	*(str + arg_list_end_pos[upper_lim]) = null(char);
	malloc_strcpy(&last_range, str + arg_list_pos[lower_lim]);
	*(str + arg_list_end_pos[upper_lim]) = c;
	return (last_range);
}

/*
 * parse_number: returns the next number found in a string and moves the
 * string pointer beyond that point	in the string.  Here's some examples: 
 *
 * "123harhar"  returns 123 and str as "harhar" 
 *
 * while: 
 *
 * "hoohar"     returns -1  and str as "hoohar" 
 */
static	int	parse_number(str)
char	**str;
{
	int	ret;
	char	*ptr;

	ptr = *str;
	if (isdigit(*ptr))
	{
		ret = atoi(ptr);
		for (; isdigit(*ptr); ptr++);
		*str = ptr;
	}
	else
		ret = -1;
	return (ret);
}

void	do_alias_string()
{
	malloc_strcpy(&alias_string, get_input());
	irc_io_loop = 0;
}

/*
 * expander_addition: This handles string width formatting for irc variables
 * when [] is specified.  
 */
static	void	expander_addition(buff, add, length, quote_em)
char	*buff,
	*add;
int	length;
char	*quote_em;
{
	char	format[40],
		*ptr;

	if (length)
	{
		sprintf(format, "%%%d.%ds", -length, (length < 0 ? -length :
			length));
		sprintf(buffer, format, add);
		add = buffer;
	}
	if (quote_em)
	{
		ptr = double_quote(add, quote_em);
		strmcat(buff, ptr, BIG_BUFFER_SIZE);
		new_free(&ptr);
	}
	else
		strmcat(buff, add, BIG_BUFFER_SIZE);
}

/* MatchingBracket returns the next unescaped bracket of the given type */
char	*MatchingBracket(string, left, right)
char	*string;
char	left,
	right;
{
	int	bracket_count = 1;

	while (*string && bracket_count)
	{
		if (*string == left)
			bracket_count++;
		else if (*string == right)
		{
			if (!--bracket_count)
				return string;
		}
		else if (*string == '\\' && string[1])
			string++;
		string++;
	}
	return null(char *);
}


/*
 * alias_special_char: Here we determin what to do with the character after
 * the $ in a line of text. The special characters are described more fulling
 * in the help/ALIAS file.  But they are all handled here. Paremeters are the
 * name of the alias (if applicable) to prevent deadly recursion, a
 * destination buffer (of size BIG_BUFFER_SIZE) to which things are appended,
 * a ptr to the string (the first character of which is the special
 * character, the args to the alias, and a character indication what
 * characters in the string should be quoted with a backslash.  It returns a
 * pointer to the character right after the converted alias.

 The args_flag is set to 1 if any of the $n, $n-, $n-m, $-m, $*, or $() is used
 in the alias.  Otherwise it is left unchanged.
 */
/*ARGSUSED*/
static	char	*alias_special_char(name, buffer, ptr, args, quote_em,args_flag)
char	*name;
char	*buffer;
char	*ptr;
char	*args;
char	*quote_em;
int	*args_flag;
{
	char	*tmp,
		c;
	int	upper,
	lower,
	length;

	length = 0;
	if ((c = *ptr) == LEFT_BRACKET)
	{
		ptr++;
		if (tmp = index(ptr, RIGHT_BRACKET))
		{
			*(tmp++) = null(char);
			length = atoi(ptr);
			ptr = tmp;
			c = *ptr;
		}
		else
		{
			say("Missing %c", RIGHT_BRACKET);
			return (ptr);
		}
	}
	tmp = ptr+1;
	switch (c)
	{
	case LEFT_PAREN:
		{
			char	sub_buffer[BIG_BUFFER_SIZE+1];

			if ((ptr = MatchingBracket(tmp, LEFT_PAREN,
					RIGHT_PAREN)) || (ptr = index(tmp,
					RIGHT_PAREN)))
				*(ptr++) = null(char);
			tmp = expand_alias(null(char *), tmp, args, args_flag,
				NULL);
			*sub_buffer = null(char);
			alias_special_char(null(char *), sub_buffer, tmp,
				args, quote_em,args_flag);
			expander_addition(buffer, sub_buffer, length, quote_em);
			new_free(&tmp);
			*args_flag = 1;
		}
		return (ptr);
	case '!':
		if (ptr = index(tmp, '!'))
			*(ptr++) = null(char);
		if (tmp = do_history(tmp, empty_string))
		{
			expander_addition(buffer, tmp, length, quote_em);
			new_free(&tmp);
		}
		return (ptr);
	case LEFT_BRACE:
		if (ptr = index(tmp, RIGHT_BRACE))
			*(ptr++) = null(char);
		if (tmp = parse_inline(tmp, args, args_flag))
		{
			expander_addition(buffer, tmp, length, quote_em);
			new_free(&tmp);
		}
		return (ptr);
	case DOUBLE_QUOTE:
		if (ptr = index(tmp, DOUBLE_QUOTE))
			*(ptr++) = null(char);
		alias_string = null(char *);
		if (irc_io(tmp, do_alias_string, use_input, 1))
		{
			yell("Illegal recursive edit");
			break;
		}
		expander_addition(buffer, alias_string, length, quote_em);
		new_free(&alias_string);
		return (ptr);
	case '*':
		expander_addition(buffer, args, length, quote_em);
		*args_flag = 1;
		return (ptr + 1);
	default:
		if (isdigit(c) || (c == '-') || c == '~')
		{
			*args_flag = 1;
			if (*ptr == '~')
			{
				lower = upper = LAST_ARG;
				ptr++;
			}
			else
			{
				lower = parse_number(&ptr);
				if (*ptr == '-')
				{
					ptr++;
					upper = parse_number(&ptr);
				}
				else
					upper = lower;
			}
			expander_addition(buffer, arg_number(lower, upper,
				args), length, quote_em);
			return (ptr ? ptr : empty_string);
		}
		else
		{
			char	*rest,
				c = null(char);

		/*
		 * Why use ptr+1?  Cause try to maintain backward compatability
		 * can be a pain in the butt.  Basically, we don't want any of
		 * the illegal characters in the alias, except that things like
		 * $* and $, were around first, so they must remain legal.  So
		 * we skip the first char after the $.  Does this make sense?
		 */
			/* special case for $ */
			if (*ptr == '$')
			{
				rest = ptr+1;
				c = *rest;
				*rest = null(char);
			}
			else if (rest = sindex(ptr+1, alias_illegals))
			{
				if (isalpha(*ptr) || *ptr == '_')
					while ((*rest == LEFT_BRACKET ||
					    *rest == LEFT_PAREN) &&
					    (tmp = MatchingBracket(rest+1,
					    *rest, (*rest == LEFT_BRACKET) ?
					    RIGHT_BRACKET: RIGHT_PAREN)))
						rest = tmp + 1;
				c = *rest;
				*rest = null(char);
			}
			if (tmp = parse_inline(ptr, args, args_flag))
			{
				expander_addition(buffer, tmp, length,
					quote_em);
				new_free(&tmp);
			}
			if (rest)
				*rest = c;
			return(rest);
		}
	}
	return NULL;
}


/*
 * expand_alias: Expands inline variables in the given string and returns the
 * expanded string in a new string which is malloced by expand_alias(). 
 *
 * Also unescapes anything that was quoted with a backslash
 *
 * Behaviour is modified by the following:
 *	Anything between brackets (...) {...} is left unmodified.
 *	If more_text is supplied, the text is broken up at
 *		semi-colons and returned one at a time. The unprocessed
 *		portion is written back into more_text.
 *	Backslash escapes are unescaped.
 */

char	*expand_alias(name, string, args,args_flag, more_text)
char	*name,
	*string,
	*args;
int	*args_flag;
char	**more_text;
{
	char	buffer[BIG_BUFFER_SIZE + 1],
		*ptr,
		*stuff = null(char *),
		*free_stuff;
	char	*quote_em,
		*quote_str = null(char *);
	char	ch;
	int	quote_cnt = 0;
	int	is_quote = 0;
	void	(*str_cat)();

	if (*string == '@' && more_text)
	{
		str_cat = strmcat;
		*args_flag = 1; /* Stop the @ command from auto appending */
	}
	else
		str_cat = strmcat_ue;
	malloc_strcpy(&stuff, string);
	free_stuff = stuff;
	*buffer = null(char);
	eval_args = 1;
	ptr = stuff;
	if (more_text)
		*more_text = NULL;
	while (ptr && *ptr)
	{
		if (is_quote)
		{
			is_quote = 0;
			++ptr;
			continue;
		}
		switch(*ptr)
		{
		case '$':
	/*
	 * The test here ensures that if we are in the expression
	 * evaluation command, we don't expand $. In this case we
	 * are only coming here to do command separation at ';'s.
	 * If more_text is not defined, and the first character is
	 * '@', we have come here from [] in an expression.
	 */
			if (more_text && *string == '@')
			{
				ptr++;
				break;
			}
			*(ptr++) = null(char);
			(*str_cat)(buffer, stuff, BIG_BUFFER_SIZE);
			while (*ptr == '^')
			{
				ptr++;
				if (quote_str)
					quote_str = (char *)
						new_realloc(quote_str,
						sizeof(char) * (quote_cnt + 2));
				else
					quote_str = (char *)
						new_malloc(sizeof(char) *
						(quote_cnt + 2));
				quote_str[quote_cnt++] = *(ptr++);
				quote_str[quote_cnt] = null(char);
			}
			quote_em = quote_str;
			stuff = alias_special_char(name, buffer, ptr, args,
				quote_em, args_flag);
			if (stuff)
				new_free(&quote_str);
			quote_cnt = 0;
			ptr = stuff;
			break;
		case ';':
			if (!more_text)
			{
				ptr++;
				break;
			}
			*more_text = string + (ptr - free_stuff) +1;
			*ptr = '\0'; /* To terminate the loop */
			break;
		case LEFT_PAREN:
		case LEFT_BRACE:
			ch = *ptr;
			*ptr = '\0';
			(*str_cat)(buffer, stuff, BIG_BUFFER_SIZE);
			stuff = ptr;
			*args_flag = 1;
			if (!(ptr = MatchingBracket(stuff + 1, ch,
					(ch == LEFT_PAREN) ?
					RIGHT_PAREN : RIGHT_BRACE)))
			{
				yell("Unmatched %c", ch);
				ptr = stuff + strlen(stuff+1)+1;
			}
			else
				ptr++;
			*stuff = ch;
			ch = *ptr;
			*ptr = '\0';
			strmcat(buffer, stuff, BIG_BUFFER_SIZE);
			stuff = ptr;
			*ptr = ch;
			break;
		case '\\':
			is_quote = 1;
			ptr++;
			break;
		default:
			ptr++;
			break;
		}
	}
	if (stuff)
		(*str_cat)(buffer, stuff, BIG_BUFFER_SIZE);
	ptr = null(char *);
	new_free(&free_stuff);
	malloc_strcpy(&ptr, buffer);
	if (get_int_var(DEBUG_VAR) & DEBUG_EXPANSIONS)
		yell("Expanded [%s] to [%s]",
			string, ptr);
	return (ptr);
}

/*
 * get_alias: returns the alias matching 'name' as the function value. 'args'
 * are expanded as needed, etc.  If no matching alias is found, null is
 * returned, cnt is 0, and full_name is null.  If one matching alias is
 * found, it is retuned, with cnt set to 1 and full_name set to the full name
 * of the alias.  If more than 1 match are found, null is returned, cnt is
 * set to the number of matches, and fullname is null. NOTE: get_alias()
 * mallocs the space for the full_name, but returns the actual value of the
 * alias if found! 
 */
char	*get_alias(type, name, cnt, full_name)
int	type;
char	*name,
	**full_name;
int	*cnt;
{
	Alias	*tmp;

	*full_name = null(char *);
	if ((name == null(char *)) || (*name == null(char)))
	{
		*cnt = 0;
		return (null(char *));
	}
	if (tmp = find_alias(&(alias_list[type]), name, 0, cnt))
	{
		if (*cnt < 2)
		{
			malloc_strcpy(full_name, tmp->name);
			return (tmp->stuff);
		}
	}
	return (null(char *));
}

/*
 * match_alias: this returns a list of alias names that match the given name.
 * This is used for command completion etc.  Note that the returned array is
 * malloced in this routine.  Returns null if no matches are found 
 */
char	**match_alias(name, cnt, type)
char	*name;
int	*cnt;
int	type;
{
	Alias	*tmp;
	char	**matches = null(char **);
	int	matches_size = 5;
	int	len;
	char	*last_match = null(char *);
	char	*dot;

	len = strlen(name);
	*cnt = 0;
	matches = (char	**) new_malloc(sizeof(char *) * matches_size);
	for (tmp = alias_list[type]; tmp; tmp = tmp->next)
	{
		if (strncmp(name, tmp->name, len) == 0)
		{
			if (dot = index(tmp->name+len, '.'))

			{
				if (type == COMMAND_ALIAS)
					continue;
				else
				{
					*dot = '\0';
					if (last_match && !strcmp(last_match,
							tmp->name))
					{
						*dot = '.';
						continue;
					}
				}
			}
			matches[*cnt] = null(char *);
			malloc_strcpy(&(matches[*cnt]), tmp->name);
			last_match = matches[*cnt];
			if (dot)
				*dot = '.';
			if (++(*cnt) == matches_size)
			{
				matches_size += 5;
				matches = (char	**) new_realloc(matches,
					sizeof(char *) * matches_size);
			}
		}
		else if (*cnt)
			break;
	}
	if (*cnt)
	{
		matches = (char	**) new_realloc(matches, sizeof(char *) *
			(*cnt + 1));
		matches[*cnt] = null(char *);
	}
	else
		new_free(&matches);
	return (matches);
}

/* delete_alias: The alias name is removed from the alias list. */
void	delete_alias(type, name)
int	type;
char	*name;
{
	Alias	*tmp;

	upper(name);
	if (tmp = find_alias(&(alias_list[type]), name, 1, null(int *)))
	{
		new_free(&(tmp->name));
		new_free(&(tmp->stuff));
		new_free(&tmp);
		if (type == COMMAND_ALIAS)
			say("Alias	%s removed", name);
		else
			say("Assign %s removed", name);
	}
	else
		say("No such alias: %s", name);
}

/*
 * list_aliases: Lists all aliases matching 'name'.  If name is null, all
 * aliases are listed 
 */
void	list_aliases(type, name)
int	type;
char	*name;
{
	Alias	*tmp;
	int	len;
	int	DotLoc,
		LastDotLoc = 0;
	char	*LastStructName = null(char *);

	if (type == COMMAND_ALIAS)
		say("Aliases:");
	else
		say("Assigns:");
	if (name)
	{
		upper(name);
		len = strlen(name);
	}
	else
		len = 0;
	for (tmp = alias_list[type]; tmp; tmp = tmp->next)

	{
		if (!name || !strncmp(tmp->name, name, len))
		{
			if (!index(tmp->name+len, '.'))
				say("\t%s\t%s", tmp->name, tmp->stuff);
			else

			{
				DotLoc=index(tmp->name+len, '.')-tmp->name;
				if (!LastStructName || (DotLoc != LastDotLoc) ||
				    strncmp(tmp->name, LastStructName, DotLoc))
				{
					say("\t%*.*s\t<Structure>", DotLoc,
						DotLoc, tmp->name);
					LastStructName = tmp->name;
					LastDotLoc = DotLoc;
				}
			}
		}
	}
}

/*
 * mark_alias: sets the mark field of the given alias to 'flag', and returns
 * the previous value of the mark.  If the name is not found, -1 is returned.
 * This is used to prevent recursive aliases by marking and unmarking
 * aliases, and not reusing an alias that has previously been marked.  I'll
 * explain later 
 */
int	mark_alias(name, flag)
char	*name;
int	flag;
{
	int	old_mark;
	Alias	*tmp;
	int	match;

	if (tmp = find_alias(&(alias_list[COMMAND_ALIAS]), name, 0, &match))
	{
		if (match < 2)
		{
			old_mark = tmp->mark;
		/* New handling of recursion */
			if (flag)
			{
				int	i;
				/* Count recursion */

				tmp->mark = tmp->mark + flag;
				if ((i = get_int_var(MAX_RECURSIONS_VAR)) > 1)
				{
					if (tmp->mark > i)
					{
						tmp->mark = 0;
						return(1); /* MAX exceeded. */
					}
					else return(0);
				/* In recursion but it's ok */
				}
				else
				{
					if (tmp->mark > 1)
					{
						tmp->mark = 0;
						return(1);
				/* max of 1 here.. exceeded */
					}
					else return(0);
				/* In recursion but it's ok */
				}
			}
			else
		/* Not in recursion at all */
			{
				tmp->mark = 0;
				return(old_mark);
			/* This one gets ignored anyway */
			}
		}
	}
	return (-1);
}

/*
 * execute_alias: After an alias has been identified and expanded, it is sent
 * here for proper execution.  This routine mainly prevents recursive
 * aliasing.  The name is the full name of the alias, and the alias is
 * already expanded alias (both of these parameters are returned by
 * get_alias()) 
 */
void	execute_alias(alias_name, alias, args)
char	*alias_name,
	*alias,
	*args;
{
	if (mark_alias(alias_name, 1))
		say("Maximum recursion count exceeded in: %s", alias_name);
	else
	{
		parse_line(alias_name, alias, args, 0,1);
		mark_alias(alias_name, 0);
	}
}

/*
 * save_aliases: This will write all of the aliases to the FILE pointer fp in
 * such a way that they can be read back in using LOAD or the -l switch 
 */
void	save_aliases(fp)
FILE	*fp;
{
	Alias	*tmp;

	for (tmp = alias_list[VAR_ALIAS]; tmp; tmp = tmp->next)
	{
		fprintf(fp, "ASSIGN %s %s\n", tmp->name, tmp->stuff);
	}
	for (tmp = alias_list[COMMAND_ALIAS]; tmp; tmp = tmp->next)
	{
		fprintf(fp, "ALIAS %s %s\n", tmp->name, tmp->stuff);
	}
}

/* The Built-In Alias expando functions */
static	char	*alias_line()		{ return(get_input()); }
static	char	*alias_buffer()		{ return cut_buffer; }
static	char	*alias_time()		{ return(update_clock(GET_TIME)); }
static	char	*alias_dollar()		{ return("$"); }
static	char	*alias_detected()	{ return last_notify_nick; }

static	char	*alias_nick()
{
	return(get_server_nickname(from_server));
}

static	char	*alias_away()
{
	return server_list[curr_scr_win->server].away;
}

static	char	*alias_sent_nick()
{
	return (sent_nick) ? sent_nick : empty_string;
}

static	char	*alias_recv_nick()
{
	return (recv_nick) ? recv_nick : empty_string;
}

static	char	*alias_msg_body()
{
	return (sent_body) ? sent_body : empty_string;
}

static	char	*alias_joined_nick()
{
	return (joined_nick) ? joined_nick : empty_string;
}

static	char	*alias_public_nick()
{
	return (public_nick) ? public_nick : empty_string;
}

static	char	*alias_channel()
{
	char	*tmp;

	return (tmp = get_channel_by_refnum(0)) ? tmp : "0";
}

static	char	*alias_server()
{
	return (parsing_server_index == -1) ?
		get_server_itsname(parsing_server_index) :
		(get_window_server(0) != -1) ?
			get_server_itsname(get_window_server(0)) : empty_string;
}

static	char	*alias_query_nick()
{
	char	*tmp;

	return (tmp = query_nick()) ? tmp : empty_string;
}

static	char	*alias_target()
{
	char	*tmp;

	return (tmp = get_target_by_refnum(0)) ? tmp : empty_string;
}

static	char	*alias_invite()
{
	return (invite_channel) ? invite_channel : empty_string;
}

static	char	*alias_cmdchar()
{
	static	char	thing[2];
	char	*cmdchars;

	if ((cmdchars = get_string_var(CMDCHARS_VAR)) == null(char *))
		cmdchars = DEFAULT_CMDCHARS;
	thing[0] = cmdchars[0];
	thing[1] = null(char);
	return(thing);
}

static	char	*alias_oper()
{
	return get_server_operator(from_server) ?
		get_string_var(STATUS_OPER_VAR) : empty_string;
}

static	char	*alias_chanop()
{
	char	*tmp;

	return ((tmp = get_channel_by_refnum(0)) && get_channel_oper(tmp,
			from_server)) ?
		"@" : empty_string;
}

static	char	*alias_modes()
{
	char	*tmp;

	return (tmp = get_channel_by_refnum(0)) ?
		get_channel_mode(tmp, from_server) : empty_string;
}

/*
 * alias: the /ALIAS command.  Calls the correct alias function depending on
 * the args 
 */
void	alias(command, args)
char	*command,
	*args;
{
	char	*name,
		*rest;
	int	type;
	char	*ArrayIndex;
	char	*EndIndex;

	type = *command - 48;	/*
				 * A trick!  Yes, well, what the hell.  Note
                                 * the the command part of ALIAS is "0" and
                                 * the command part of ASSIGN is "1" in the
				 * command array list
				 */
	if (name = next_arg(args, &rest))
	{
		while (ArrayIndex = index(name, '['))
		{
			*ArrayIndex++ = '.';
			if (EndIndex = MatchingBracket(ArrayIndex, LEFT_BRACKET,
					RIGHT_BRACKET))
			{
				*EndIndex++ = '\0';
				strcat(name, EndIndex);
			}
			else
				break;
		}
		if (*rest)
		{
			if (*rest == LEFT_BRACE)
			{
				char	*ptr = MatchingBracket(++rest,
						LEFT_BRACE, RIGHT_BRACE);
				if (!ptr)
				    say("Unmatched brace in ALIAS or ASSIGN");
				else if (ptr[1])
				{
					say("Junk after closing brace in ALIAS \
or ASSIGN");
				}
				else
				{
					*ptr = '\0';
					add_alias(type, name, rest);
				}
			}
			else
				add_alias(type, name, rest);
		}
		else
		{
			if (*name == '-')
			{
				if (*(name + 1))
					delete_alias(type, name + 1);
				else
					say("You must specify an alias to be \
removed");
			}
			else
				list_aliases(type, name);
		}
	}
	else
		list_aliases(type, null(char *));
}



char	*function_left(input)
char	*input;
{
	char	*result = null(char *);
	char	*count;
	int	cvalue;

	count=next_arg(input, &input);
	if (count)
		cvalue=atoi(count);
	else
		cvalue=0;
	if (strlen(input)>cvalue)
		input[cvalue]=null(char);
	malloc_strcpy(&result, input);
	return result;
}

char	*function_right(input)
char	*input;
{
	char	*result = null(char *);
	char	*count;
	int	cvalue;

	count = next_arg(input, &input);
	if (count)
		cvalue = atoi(count);
	else
		cvalue = 0;
	if (strlen(input) > cvalue)
		input += strlen(input) - cvalue;
	malloc_strcpy(&result, input);
	return result;
}

char	*function_mid(input)
char	*input;
{
	char	*result = null(char *);
	char	*index;
	int	ivalue;
	char	*count;
	int	cvalue;

	index = next_arg(input, &input);
	if (index)
		ivalue = atoi(index);
	else
		ivalue = 0;
	count = next_arg(input, &input);
	if (count)
		cvalue = atoi(count);
	else
		cvalue = 0;
	if (strlen(input) > ivalue)
		input += ivalue;
	else
		*input = null(char);
	if (strlen(input) > cvalue)
		input[cvalue] = null(char);
	malloc_strcpy(&result, input);
	return result;
}

char	*function_rand(input)
char	*input;
{
	char	*result = null(char *);
	char	tmp[40];
	int	tempin;

	sprintf(tmp, "%ld", (tempin = atol(input)) ? rand() % tempin : 0);
	malloc_strcpy(&result, tmp);
	return result;
}

char	*function_srand(input)
char	*input;
{
	char	*result = null(char *);

	srand((int) atol(input));
	malloc_strcpy(&result, empty_string);
	return result;
}

/*ARGSUSED*/
char	*function_time(input)
char	*input;
{
	char	*result = null(char *);
	Long	ltime;
	char	tmp[40];

	time(&ltime);
	sprintf(tmp, "%ld", ltime);
	malloc_strcpy(&result, tmp);
	return result;
}

char	*function_stime(input)
char	*input;
{
	char	*result = null(char *);
	Long ltime;

	ltime = atol(input);
	malloc_strcpy(&result, ctime(&ltime));
	result[strlen(result) - 1] = null(char);
	return result;
}

char	*function_tdiff(input)
char	*input;
{
	char	*result = null(char *);
	Long	ltime;
	Long	days,
		hours,
		minutes,
		seconds;
	char	tmp[80];
	char	*tstr;

	ltime = atol(input);
	seconds = ltime % 60;
	ltime = (ltime - seconds) / 60;
	minutes = ltime%60;
	ltime = (ltime - minutes) / 60;
	hours = ltime % 24;
	days = (ltime - hours) / 24;
	tstr = tmp;
	if (days)
	{
		sprintf(tstr, "%d day%s ", days, (days==1)?"":"s");
		tstr += strlen(tstr);
	}
	if (hours)
	{
		sprintf(tstr, "%d hour%s ", hours, (hours==1)?"":"s");
		tstr += strlen(tstr);
	}
	if (minutes)
	{
		sprintf(tstr, "%d minute%s ", minutes, (minutes==1)?"":"s");
		tstr += strlen(tstr);
	}
	if (seconds || (!days && !hours && !minutes))
	{
		sprintf(tstr, "%d second%s", seconds, (seconds==1)?"":"s");
		tstr += strlen(tstr);
	}
	malloc_strcpy(&result, tmp);
	return result;
}

char	*function_index(input)
char	*input;
{
	char	*result = null(char *);
	char	*schars;
	char	*iloc;
	int	ival;
	char	tmp[40];

	schars = next_arg(input, &input);
	iloc = (schars) ? sindex(input, schars) : NULL;
	ival = (iloc) ? iloc - input : -1;
	sprintf(tmp, "%d", ival);
	malloc_strcpy(&result, tmp);
	return result;
}

char	*function_rindex(input)
char	*input;
{
	char	*result = null(char *);
	char	*schars;
	char	*iloc, *liloc;
	int	ival;
	char	tmp[40];

	schars = next_arg(input, &input);
	iloc = NULL;
	if (schars)
	{
		liloc = sindex(input, schars);
		while (liloc)
			liloc = sindex((iloc = liloc) + 1, schars);
	}
	ival = (iloc) ? iloc-input : -1;
	sprintf(tmp, "%d", ival);
	malloc_strcpy(&result, tmp);
	return result;
}

char	*function_match(input)
char	*input;
{
	char	*result = null(char *);
	char	*pattern;
	char	*word;
	int	current_match;
	int	best_match = 0;
	int	match = 0;
	int	index = 0;
	char	tmp[40];

	if (pattern = next_arg(input, &input))
	{
		while(word = next_arg(input, &input))
		{
			index++;
			if ((current_match = wild_match(pattern, word))
					> best_match)
			{
				match = index;
				best_match=current_match;
			}
		}
	}
	sprintf(tmp, "%d", match);
	malloc_strcpy(&result, tmp);
	return result;
}

char	*function_rmatch(input)
char	*input;
{
	char	*result = null(char *);
	char	*pattern;
	char	*word;
	int	current_match;
	int	best_match = 0;
	int	match = 0;
	int	index = 0;
	char	tmp[40];

	if (pattern = next_arg(input, &input))
	{
		while(word = next_arg(input, &input))
		{
			index++;
			if ((current_match = wild_match(word, pattern)) >
					best_match)
			{
				match = index;
				best_match = current_match;
			}
		}
	}
	sprintf(tmp, "%d", match);
	malloc_strcpy(&result, tmp);
	return result;
}

/*ARGSUSED*/
char	*function_userhost(input)
char	*input;
{
	char	*result = null(char *);

	malloc_strcpy(&result, FromUserHost ? FromUserHost : empty_string);
	return result;
}

char	*function_strip(input)
char	*input;
{
	static	char	result[BIG_BUFFER_SIZE+1];
	char	*retval = null(char *);
	char	*chars;
	char	*cp, *dp;

	if ((chars = next_arg(input, &input)) && input)
	{
		for (cp = input, dp = result; *cp; cp++)
		{
			if (!index(chars, *cp))
				*dp++ = *cp;
		}
		*dp = '\0';
	}
	malloc_strcpy(&retval, result);
	return retval;
}

char	*function_encode(input)
unsigned char	*input;
{
	static	char	result[BIG_BUFFER_SIZE+1];
	char	*retval = null(char *);
	unsigned char	*c;
	int	i = 0;

	c = input;
	while (*c)
	{
		result[i++] = (*c >> 4) + 0x41;
		result[i++] = (*c & 0x0f) + 0x41;
		c++;
	}
	result[i] = '\0';
	malloc_strcpy(&retval, result);
	return retval;
}


char	*function_decode(input)
unsigned char	*input;
{
	static	char	result[BIG_BUFFER_SIZE+1];
	char	*retval = null(char *);
	unsigned char	*c;
	unsigned char	d,e;
	int	i = 0;

	c = input;
	while((d = *c) && (e = *(c+1)))

	{
		result[i] = ((d - 0x41) << 4) | (e - 0x41);
		c += 2;
		i++;
	}
	result[i] = '\0';
	malloc_strcpy(&retval, result);
	return retval;
}

char	*function_ischannel(input)
char	*input;
{
	char	*result = null(char *);

	malloc_strcpy(&result, is_channel(input) ? "1" : "0");
	return result;
}

char	*function_ischanop(input)
char	*input;
{
	char	*result = null(char *);
	char	*nick;
	char	*channel = NULL;

	if (!(nick = next_arg(input, &channel)))
		malloc_strcpy(&result, "0");
	else
		malloc_strcpy(&result, is_chanop(channel, nick) ? "1" : "0");
	return result;
}


char	*function_word(input)
char	*input;
{
	char	*result = null(char *);
	char	*count;
	int	cvalue;
	char	*word;

	count = next_arg(input, &input);
	if (count)
		cvalue = atoi(count);
	else
		cvalue = 0;
	if (cvalue < 0)
		malloc_strcpy(&result, empty_string);
	else
	{
		for (word = next_arg(input, &input); word && cvalue--;
				word = next_arg(input, &input))
			;
		malloc_strcpy(&result, (word) ? word : empty_string);
	}
	return result;
}


char	*function_winnum(input)
char	*input;
{
	char	*result = null(char *);
	char	tmp[10];

	if (curr_scr_win)
		sprintf(tmp, "%d", curr_scr_win->refnum);
	else
		strcpy(tmp, "-1");
	malloc_strcpy(&result, tmp);
	return result;
}

char	*function_winnam(input)
char	*input;
{
	char	*result = null(char *);

	malloc_strcpy(&result, (curr_scr_win && curr_scr_win->name) ?
		curr_scr_win->name : empty_string);
	return result;
}

char	*function_connect(input)
char	*input;
{
	char	*result = null(char *);
	char	*host;

#ifdef DAEMON_UID
	if (getuid() == DAEMON_UID)
		put_it("You are not permitted to use CONNECT()");
	else
#endif
		if (host = next_arg(input, &input))
			result = dcc_raw_connect(host, atoi(input));
	return result;
}


char	*function_listen(input)
char	*input;
{
	char	*result = null(char *);

#ifdef DAEMON_UID
	if (getuid() == DAEMON_UID)
		malloc_strcpy(&result, "0");
	else
#endif
		result = dcc_raw_listen(atoi(input));
	return result;
}

static	char	*alias_version(input)
char	*input;
{
	char *result = null(char *);
	malloc_strcpy(&result, internal_version);
	return result;
}

static	char	*alias_currdir(input)
char	*input;
{
	char	*result = null(char *);

#if defined(ISC22) || defined(SVR4) || defined(_IBMR2) || defined(POSIX)
        getcwd(buffer, BIG_BUFFER_SIZE);
#else
        getwd(buffer);
#endif
	malloc_strcpy(&result, buffer);
	return result;
}

static	char	*alias_current_numeric(input)
char	*input;
{
	char	*result = null(char *),
		number[4];
	
	sprintf(number, "%03d", -current_numeric);
	malloc_strcpy(&result, number);
	return result;
}

static	char	*alias_server_version(input)
char	*input;
{
	char	*result = null(char *),
		*s;

	malloc_strcpy(&result,
		(s = server_list[curr_scr_win->server].version_string) ?
				s : empty_string);
	return result;
}

char	*function_toupper(input)
char	*input;
{
	char	*new = null(char *),
		*ptr;

	if (!input)
		return empty_string;
	malloc_strcpy(&new, input);
	for (ptr = new; *ptr; ptr++)
		*ptr = islower(*ptr) ? toupper(*ptr) : *ptr;
	return new;
}

char	*function_tolower(input)
char	*input;
{
	char	*new = null(char *),
		*ptr;

	if (!input)
		return empty_string;
	malloc_strcpy(&new, input);
	for (ptr = new; *ptr; ptr++)
		*ptr = (isupper(*ptr)) ? tolower(*ptr) : *ptr;
	return new;
}

char	*function_curpos(input)
char	*input;
{
	char	*new = null(char *),
		pos[4];

	sprintf(pos, "%d", current_screen->buffer_pos);
	malloc_strcpy(&new, pos);
	return new;
}

char	*function_channels(input)
char	*input;
{
	Window	*window;

	if (input)
		window = isdigit(*input) ? get_window_by_refnum(atoi(input))
					 : curr_scr_win;
	else
		window = curr_scr_win;

	return create_channel_list(window);
}

char	*function_servers(input)
char	*input;
{
	return create_server_list();
}
