/*
 * alias.c Handles command aliases for irc.c 
 *
 * Written By Michael Sandrof
 *
 * Copyright (c) 1990 Michael Sandrof.
 * Copyright (c) 1991, 1992 Troy Rollo.
 * Copyright (c) 1992-2003 Matthew R. Green.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: alias.c,v 1.39 2004/04/25 09:38:59 f Exp $
 */

#include "irc.h"

#include "alias.h"
#include "dcc.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"
#include "parse.h"
#include "notify.h"
#include "ignore.h"
#include "exec.h"
#include "ircterm.h"

#include "sys/stat.h"

/*
 * define this to use the old way of managing allocations
 * inside the guts of alias handling (static buffers, not
 * malloc & realloc'ed ones).
 */
#undef USE_OLD_ALIAS_ALLOC

/**************************** Patched by Flier ******************************/
#include "myvars.h"

extern int  OpenFileRead _((char *));
extern int  OpenFileWrite _((char *, char *));
extern int  FileClose _((int));
extern int  FileEof _((int));
extern int  FileWrite _((int, char *));
extern char *FileRead _((int));
extern struct friends *CheckUsers _((char *, char *));
extern void StripAnsi _((char *, char *, int));
extern struct autobankicks *CheckABKs _((char *, char *));
extern NickList *CheckJoiners _((char *, char *, int , ChannelList *));
extern void BuildPrivs _((struct friends *, char *));
extern void PrintUsage _((char *));
extern int  ColorNumber  _((char *));
extern char *TimeStamp _((int));
#ifndef CELESCRP
extern int  TotalSendDcc _((void));
extern int  TotalQueue _((void));
extern int  is_voiced _((char *, char *));
#endif

extern char VersionInfo[];
extern char *ScrollZver1;
/****************************************************************************/

extern	int	parse_number _((char **));
static	char	*next_unit _((char *, char *, int *, int));
/**************************** PATCHED by Flier ******************************/
/*static	long	randm _((long));*/
/****************************************************************************/
static	char	*lastop _((char *));
static	char	*arg_number _((int, int, char *));
static	void	do_alias_string _((void));
static	Alias	*find_alias _((Alias **, char *, int, int *));
static	void	insert_alias _((Alias **, Alias *));
static	char	*alias_arg _((char **, u_int *));
static	char	*find_inline _((char *));
static	u_char	*built_in_alias _((int));
#ifndef USE_OLD_ALIAS_ALLOC
static	void	expander_addition _((char **, char *, int, char *));
static	char	*alias_special_char _((char *, char **, char *, char *, char *, int *));
#else /* USE_OLD_ALIAS_ALLOC */
static	void	expander_addition _((char *, char *, int, char *));
static	char	*alias_special_char _((char *, char *, char *, char *, char *, int *));
#endif /* USE_OLD_ALIAS_ALLOC */

static	u_char	*alias_detected _((void));
static	u_char	*alias_sent_nick _((void));
static	u_char	*alias_recv_nick _((void));
#ifndef LITE
static	u_char	*alias_msg_body _((void));
#endif
static	u_char	*alias_joined_nick _((void));
static	u_char	*alias_public_nick _((void));
static	u_char	*alias_dollar _((void));
static	u_char	*alias_channel _((void));
static	u_char	*alias_server _((void));
static	u_char	*alias_query_nick _((void));
static	u_char	*alias_target _((void));
static	u_char	*alias_nick _((void));
static	u_char	*alias_invite _((void));
static	u_char	*alias_cmdchar _((void));
#ifndef LITE
static	u_char	*alias_line _((void));
#endif
static	u_char	*alias_away _((void));
static	u_char	*alias_oper _((void));
static	u_char	*alias_chanop _((void));
static	u_char	*alias_modes _((void));
static	u_char	*alias_buffer _((void));
static	u_char	*alias_time _((void));
static	u_char	*alias_version _((void));
static	u_char	*alias_currdir _((void));
static	u_char	*alias_current_numeric _((void));
static	u_char	*alias_server_version _((void));

/**************************** Patched by Flier ******************************/
static	u_char	*alias_ScrollZ_version _((void));
/*static	char	*function_pattern _((void));
static	char	*function_chops _((void));
static	char	*function_chnops _((void));*/
/* Patched by Zakath */
#ifdef CELE
static	u_char	*alias_Celerity_version _((void));
#endif
/* ***************** */
/****************************************************************************/

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

static	FAR BuiltIns built_in[] =
{
	{ '.',		alias_sent_nick },
	{ ',',		alias_recv_nick },
	{ ':',		alias_joined_nick },
	{ ';',		alias_public_nick },
	{ '$',		alias_dollar },
	{ 'A',		alias_away },
#ifndef LITE
	{ 'B',		alias_msg_body },
#endif
	{ 'C',		alias_channel },
	{ 'D',		alias_detected },
/*
	{ 'E' },
	{ 'F' },
	{ 'G' },
*/
	{ 'H', 		alias_current_numeric },
	{ 'I',		alias_invite },
/**************************** PATCHED by Flier ******************************/
/*
	{ 'J' },
*/
        { 'J',          alias_ScrollZ_version },
/****************************************************************************/
	{ 'K',		alias_cmdchar },
#ifndef LITE
	{ 'L',		alias_line },
#endif
	{ '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 },
/**************************** PATCHED by Flier ******************************/
/*
	{ 'X' },
	{ 'Y' },
*/
#ifdef CELE
        { 'Y',		alias_Celerity_version },
#endif
/****************************************************************************/
	{ 'Z',		alias_time },
	{ (char) 0,	 NULL }
};

/**************************** PATCHED by Flier ******************************/
 	/*char	*command_line = (char *) 0;*/
/****************************************************************************/

	u_char	*function_left _((u_char *));
	u_char	*function_right _((u_char *));
	u_char	*function_mid _((u_char *));
	u_char	*function_rand _((u_char *));
	u_char	*function_srand _((u_char *));
	u_char	*function_time _((u_char *));
	u_char	*function_stime _((u_char *));
	u_char	*function_index _((u_char *));
	u_char	*function_rindex _((u_char *));
	u_char	*function_match _((u_char *));
	u_char	*function_rmatch _((u_char *));
	u_char	*function_userhost _((u_char *));
	u_char	*function_strip _((u_char *));
	u_char	*function_encode _((u_char *));
	u_char	*function_decode _((u_char *));
	u_char	*function_ischannel _((u_char *));
	u_char	*function_ischanop _((u_char *));
#ifdef HAVE_CRYPT
	u_char	*function_crypt _((u_char *));
#endif /* HAVE_CRYPT */
	u_char	*function_hasvoice _((u_char *));
	u_char	*function_dcclist _((u_char *));
	u_char	*function_chatpeers _((u_char *));
	u_char	*function_word _((u_char *));
	u_char	*function_winnum _((u_char *));
	u_char	*function_winnam _((u_char *));
	u_char	*function_winrows _((u_char *));
	u_char	*function_wincols _((u_char *));
	u_char	*function_winvisible _((u_char *));
	u_char	*function_winserver _((u_char *));
	u_char	*function_winservergroup _((u_char *));
	u_char	*function_connect _((u_char *));
	u_char	*function_listen _((u_char *));
	u_char	*function_tdiff _((u_char *));
	u_char	*function_toupper _((u_char *));
	u_char	*function_tolower _((u_char *));
	u_char	*function_channels _((u_char *));
	u_char	*function_servers _((u_char *));
	u_char	*function_servertype _((u_char *));
	u_char	*function_curpos _((u_char *));
	u_char	*function_onchannel _((u_char *));
	u_char	*function_pid _((u_char *));
	u_char	*function_ppid _((u_char *));
/**************************** PATCHED by Flier ******************************/
#ifndef CELESCRP
	u_char	*function_chanusers _((u_char *));
#endif
/****************************************************************************/
	u_char	*function_idle _((u_char *));
	u_char	*function_querynick _((u_char *));
#ifdef HAVE_STRFTIME
	u_char	*function_strftime _((u_char *));
#endif /* HAVE_STRFTIME */
/**************************** PATCHED by Flier ******************************/
#ifndef LITE
	u_char	*function_windows _((u_char *));
	u_char	*function_screens _((u_char *));
	u_char	*function_notify _((u_char *));
	u_char	*function_ignored _((u_char *));
	u_char	*function_urlencode _((u_char *));
	u_char	*function_shellfix _((u_char *));
	u_char	*function_filestat _((u_char *));
#endif
        u_char  *function_open _((u_char *));
        u_char  *function_close _((u_char *));
        u_char  *function_write _((u_char *));
        u_char  *function_read _((u_char *));
        u_char  *function_eof _((u_char *));
        u_char  *function_rename _((u_char *));
        u_char  *function_intuhost _((u_char *));
        u_char  *function_checkuser _((u_char *));
        u_char  *function_checkshit _((u_char *));
        u_char  *function_stripansi _((u_char *));
#ifndef CELESCRP
	u_char	*function_isvoiced _((u_char *));
        u_char	*function_uhost _((u_char *));
	u_char	*function_hhost _((u_char *));
	u_char	*function_topic _((u_char *));
	u_char	*function_strlen _((u_char *));
        u_char	*function_strnum _((u_char *));
#endif
        u_char	*function_fsize _((u_char *));
#ifndef CELESCRP
        u_char	*function_sar _((u_char *));
        u_char	*function_cdccslots _((u_char *));
        u_char	*function_cdccqslots _((u_char *));
        u_char	*function_url _((u_char *));
        u_char	*function_strstr _((u_char *));
        u_char	*function_szvar _((u_char *));
        u_char	*function_stamp _((u_char *));
#endif
#ifdef COUNTRY
        u_char	*function_country _((u_char *));
#endif
#ifdef WANTANSI
        u_char  *function_color _((u_char *));
#endif
    /*  u_char  *function_pattern _((u_char *));
        u_char  *function_chops _((u_char *));
        u_char  *function_chnops _((u_char *));  */
/****************************************************************************/

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

static BuiltInFunctions	FAR built_in_functions[] =
{
/**************************** Patched by Flier ******************************/
	{ "UH",                 function_intuhost },
#ifndef CELE
	{ "STAMP",              function_stamp },
#endif
/****************************************************************************/
	{ "LEFT",		function_left },
	{ "RIGHT",		function_right },
	{ "MID",		function_mid },
	{ "RAND",		function_rand },
	{ "SRAND",		function_srand },
	{ "TIME",		function_time },
#ifndef LITE
	{ "TDIFF",		function_tdiff },
	{ "STIME",		function_stime },
#endif
	{ "INDEX",		function_index },
	{ "RINDEX",		function_rindex },
	{ "MATCH",		function_match },
#ifndef LITE
	{ "RMATCH",		function_rmatch },
#endif
	{ "USERHOST",		function_userhost },
	{ "STRIP",		function_strip },
	{ "ENCODE",		function_encode },
	{ "DECODE",		function_decode },
/**************************** Patched by Flier ******************************/
	{ "STRIPANSI",          function_stripansi },
#if !defined(CELESCRP) && !defined(LITE)
	{ "STRSTR",             function_strstr },
	{ "STRLEN",             function_strlen },
	{ "STRNUM",             function_strnum },
        { "UHOST",              function_uhost },
        { "HHOST",              function_hhost },
#endif
        { "CHECKUSER",          function_checkuser },
	{ "CHECKSHIT",          function_checkshit },
/****************************************************************************/
	{ "ISCHANNEL",		function_ischannel },
	{ "ISCHANOP",		function_ischanop },
#if defined(HAVE_CRYPT) && !defined(LITE)
	{ "CRYPT",		function_crypt },
#endif /* HAVE_CRYPT */
/**************************** PATCHED by Flier ******************************/
#if !defined(CELESCRP) && !defined(LITE)
 	{ "HASVOICE",		function_hasvoice },
#endif
/****************************************************************************/
#ifndef LITE
 	{ "DCCLIST",		function_dcclist },
 	{ "CHATPEERS",		function_chatpeers },
#endif
	{ "WORD",		function_word },
#ifndef LITE
	{ "WINNUM",		function_winnum },
	{ "WINNAM",		function_winnam },
 	{ "WINVIS",		function_winvisible },
	{ "WINROWS",		function_winrows },
	{ "WINCOLS",		function_wincols },
 	{ "WINSERVER",		function_winserver },
#endif
 	{ "WINSERVERGROUP",	function_winservergroup },
#ifndef LITE
	{ "CONNECT",		function_connect },
	{ "LISTEN",		function_listen },
#endif
	{ "TOUPPER",		function_toupper },
	{ "TOLOWER",		function_tolower },
	{ "MYCHANNELS",		function_channels },
#ifndef LITE
	{ "MYSERVERS",		function_servers },
	{ "SERVERTYPE",		function_servertype },
	{ "CURPOS",		function_curpos },
#endif
/**************************** PATCHED by Flier ******************************/
#if defined(WANTANSI) && !defined(LITE)
        { "COLOR",              function_color },
#endif
/****************************************************************************/
	{ "ONCHANNEL",		function_onchannel },
#ifndef LITE
	{ "PID",		function_pid },
	{ "PPID",		function_ppid },
#endif
/**************************** PATCHED by Flier ******************************/
#ifndef CELESCRP
        { "CHANUSERS",		function_chanusers },
#endif
/****************************************************************************/
#ifdef HAVE_STRFTIME
	{ "STRFTIME",		function_strftime },
#endif
	{ "IDLE",		function_idle },
 	{ "QUERYNICK",		function_querynick },
/**************************** PATCHED by Flier ******************************/
#ifndef LITE
	{ "WINDOWS",		function_windows },
	{ "SCREENS",		function_screens },
	{ "NOTIFY",		function_notify },
	{ "IGNORED",		function_ignored },
	{ "URLENCODE",		function_urlencode },
	{ "SHELLFIX",		function_shellfix },
	{ "FILESTAT",		function_filestat },
#endif
	/*{ "PATTERN",            function_pattern },
	{ "CHOPS",              function_chops },
	{ "CHNOPS",             function_chnops },*/
#if !defined(CELESCRP) && !defined(LITE)
        { "SZVAR",              function_szvar },
        { "TOPIC",              function_topic },
        { "SAR",                function_sar },
#endif
#ifdef COUNTRY
        { "COUNTRY",            function_country },
#endif
#if !defined(CELESCRP) && !defined(LITE)
        { "URL",                function_url },
        { "CDCCSLOTS",          function_cdccslots },
        { "CDCCQSLOTS",         function_cdccqslots },
#endif
#ifndef LITE
        { "OPEN",               function_open },
        { "FSIZE",              function_fsize },
	{ "CLOSE",              function_close },
	{ "READ",               function_read },
	{ "WRITE",              function_write },
        { "EOF",                function_eof },
        { "RENAME",             function_rename },
#endif
/****************************************************************************/
	{ (char *) 0,		NULL }
};

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

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

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

static	int	eval_args;

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

/*
 * find_alias: looks up name in in alias list.  Returns the Alias	entry if
 * found, or null if not found.   If do_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, do_unlink, match)
	Alias	**list;
	char	*name;
	int	do_unlink;
	int	*match;
{
	Alias	*tmp,
		*last = (Alias *) 0;
	int	cmp;
	size_t	len;
 	int	(*cmp_func) _((char *, char *, size_t));

	if (match)
	{
		*match = 0;
		cmp_func = my_strnicmp;
	}
	else
 		cmp_func = (int (*) _((char *, char *, size_t))) my_stricmp;
	if (name)
	{
		len = strlen(name);
		for (tmp = *list; tmp; tmp = tmp->next)
		{
			if ((cmp = cmp_func(name, tmp->name, len)) == 0)
			{
				if (do_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 ((Alias *) 0);
}

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

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

/*
 * 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((char **) &function_stack[function_stkptr], stuff);
			return;
		}
		if ((ptr = sindex(name, alias_illegals)) != NULL)
		{
			yell("Assign names may not contain '%c'", *ptr);
			return;
		}
		say("Assign %s added", name);
	}
	if ((tmp = find_alias(&(alias_list[type]), name, 1, (int *) 0)) ==
			(Alias *) 0)
	{
		tmp = (Alias *) new_malloc(sizeof(Alias));
		if (tmp == (Alias *) 0)
		{
			yell("Couldn't allocate memory for new alias!");
			return;
		}
		tmp->name = (char *) 0;
		tmp->stuff = (char *) 0;
	}
	malloc_strcpy(&(tmp->name), name);
        malloc_strcpy(&(tmp->stuff), stuff);
	tmp->mark = 0;
	tmp->global = loading_global;
	insert_alias(&(alias_list[type]), tmp);
}

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

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

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

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

static	u_char	*
built_in_alias(c)
	int	c;
{
	BuiltIns	*tmp;
	u_char	*ret = (u_char *) 0;

 	for (tmp = built_in; tmp->name; tmp++)
 		if ((char)c == tmp->name)
		{
			malloc_strcpy((char **) &ret, (char *) 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	*nalias;
	char	*ret = NULL;
	char	*tmp;

 	if ((nalias = find_alias(&(alias_list[VAR_ALIAS]), str, 0, (int *) NULL))
			!= NULL)
	{
 		malloc_strcpy(&ret, nalias->stuff);
		return (ret);
	}
	if ((strlen(str) == 1) && (ret = (char *) built_in_alias(*str)))
		return(ret);
	if ((ret = make_string_var(str)) != NULL)
		return (ret);
#ifdef DAEMON_UID
	if (getuid() == DAEMON_UID)
	malloc_strcpy(&ret, (getuid() != DAEMON_UID) && (tmp = getenv(str)) ?
				tmp : empty_string);
#else
	malloc_strcpy(&ret, (tmp = getenv(str)) ? tmp : empty_string);
#endif /* DAEMON_UID */
	return (ret);
}

char	*
call_function(name, f_args, args, args_flag)
	char	*name,
		*f_args,
		*args;
	int	*args_flag;
{
	u_char	*tmp;
	u_char	*result = (u_char *) 0;
	char	*sub_buffer = (char *) 0;
	int	builtnum;
	char	*debug_copy = (char *) 0;
	char	*cmd = (char *) 0;

	malloc_strcpy(&cmd, name);
	upper(cmd);
	tmp = (u_char *) expand_alias((char *) 0, f_args, args, args_flag, NULL);
	if (get_int_var(DEBUG_VAR) & DEBUG_FUNCTIONS)
		malloc_strcpy(&debug_copy, (char *) tmp);
	for (builtnum = 0; built_in_functions[builtnum].name != NULL &&
			strcmp((char *) built_in_functions[builtnum].name, cmd);
			builtnum++)
		;
 	new_free(&cmd);
	if (built_in_functions[builtnum].name)
		result = built_in_functions[builtnum].func(tmp);
	else
	{
		sub_buffer = new_malloc(strlen(name)+strlen((char *) tmp)+2);
		strcpy(sub_buffer, name);
		strcat(sub_buffer, " ");
		strcat(sub_buffer, (char *) tmp);
		function_stack[++function_stkptr] = (u_char *) 0;
		parse_command(sub_buffer, 0, empty_string);
		new_free(&sub_buffer);
		eval_args=1;
		result = function_stack[function_stkptr];
		function_stack[function_stkptr] = (u_char *) 0;
		if (!result)
			malloc_strcpy((char **) &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 */
static	char	*
lastop(ptr)
	char	*ptr;
{
	while (ptr[1] && index("!=<>&^|#+/-*", ptr[1]))
		ptr++;
	return ptr;
}

#define	NU_EXPR	0
#define	NU_CONJ NU_EXPR
#define	NU_ASSN	1
#define	NU_COMP 2
#define	NU_ADD  3
#define	NU_MULT 4
#define	NU_UNIT 5
#define	NU_TERT 6
#define	NU_BITW 8

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 = (char *) 0,
		*result2 = (char *) 0;
	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, (int)'(', (int)')')))
					ptr = ptr+strlen(ptr)-1;
				else
					ptr = ptr2;
				break;
			}
			*ptr++ = '\0';
			right = ptr;
 			ptr = MatchingBracket(right, (int)LEFT_PAREN, (int)RIGHT_PAREN);
			if (ptr)
				*ptr++ = '\0';
			result1 = (char *)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, (int)'[', (int)']')))
					ptr = ptr+strlen(ptr)-1;
				else
					ptr = ptr2;
				break;
			}
			*ptr++ = '\0';
			right = ptr;
 			ptr = MatchingBracket(right, (int)LEFT_BRACKET, (int)RIGHT_BRACKET);
			if (ptr)
				*ptr++ = '\0';
			result1 = expand_alias((char *) 0, 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+1) == *(ptr))  /* index operator */
                        {
                                char *tptr;

                                *ptr++ = '\0';
				if (ptr == str + 1)	/* Its a prefix */
                                {
                                        tptr = str + 2;
                                }
				else			/* Its a postfix */
                                {
                                        tptr = str;
                                }
			    	result1 = find_inline(tptr);
                                if (!result1) 
                                        malloc_strcpy(&result1, zero);

                                {           /* This isnt supposed to be
                                                attached to the if, so
                                                dont "fix" it. */
                                        int r;
                                        r = atoi(result1);
                                        if (*ptr == '+')
                                                r++;
                                        else    r--;
                                        snprintf(tmp, sizeof tmp, "%d", r);
                                        display = window_display;
                                        window_display = 0;
                                        add_alias(VAR_ALIAS, tptr, tmp);
                                        window_display = display;
                                }
		/* A kludge?  Cheating?  Maybe.... */
				if (ptr == str + 1) 
				{
	                                *(ptr-1) = ' ';
					*ptr = ' ';
				} else            
				{
                                        if (*ptr == '+')
					        *(ptr-1) = '-';
                                        else
                                                *(ptr-1) = '+';
					*ptr = '1';
				}
                                ptr = str; 
                                new_free(&result1);
                                break;
                        }
			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;
			snprintf(tmp, sizeof tmp, "%ld", value3);
			malloc_strcpy(&result1, tmp);
			return result1;
		case '/':
		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
                        if (op == '*')
				value3 = value1 * value2;
                        else
                        {
                                if (value2)
                                    value3 = value1 % value2;
                                else
                                {
                                        value3 = 0;
                                        say("Mod by zero");
                                }
                        }
			snprintf(tmp, sizeof 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;
	/* Reworked - Jeremy Nelson, Feb 1994
	 * & or && should both be supported, each with different
	 * stages, same with || and ^^.  Also, they should be
	 * short-circuit as well.
	 */
		case '&':
			if (ptr[0] == ptr[1])
			{
				if (stage != NU_CONJ)
				{
					ptr = lastop(ptr);
					break;
				}
				*ptr = '\0';
				ptr += 2;
				result1 = next_unit(str, args, arg_flag, stage);
				value1 = atol(result1);
				if (value1)
				{
					result2 = next_unit(ptr, args, arg_flag, stage);
					value2 = atol(result2);
					value3 = value1 && value2;
				}
				else
					value3 = 0;
				new_free(&result1);
				new_free(&result2);
                                tmp[0] = '0' + (value3?1:0);
                                tmp[1] = '\0';
				malloc_strcpy(&result1, tmp);
 				return result1;
			}
			else
			{
				if (stage != NU_BITW)
				{
					ptr = lastop(ptr);
					break;
				}
				*ptr = '\0';
				ptr += 1;
				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);
				value3 = value1 & value2;
				snprintf(tmp, sizeof tmp, "%ld", value3);
				malloc_strcpy(&result1, tmp);
 				return result1;
			}
		case '|':
			if (ptr[0] == ptr[1])
			{
				if (stage != NU_CONJ)
				{
					ptr = lastop(ptr);
					break;
				}
				*ptr = '\0';
				ptr += 2;
				result1 = next_unit(str, args, arg_flag, stage);
				value1 = atol(result1);
				if (!value1)
				{
					result2 = next_unit(ptr, args, arg_flag, stage);
					value2 = atol(result2);
					value3 = value1 || value2;
				}
				else	
					value3 = 1;
				new_free(&result1);
				new_free(&result2);
				tmp[0] = '0' + (value3 ? 1 : 0);
				tmp[1] = '\0';
				malloc_strcpy(&result1, tmp);
 				return result1;
			}
			else
			{
				if (stage != NU_BITW)
				{
					ptr = lastop(ptr);
					break;
				}
				*ptr = '\0';
				ptr += 1;
				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);
				value3 = value1 | value2;
				snprintf(tmp, sizeof tmp, "%ld", value3);
				malloc_strcpy(&result1, tmp);
 				return result1;
			}
		case '^':
			if (ptr[0] == ptr[1])
			{
				if (stage != NU_CONJ)
				{
					ptr = lastop(ptr);
					break;
				}
				*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);
				value1 = value1?1:0;
				value2 = value2?1:0;
				value3 = value1 ^ value2;
				new_free(&result1);
				new_free(&result2);
				tmp[0] = '0' + (value3 ? 1 : 0);
				tmp[1] = '\0';
				malloc_strcpy(&result1, tmp);
 				return result1;
			}
			else
			{
				if (stage != NU_BITW)
				{
					ptr = lastop(ptr);
					break;
				}
				*ptr = '\0';
				ptr += 1;
				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);
				value3 = value1 ^ value2;
				snprintf(tmp, sizeof tmp, "%ld", value3);
				malloc_strcpy(&result1, tmp);
 				return result1;
			}
                case '?':
                        if (stage != NU_TERT)
                        {
				ptr = lastop(ptr);
				break;
			}
			*ptr++ = '\0';
			result1 = next_unit(str, args, arg_flag, stage);
			ptr2 = index(ptr, ':');
			*ptr2++ = '\0';
                        right = result1;
                        value1 = parse_number(&right);
                        if ((value1 == -1) && (*right == (char) 0))
                                value1 = 0;
                        if ( value1 == 0 )
                                while (isspace(*right))
                                        *(right++) = '\0';
                        if ( value1 || *right )
				result2 = next_unit(ptr, args, arg_flag, stage);
			else
				result2 = next_unit(ptr2, args, arg_flag, stage);
                        *(ptr2-1) = ':';
			new_free(&result1);
			return result2;
		case '=':
			if (ptr[1] != '=')
			{
				if (stage != NU_ASSN)
				{
					ptr = lastop(ptr);
					break;
				}
				*ptr++ = '\0';
				result1 = expand_alias((char *) 0, 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 = (char *) index(ptr, '['))
						!= NULL)
				{
				    *ArrayIndex++='.';
				    if ((EndIndex = MatchingBracket(ArrayIndex,
 				        (int)LEFT_BRACKET, (int)RIGHT_BRACKET)) != NULL)
				    {
				        *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 (!my_stricmp(result1, result2))
				malloc_strcpy(&result1, one);
			else
				malloc_strcpy(&result1, zero);
			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 = my_stricmp(result1, result2);
			if (value1)
			{
				value2 = (value1 > 0) ? 1 : 0;
				if (op == '<')
					value2 = 1 - value2;
			}
			else
				value2 = value3;
			new_free(&result2);
			snprintf(tmp, sizeof tmp, "%ld", value2);
			malloc_strcpy(&result1, tmp);
			return result1;
		case '~':
			if (ptr == str)
			{
				if (stage != NU_BITW)
					break;
				result1 = next_unit(str+1, args, arg_flag,
					stage);
				if (isdigit(*result1))
				{
					value1 = atol(result1);
					value2 = ~value1;
				}
				else
					value2 = 0;
				snprintf(tmp, sizeof tmp, "%ld", value2);
				malloc_strcpy(&result1, tmp);
				return result1;
			}
                        else
                        {
                                ptr = lastop(ptr);
                                break;
                        }
		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);
				}
				snprintf(tmp, sizeof 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 (!my_stricmp(result1, result2))
				malloc_strcpy(&result1, zero);
			else
				malloc_strcpy(&result1, one);
			new_free(&result2);
			return result1;
                case ',': 
			/*
			 * this utterly kludge code is needed (?) to get
			 * around bugs introduced from hop's patches to
			 * alias.c.  the $, variable stopped working
			 * because of this.  -mrg, july 94.
			 */
			if (ptr == str || (ptr > str && ptr[-1] == '$'))
				break;
                        if (stage != NU_EXPR)
                        {
				ptr = lastop(ptr);
				break;
			}
			*ptr++ = '\0';
			result1 = next_unit(str, args, arg_flag, stage);
			result2 = next_unit(ptr, args, arg_flag, stage);
			new_free(&result1);
			return result2;
		}
	}
	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);
			snprintf(tmp, sizeof 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 = (char *) 0;
	static	char	*last_range = (char *) 0;
	static	char	**arg_list = (char **) 0;
	static	unsigned int	*arg_list_pos = (unsigned int *) 0;
	static	unsigned int	*arg_list_end_pos = (unsigned int *) 0;
	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)) != NULL)
		{
			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((char *) arg_list, sizeof(char *) * arg_list_limit);
				arg_list_pos = (unsigned int *) new_realloc((char *) arg_list_pos, sizeof(unsigned int) * arg_list_limit);
				arg_list_end_pos = (unsigned int *) new_realloc((char *) 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]) = (char) 0;
	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" 
 */
extern	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);
}

static 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)
#ifndef USE_OLD_ALIAS_ALLOC
	char	**buff,
#else /* USE_OLD_ALIAS_ALLOC */
	char	*buff,
#endif /* USE_OLD_ALIAS_ALLOC */
		*add;
	int	length;
	char	*quote_em;
{
	char	format[40],
		buffer[BIG_BUFFER_SIZE+1],
		*ptr;

	if (length)
	{
		snprintf(format, sizeof format, "%%%d.%ds", -length, (length < 0 ? -length :
			length));
		snprintf(buffer, sizeof buffer, format, add);
		add = buffer;
	}
	if (quote_em)
	{
		ptr = double_quote(add, quote_em);
#ifndef USE_OLD_ALIAS_ALLOC
		malloc_strcat(buff, ptr);
#else /* USE_OLD_ALIAS_ALLOC */
		strmcat(buff, ptr, BIG_BUFFER_SIZE);
#endif /* USE_OLD_ALIAS_ALLOC */
		new_free(&ptr);
	}
#ifndef USE_OLD_ALIAS_ALLOC
	else if (add && *add)
	        malloc_strcat(buff, add);
#else /* USE_OLD_ALIAS_ALLOC */
	else
                if (buff)
		        strmcat(buff, add, BIG_BUFFER_SIZE);
#endif /* USE_OLD_ALIAS_ALLOC */
}

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

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


/*
 * 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 (that we are malloc_str*ing) 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, lbuf, ptr, args, quote_em, args_flag)
	char	*name;
#ifndef USE_OLD_ALIAS_ALLOC
	char	**lbuf;
#else /* USE_OLD_ALIAS_ALLOC */
 	char	*lbuf;
#endif /* USE_OLD_ALIAS_ALLOC */
	char	*ptr;
	char	*args;
	char	*quote_em;
	int	*args_flag;
{
	char	*tmp,
		c;
 	int	is_upper,
 		is_lower,
 		length;

	length = 0;
	if ((c = *ptr) == LEFT_BRACKET)
	{
		ptr++;
		if ((tmp = (char *) index(ptr, RIGHT_BRACKET)) != NULL)
		{
			*(tmp++) = (char) 0;
			length = atoi(ptr);
#ifdef USE_OLD_ALIAS_ALLOC
 			/* XXX hack to avoid core dumps */
 			if (length > ((BIG_BUFFER_SIZE * 5) / 3))
 				length = ((BIG_BUFFER_SIZE * 5) / 3);
#endif /* USE_OLD_ALIAS_ALLOC */
			ptr = tmp;
			c = *ptr;
		}
		else
		{
			say("Missing %c", RIGHT_BRACKET);
			return (ptr);
		}
	}
	tmp = ptr+1;
	switch (c)
	{
	case LEFT_PAREN:
		{
#ifndef USE_OLD_ALIAS_ALLOC
			char	*sub_buffer = (char *) 0;
#else /* USE_OLD_ALIAS_ALLOC */
			char	sub_buffer[BIG_BUFFER_SIZE+1];
#endif /* USE_OLD_ALIAS_ALLOC */

 			if ((ptr = MatchingBracket(tmp, (int)LEFT_PAREN,
 			    (int)RIGHT_PAREN)) || (ptr = (char *) index(tmp,
			    RIGHT_PAREN)))
				*(ptr++) = (char) 0;
			tmp = expand_alias((char *) 0, tmp, args, args_flag,
				NULL);
#ifndef USE_OLD_ALIAS_ALLOC
			malloc_strcpy(&sub_buffer, empty_string);
			alias_special_char((char *) 0, &sub_buffer, tmp,
#else /* USE_OLD_ALIAS_ALLOC */
			*sub_buffer = (char) 0;
			alias_special_char((char *) 0, sub_buffer, tmp,
#endif /* USE_OLD_ALIAS_ALLOC */
				args, quote_em, args_flag);
 			expander_addition(lbuf, sub_buffer, length, quote_em);
#ifndef USE_OLD_ALIAS_ALLOC
			new_free(&sub_buffer);
#endif /* not USE_OLD_ALIAS_ALLOC */
			new_free(&tmp);
			*args_flag = 1;
		}
		return (ptr);
	case '!':
		if ((ptr = (char *) index(tmp, '!')) != NULL)
			*(ptr++) = (char) 0;
		if ((tmp = do_history(tmp, empty_string)) != NULL)
		{
 			expander_addition(lbuf, tmp, length, quote_em);
			new_free(&tmp);
		}
		return (ptr);
	case LEFT_BRACE:
		if ((ptr = (char *) index(tmp, RIGHT_BRACE)) != NULL)
			*(ptr++) = (char) 0;
		if ((tmp = parse_inline(tmp, args, args_flag)) != NULL)
		{
 			expander_addition(lbuf, tmp, length, quote_em);
			new_free(&tmp);
		}
		return (ptr);
	case DOUBLE_QUOTE:
		if ((ptr = (char *) index(tmp, DOUBLE_QUOTE)) != NULL)
			*(ptr++) = (char) 0;
		alias_string = (char *) 0;
			/* XXX - the cast in the following is an ugly hack! */
 		if (irc_io(tmp, (void (*) _((u_int, char *))) do_alias_string, use_input, 1))
		{
			yell("Illegal recursive edit");
			break;
		}
 		expander_addition(lbuf, alias_string, length, quote_em);
		new_free(&alias_string);
		return (ptr);
	case '*':
 		expander_addition(lbuf, args, length, quote_em);
		*args_flag = 1;
		return (ptr + 1);
	default:
		if (isdigit(c) || (c == '-') || c == '~')
		{
			*args_flag = 1;
			if (*ptr == '~')
			{
 				is_lower = is_upper = LAST_ARG;
				ptr++;
			}
			else
			{
 				is_lower = parse_number(&ptr);
				if (*ptr == '-')
				{
					ptr++;
 					is_upper = parse_number(&ptr);
				}
				else
 					is_upper = is_lower;
			}
 			expander_addition(lbuf, arg_number(is_lower, is_upper,
				args), length, quote_em);
			return (ptr ? ptr : empty_string);
		}
		else
		{
			char	*rest,
 				c2 = (char) 0;

		/*
		 * 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;
 				c2 = *rest;
				*rest = (char) 0;
			}
			else if ((rest = sindex(ptr+1, alias_illegals)) != NULL)
			{
				if (isalpha(*ptr) || *ptr == '_')
					while ((*rest == LEFT_BRACKET ||
					    *rest == LEFT_PAREN) &&
					    (tmp = MatchingBracket(rest+1,
 					    (int)*rest, (int)(*rest == LEFT_BRACKET) ?
 					    RIGHT_BRACKET : RIGHT_PAREN)))
						rest = tmp + 1;
				c2 = *rest;
				*rest = (char) 0;
			}
			if ((tmp = parse_inline(ptr, args, args_flag)) != NULL)
			{
 				expander_addition(lbuf, tmp, length,
					quote_em);
				new_free(&tmp);
			}
			if (rest)
				*rest = c2;
			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;
{
#ifndef USE_OLD_ALIAS_ALLOC
	char	*lbuf = (char *) 0,
#else /* USE_OLD_ALIAS_ALLOC */
	char	lbuf[BIG_BUFFER_SIZE + 1],
#endif /* USE_OLD_ALIAS_ALLOC */
		*ptr,
		*stuff = (char *) 0,
		*free_stuff;
	char	*quote_em,
		*quote_str = (char *) 0;
	char	ch;
	int	quote_cnt = 0;
	int	is_quote = 0;
#ifndef USE_OLD_ALIAS_ALLOC
	void	(*str_cat) _((char **, char *));
#else /* USE_OLD_ALIAS_ALLOC */
	void	(*str_cat) _((char *, char *, size_t));
#endif /* USE_OLD_ALIAS_ALLOC */

	if (*string == '@' && more_text)
	{
#ifndef USE_OLD_ALIAS_ALLOC
		str_cat = malloc_strcat;
#else /* USE_OLD_ALIAS_ALLOC */
		str_cat = strmcat;
#endif /* USE_OLD_ALIAS_ALLOC */
		*args_flag = 1; /* Stop the @ command from auto appending */
	}
	else
#ifndef USE_OLD_ALIAS_ALLOC
		str_cat = malloc_strcat_ue;
#else /* USE_OLD_ALIAS_ALLOC */
		str_cat = strmcat_ue;
#endif /* USE_OLD_ALIAS_ALLOC */
	malloc_strcpy(&stuff, string);
	free_stuff = stuff;
#ifndef USE_OLD_ALIAS_ALLOC
	malloc_strcpy(&lbuf, empty_string);
#else /* USE_OLD_ALIAS_ALLOC */
	*lbuf = (char) 0;
#endif /* USE_OLD_ALIAS_ALLOC */
	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++) = (char) 0;
#ifndef USE_OLD_ALIAS_ALLOC
			(*str_cat)(&lbuf, stuff);
#else /* USE_OLD_ALIAS_ALLOC */
			(*str_cat)(lbuf, stuff, BIG_BUFFER_SIZE);
#endif /* USE_OLD_ALIAS_ALLOC */
			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] = (char) 0;
			}
			quote_em = quote_str;
#ifndef USE_OLD_ALIAS_ALLOC
			stuff = alias_special_char(name, &lbuf, ptr, args,
#else /* USE_OLD_ALIAS_ALLOC */
			stuff = alias_special_char(name, lbuf, ptr, args,
#endif /* USE_OLD_ALIAS_ALLOC */
				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';
#ifndef USE_OLD_ALIAS_ALLOC
			(*str_cat)(&lbuf, stuff);
#else /* USE_OLD_ALIAS_ALLOC */
			(*str_cat)(lbuf, stuff, BIG_BUFFER_SIZE);
#endif /* USE_OLD_ALIAS_ALLOC */
			stuff = ptr;
			*args_flag = 1;
 			if (!(ptr = MatchingBracket(stuff + 1, (int)ch,
 					(int)(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';
#ifndef USE_OLD_ALIAS_ALLOC
			malloc_strcat(&lbuf, stuff);
#else /* USE_OLD_ALIAS_ALLOC */
			strmcat(lbuf, stuff, BIG_BUFFER_SIZE);
#endif /* USE_OLD_ALIAS_ALLOC */
			stuff = ptr;
			*ptr = ch;
			break;
		case '\\':
			is_quote = 1;
			ptr++;
			break;
		default:
			ptr++;
			break;
		}
	}
	if (stuff)
#ifndef USE_OLD_ALIAS_ALLOC
		(*str_cat)(&lbuf, stuff);
#else /* USE_OLD_ALIAS_ALLOC */
		(*str_cat)(lbuf, stuff, BIG_BUFFER_SIZE);
#endif /* USE_OLD_ALIAS_ALLOC */
	ptr = (char *) 0;
	new_free(&free_stuff);
#ifndef USE_OLD_ALIAS_ALLOC
	ptr = lbuf;
#else /* USE_OLD_ALIAS_ALLOC */
	malloc_strcpy(&ptr, lbuf);
#endif /* USE_OLD_ALIAS_ALLOC */
	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 = (char *) 0;
	if ((name == (char *) 0) || (*name == (char) 0))
	{
		*cnt = 0;
		return ((char *) 0);
	}
	if ((tmp = find_alias(&(alias_list[type]), name, 0, cnt)) != NULL)
	{
		if (*cnt < 2)
		{
			malloc_strcpy(full_name, tmp->name);
			return (tmp->stuff);
		}
	}
	return ((char *) 0);
}

/*
 * 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 = (char **) 0;
	int	matches_size = 5;
 	size_t	len;
	char	*last_match = (char *) 0;
	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 = (char *) index(tmp->name+len, '.')) != NULL)
			{
				if (type == COMMAND_ALIAS)
					continue;
				else
				{
					*dot = '\0';
					if (last_match && !strcmp(last_match,
							tmp->name))
					{
						*dot = '.';
						continue;
					}
				}
			}
			matches[*cnt] = (char *) 0;
			malloc_strcpy(&(matches[*cnt]), tmp->name);
			last_match = matches[*cnt];
			if (dot)
				*dot = '.';
			if (++(*cnt) == matches_size)
			{
				matches_size += 5;
				matches = (char	**) new_realloc((char *) matches, sizeof(char *) * matches_size);
			}
		}
		else if (*cnt)
			break;
	}
	if (*cnt)
	{
		matches = (char	**) new_realloc((char *) matches, sizeof(char *) * (*cnt + 1));
		matches[*cnt] = (char *) 0;
	}
	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, (int *) NULL))
			!= NULL)
	{
		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;
 	size_t	len;
 	int	lastlog_level;
 	size_t	DotLoc,
		LastDotLoc = 0;
	char	*LastStructName = NULL;
	char	*s;

 	lastlog_level = message_from_level(LOG_CRAP);
	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))
		{
			s = index(tmp->name + len, '.');
			if (!s)
				say("\t%s\t%s", tmp->name, tmp->stuff);
			else
			{
				DotLoc = s - 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;
				}
			}
		}
	}
 	(void)message_from_level(lastlog_level);
}

/*
 * 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))
			!= NULL)
	{
		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, ealias, args)
	char	*alias_name,
		*ealias,
		*args;
{
	if (mark_alias(alias_name, 1))
		say("Maximum recursion count exceeded in: %s", alias_name);
	else
	{
		parse_line(alias_name, ealias, args, 0, 1, 0);
		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, do_all)
	FILE	*fp;
	int	do_all;
{
	Alias	*tmp;

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

/* The Built-In Alias expando functions */
#ifndef LITE
static	u_char	*
alias_line()
{
	return ((u_char *) get_input());
}
#endif

static	u_char	*
alias_buffer()
{
	return ((u_char *) cut_buffer);
}

static	u_char	*
alias_time()
{
	static char timestr[16];

	return ((u_char *) update_clock(timestr, 16, GET_TIME));
}

static	u_char	*
alias_dollar()
{
	return ((u_char *) "$");
}

static	u_char	*
alias_detected()
{
	return (last_notify_nick);
}

static	u_char	*
alias_nick()
{
	return (u_char *) ((from_server >= 0) ? get_server_nickname(from_server) : nickname);
}

static	u_char	*
alias_away()
{
	return (u_char *) ((from_server >= 0) ? server_list[from_server].away : empty_string);
}

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

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

#ifndef LITE
static	u_char	*
alias_msg_body()
{
	return (sent_body) ? sent_body : (u_char *) empty_string;
}
#endif

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

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

static	u_char	*
alias_channel()
{
	char	*tmp;

	return (u_char *) ((tmp = get_channel_by_refnum(0)) ? tmp : zero);
}

static	u_char	*
alias_server()
{
	return (u_char *) ((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	u_char	*
alias_query_nick()
{
	char	*tmp;

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

static	u_char	*
alias_target()
{
	char	*tmp;

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

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

static	u_char	*
alias_cmdchar()
{
	static	u_char	thing[2];
	u_char	*cmdu_chars;

	if ((cmdu_chars = get_string_var(CMDCHARS_VAR)) == (u_char *) 0)
		cmdu_chars = DEFAULT_CMDCHARS;
	thing[0] = cmdu_chars[0];
	thing[1] = (u_char) 0;
 	return (thing);
}

static	u_char	*
alias_oper()
{
	return (u_char *) ((from_server >= 0 && get_server_operator(from_server)) ?
		get_string_var(STATUS_OPER_VAR) : empty_string);
}

static	u_char	*
alias_chanop()
{
	u_char	*tmp;

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

static	u_char	*
alias_modes()
{
	u_char	*tmp;

	return (u_char *) ((from_server >= 0 && (tmp = get_channel_by_refnum(0))) ?
		get_channel_mode(tmp, from_server) : empty_string);
}

static	u_char	*
alias_version()
{
	return  (irc_version);
}

static	u_char	*
alias_currdir()
{
	static	u_char	dirbuf[1024];

	getcwd(CP(dirbuf), 1024);
	return (dirbuf);
}

static	u_char	*
alias_current_numeric()
{
	static	u_char	number[4];

	snprintf(CP(number), sizeof number, "%03d", -current_numeric);
	return (number);
}

static	u_char	*
alias_server_version()
{
	char	*s;

	return (u_char *) ((curr_scr_win->server >= 0 &&
 			(s = server_list[curr_scr_win->server].version_string)) ?
		s : empty_string);
}

/**************************** PATCHED by Flier ******************************/
static u_char *alias_ScrollZ_version() {
    static u_char tmpbuf[mybufsize / 16];

    snprintf((char *) tmpbuf, sizeof(tmpbuf), "%s [%s]", ScrollZver1, VersionInfo);
    return(tmpbuf);
}

#ifdef CELE
static u_char *alias_Celerity_version() {
    static u_char tmpbuf[32];

    snprintf((char *) tmpbuf, sizeof(tmpbuf), "%s", CelerityVersion);
    return(tmpbuf);
}
#endif
/****************************************************************************/

/*
 * alias: the /ALIAS command.  Calls the correct alias function depending on
 * the args 
 */
void	
alias(command, args, subargs)
	char	*command,
		*args,
		*subargs;
{
	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)) != NULL)
	{
		while ((ArrayIndex = (char *) index(name, '[')) != NULL)
		{
			*ArrayIndex++ = '.';
			if ((EndIndex = MatchingBracket(ArrayIndex,
 					(int)LEFT_BRACKET, (int)RIGHT_BRACKET)) != NULL)
			{
				*EndIndex++ = '\0';
				strcat(name, EndIndex);
			}
			else
				break;
		}
		if (*rest)
		{
			if (*rest == LEFT_BRACE)
			{
				char	*ptr;

				ptr = MatchingBracket(++rest,
 						(int)LEFT_BRACE, (int)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, (char *) 0);
}



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

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

u_char	*
function_right(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	char	*count;
	int	cvalue;
	int	len;

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

u_char	*
function_mid(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	char	*mid_index;
	int	ivalue;
	char	*count;
	int	cvalue;

	mid_index = next_arg((char *) input, (char **) &input);
	if (mid_index)
		ivalue = atoi(mid_index);
	else
		ivalue = 0;
	count = next_arg((char *) input, (char **) &input);
	if (count)
		cvalue = atoi(count);
	else
		cvalue = 0;
	if (ivalue >= 0 && (int) strlen(CP(input)) > ivalue)
		input += ivalue;
	else
		*input = '\0';
	if (cvalue > 0 && (int) strlen(CP(input)) > cvalue)
		input[cvalue] = '\0';
	malloc_strcpy((char **) &result, (char *) input);
	return (result);
}

/* patch from Sarayan to make $rand() better */

/**************************** PATCHED by Flier ******************************/
/*#define RAND_A 16807L
#define RAND_M 2147483647L
#define RAND_Q 127773L
#define RAND_R 2836L

static	long	
randm(l)
	long	l;
{
	static	u_long	z = 0;
	long	t;

#ifndef __MSDOS__
	if (!z)
		z = (u_long) getuid();
#endif*/ /* __MSDOS__ */
/*	if (!l)
	{
		t = RAND_A * (z % RAND_Q) - RAND_R * (z / RAND_Q);
		if (t > 0)
			z = t;
		else
			z = t + RAND_M;
		return (z >> 8) | ((z & 255) << 23);
	}
	else
	{
		if (l < 0)
#ifdef __MSDOS__
 			z = 0;
#else
  			z = (u_long) getuid();
#endif*/ /* __MSDOS__ */
/*		else
			z = l;
		return 0;
	}
}*/
/****************************************************************************/

u_char	*
function_rand(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	char	tmp[40];
/**************************** PATCHED by Flier ******************************/
	/*long	tempin;*/
        int     value = 0;
/****************************************************************************/

#ifdef _Windows
	snprintf(tmp, sizeof tmp, "%ld", random(atol(CP(input))));
#else
/**************************** PATCHED by Flier ******************************/
	/*snprintf(CP(tmp), sizeof tmp, "%ld", (tempin = my_atol(input)) ? randm(0L) % tempin : 0);*/
        if (input && * input) value = atoi(input);
        if (!value) value = 1;
        snprintf(tmp, sizeof(tmp), "%d", (input && *input) ? rand() % value : rand());
/****************************************************************************/
#endif /* _Windows */
	malloc_strcpy((char **) &result, tmp);
	return (result);
}

u_char	*
function_srand(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;

/**************************** PATCHED by Flier ******************************/
	/*if (input && *input)
		(void) randm(atol((char *) input));
	else
		(void) randm((long) time(NULL));*/
	if (input && *input) srand(atol((char *) input));
	else srand(time((time_t *) 0));
/****************************************************************************/
	malloc_strcpy((char **) &result, empty_string);
	return (result);
}

/*ARGSUSED*/
u_char	*
function_time(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	time_t	ltime;
	char	tmp[40];

	(void) time(&ltime);
	snprintf(tmp, sizeof tmp, "%ld", (long)ltime);
	malloc_strcpy((char **) &result, tmp);
	return (result);
}

#ifndef LITE
u_char	*
function_stime(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	time_t	ltime;

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

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

	ltime = atol((char *) 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)
	{
		snprintf(tstr, sizeof(tmp) - (tstr - tmp), "%ld day%s ", (long) days, (days==1)?"":"s");
		tstr += strlen(tstr);
	}
	if (hours)
	{
		snprintf(tstr, sizeof(tmp) - (tstr - tmp), "%ld hour%s ", (long) hours, (hours==1)?"":"s");
		tstr += strlen(tstr);
	}
	if (minutes)
	{
		snprintf(tstr, sizeof(tmp) - (tstr - tmp), "%ld minute%s ", (long) minutes, (minutes==1)?"":"s");
		tstr += strlen(tstr);
	}
	if (seconds || (!days && !hours && !minutes))
	{
		snprintf(tstr, sizeof(tmp) - (tstr - tmp), "%ld second%s", (long) seconds, (seconds==1)?"":"s");
		tstr += strlen(tstr);
	}
	malloc_strcpy((char **) &result, tmp);
	return (result);
}
#endif

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

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

u_char	*
function_rindex(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	char	*schars;
	char	*iloc;
	int	ival;
	char	tmp[40];

	schars = next_arg((char *) input, (char **) &input);
 	iloc = (schars) ? srindex((char *) input, schars) : NULL;
 	ival = (iloc) ? iloc - (char *) input : -1;
	snprintf(tmp, sizeof tmp, "%d", ival);
	malloc_strcpy((char **) &result, tmp);
	return (result);
}

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

	if ((pattern = next_arg((char *) input, (char **) &input)) != NULL)
	{
		while ((word = next_arg((char *) input, (char **) &input)) != NULL)
		{
			match_index++;
			if ((current_match = wild_match(pattern, word))
					> best_match)
			{
				match = match_index;
				best_match = current_match;
			}
		}
	}
	snprintf(tmp, sizeof tmp, "%d", match);
	malloc_strcpy((char **) &result, tmp);
	return (result);
}

#ifndef LITE
u_char	*
function_rmatch(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	char	*pattern;
	char	*word;
	int	current_match;
	int	best_match = 0;
	int	match = 0;
	int	rmatch_index = 0;
	char	tmp[40];

	if ((pattern = next_arg((char *) input, (char **) &input)) != NULL)
	{
		while ((word = next_arg((char *) input, (char **) &input)) != NULL)
		{
			rmatch_index++;
			if ((current_match = wild_match(word, pattern)) >
					best_match)
			{
				match = rmatch_index;
				best_match = current_match;
			}
		}
	}
	snprintf(tmp, sizeof tmp, "%d", match);
	malloc_strcpy((char **) &result, tmp);
	return (result);
}
#endif

/*ARGSUSED*/
u_char	*
function_userhost(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;

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

u_char	*
function_strip(input)
	u_char	*input;
{
	char	tmpbuf[128], *result;
	u_char	*retval = (u_char *) 0;
	char	*chars;
	char	*cp, *dp;
	size_t len = 0;

	if ((chars = next_arg((char *) input, (char **) &input)) && input)
	{
		len = strlen((char *) input);
		if (len > 127)
			result = (char *) new_malloc(len + 1);
		else
			result = tmpbuf;

		for (cp = (char *) input, dp = result; *cp; cp++)
		{
			if (!index(chars, *cp))
				*dp++ = *cp;
		}
		*dp = '\0';
	}
	malloc_strcpy((char **) &retval, result);
	if (len > 127)
		new_free(&result);	/* we could use this copy, but it might be extra-long */
	return (retval);
}

u_char	*
function_encode(input)
	u_char	*input;
{
	u_char	*result;
	u_char	*c;
	int	i = 0;

 	result = (u_char *) new_malloc(strlen((char *) input) * 2 + 1);
	for (c = input; *c; c++)
	{
		result[i++] = (*c >> 4) + 0x41;
		result[i++] = (*c & 0x0f) + 0x41;
	}
	result[i] = '\0';
 	return (result);
}


u_char	*
function_decode(input)
	u_char	*input;
{
	u_char	*result;
	u_char	*c;
	u_char	d, e;
	int	i = 0;

	c = input;
	result = (u_char *) new_malloc(strlen((char *) input) / 2 + 2);
	while((d = *c) && (e = *(c+1)))
	{
		result[i] = ((d - 0x41) << 4) | (e - 0x41);
		c += 2;
		i++;
	}
	result[i] = '\0';
	return (result);
}

u_char	*
function_ischannel(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;

	malloc_strcpy((char **) &result, is_channel((char *) input) ? one : zero);
	return (result);
}

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

	if (!(nick = next_arg((char *) input, &channel)))
		malloc_strcpy((char **) &result, zero);
	else
		malloc_strcpy((char **) &result, is_chanop(channel, nick) ? one : zero);
	return (result);
}

#ifndef LITE
#ifdef HAVE_CRYPT

u_char *
function_crypt(input)
	u_char *input;
{
	u_char	*result = (char *)0;
	char	*salt;
	char	*key = NULL;

	if (!(salt = next_arg((char *) input, &key)))
		malloc_strcpy((char **) &result, zero);
	else
		malloc_strcpy((char **) &result, (char *) crypt(key, salt));
	return (result);
}
#endif /* HAVE_CRYPT */

u_char *
function_hasvoice(input)
	u_char *input;
{
	u_char	*result = (u_char *)0;
	char	*nick;
	char	*channel = NULL;

	if (!(nick = next_arg((char *) input, &channel)))
		malloc_strcpy((char **) &result, zero);
	else
		malloc_strcpy((char **) &result, has_voice(channel, nick, from_server) ? one : zero);
	return (result);
}

u_char *
function_dcclist(Nick)
	u_char *Nick;
{
	u_char *result;
	DCC_list *Client;
	size_t len = 0;
	int i;
	
	if (!Nick)
	{
		malloc_strcpy((char **)&result, zero);
		return (result);
	}
	
	for (i = 0, Client = ClientList; Client != NULL; Client = Client->next)
		if (!my_stricmp((char *) Nick, Client->user))
			len += 3;

	result = (u_char *) new_malloc(len + 1);

	for (i = 0, Client = ClientList; Client != NULL; Client = Client->next)
		if (!my_stricmp((char *) Nick, Client->user))
		{
			int b = Client->flags;
			int a = (b & DCC_TYPES);
			
			result[i++] =
					  (a == DCC_CHAT)		? 'C' /* CHAT */
					: (a == DCC_FILEOFFER)		? 'S' /* SEND */
					: (a == DCC_FILEREAD)		? 'G' /* GET */
/**************************** PATCHED by Flier ******************************/
					/*: (a == DCC_TALK)		? 'T' *//* TALK */
					/*: (a == DCC_SUMMON)		? 'U' *//* SUMMON */
/****************************************************************************/
					: (a == DCC_RAW_LISTEN)		? 'L' /* RAW_LISTEN */
					: (a == DCC_RAW)      		? 'R' /* RAW */
					:				  'x';

			result[i++] =
					  (b & DCC_OFFER)		? 'O' /* OFFERED */
					: (b & DCC_DELETE)		? 'C' /* CLOSED */
					: (b & DCC_ACTIVE)		? 'A' /* ACTIVE */
					: (b & DCC_WAIT)		? 'W' /* WAITING */
					:                         	  'x';

			result[i++] = ' ';
		}
	
	result[i] = '\0';
	
	return (result);
}

u_char *
function_chatpeers(dummy)
	u_char *dummy;
{
	u_char *result;
	DCC_list *Client;
	int	notfirst = 0;
	size_t len = 0;
	
	/* calculate size */
	for (Client = ClientList; Client != NULL; Client = Client->next)
		if ((Client->flags & (DCC_CHAT|DCC_ACTIVE)) == (DCC_CHAT|DCC_ACTIVE))
			len += (strlen(Client->user) + 1);
	result = (u_char *) new_malloc(len);
	*result = '\0';

	for (Client = ClientList; Client != NULL; Client = Client->next)
		if ((Client->flags & (DCC_CHAT|DCC_ACTIVE)) == (DCC_CHAT|DCC_ACTIVE))
		{
			if (notfirst)
				strcat((char *) result, ",");
			else
				notfirst = 1;
			strcat((char *) result, Client->user);
		}

	return (result);
}
#endif /* LITE */

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

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

u_char	*
function_querynick(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	Window	*win;

	if (input && isdigit(*input))
		win = get_window_by_refnum((u_int)atoi((char *) input));
	else
		win = curr_scr_win;
	malloc_strcpy((char **) &result, win ? win->query_nick : "-1");
	return (result);
}

#ifndef LITE
u_char	*
function_windows(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	Win_Trav stuff;
	Window	*tmp;

	malloc_strcat((char **) &result, empty_string);
	stuff.flag = 1;
	while ((tmp = window_traverse(&stuff)))
	{
		if (tmp->name)
		{
			malloc_strcat((char **) &result, tmp->name);
			malloc_strcat((char **) &result, " ");
		}
		else
		{
			char buf[32];

			snprintf(buf, sizeof buf, "%u ", tmp->refnum);
			malloc_strcat((char **) &result, buf);
		}
	}

	return (result);
}

u_char	*
function_screens(input)
	u_char	*input;
{
	Screen	*list;
	u_char	*result = (u_char *) 0;
	char buf[32];

	malloc_strcat((char **) &result, empty_string);
	for (list = screen_list; list; list = list->next)
	{
		if (list->alive)
		{
			snprintf(buf, sizeof buf, "%u ", list->screennum);
			malloc_strcat((char **) &result, buf);
		}
	}

	return (result);
}

u_char	*
function_notify(input)
	u_char	*input;
{
	u_char	*result;

	if (input && my_stricmp(input, "gone") == 0)
		result = get_notify_list(NOTIFY_LIST_GONE);
	else if (input && my_stricmp(input, "all") == 0)
		result = get_notify_list(NOTIFY_LIST_ALL);
	else
		result = get_notify_list(NOTIFY_LIST_HERE);

	return result;
}

/*
 * $ignored(nick!user@host type) with type from:
 *	ALL MSGS PUBLIC WALLS WALLOPS INVITES NOTICES NOTES CTCP CRAP
 */
u_char	*
function_ignored(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0, *userhost, *nick;
	int type;

	if ((nick = next_arg((char *) input, (char **) &input)) != NULL)
	{
		type = get_ignore_type(input);
		if (type == 0 || type == -1 || type == IGNORE_ALL)
			goto do_zero;

		if ((userhost = index(nick, '!')))
			*userhost++ = 0;
		switch (double_ignore(nick, userhost, type))
		{
		case DONT_IGNORE:
			malloc_strcpy((char **) &result, "dont");
			break;
		case HIGHLIGHTED:
			malloc_strcpy((char **) &result, "highlighted");
			break;
		case IGNORED:
			malloc_strcpy((char **) &result, "ignored");
			break;
		default:
			goto do_zero;
		}
	}
	else
do_zero:
		malloc_strcpy((char **) &result, zero);
	return (result);
}

u_char	*
function_winserver(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	char	tmp[10];
	Window	*win;

	if (input && isdigit(*input))
		win = get_window_by_refnum((u_int)atoi((char *) input));
	else
		win = curr_scr_win;
	snprintf(tmp, sizeof tmp, "%d", win ? win->server : -1);
	malloc_strcpy((char **) &result, tmp);
	return (result);
}
#endif

u_char	*
function_winservergroup(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	char	tmp[10];
	Window	*win;

	if (input && isdigit(*input))
		win = get_window_by_refnum((u_int)atoi((char *) input));
	else
		win = curr_scr_win;
	snprintf(tmp, sizeof tmp, "%d", win ? win->server_group : -1);
	malloc_strcpy((char **) &result, tmp);
	return (result);
}

#ifndef LITE
u_char	*
function_winvisible(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	char	tmp[10];
	Window	*win;

	if (input && isdigit(*input))
		win = get_window_by_refnum((u_int)atoi((char *) input));
	else
		win = curr_scr_win;
	snprintf(tmp, sizeof tmp, "%d", win ? win->visible : -1);
	malloc_strcpy((char **) &result, tmp);
	return (result);
}

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

	snprintf(tmp, sizeof tmp, "%d", curr_scr_win ? (int)curr_scr_win->refnum : -1);
	malloc_strcpy((char **) &result, tmp);
	return (result);
}

u_char	*
function_winnam(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	Window	*win;

	if (input && isdigit(*input))
		win = get_window_by_refnum((u_int)atoi((char *) input));
	else
		win = curr_scr_win;
	malloc_strcpy((char **) &result, (win && win->name) ? win->name : empty_string);
	return (result);
}

/*
 * returns the current window's display (len) size counting for double
 * status bars, etc.. -Toasty
 */
u_char    *
function_winrows(input)
        u_char   *input;
{
	u_char	*result = (u_char *) 0;

	if (curr_scr_win)
	{
		char	tmp[10];

		snprintf(tmp, sizeof tmp, "%d", curr_scr_win->display_size);
		malloc_strcpy((char **) &result, tmp);
	}
	else
		malloc_strcpy((char **) &result, "-1");
	return (result);
}

/*
 * returns the current screen's (since all windows have the same
 * column/width) column value -Toasty
 */
u_char	*
function_wincols(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;

	if (curr_scr_win)
	{
		char	tmp[10];

		snprintf(tmp, sizeof tmp, "%d", current_screen->co);
		malloc_strcpy((char **) &result, tmp);
	}
	else
		malloc_strcpy((char **) &result, "-1");
	return (result);
}

u_char	*
function_connect(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	char	*host;

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


u_char	*
function_listen(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;

#ifdef DAEMON_UID
	if (getuid() == DAEMON_UID)
		malloc_strcpy((char **) &result, zero);
	else
#endif
		result = (u_char *) dcc_raw_listen((u_int) atoi((char *) input));
	return (result);
}
#endif /* LITE */

u_char	*
function_toupper(input)
	u_char	*input;
{
	u_char	*new = (u_char *) 0,
		*ptr;

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

u_char	*
function_tolower(input)
	u_char	*input;
{
	u_char	*new = (u_char *) 0,
		*ptr;

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

#ifndef LITE
u_char	*
function_curpos(input)
	u_char	*input;
{
	u_char	*new = (u_char *) 0,
		pos[8];

	snprintf(CP(pos), sizeof pos, "%d", current_screen->buffer_pos);
	malloc_strcpy((char **) &new, (char *) pos);
	return new;
}
#endif

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

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

	return (u_char *) create_channel_list(window);
}

#ifndef LITE
u_char	*
function_servers(input)
	u_char	*input;
{
	return (u_char *) create_server_list();
}

u_char	*
function_servertype(input)
	u_char	*input;
{
	int server;
	char *s;
	u_char *result = NULL;

	if (from_server < 0)
		server = primary_server;
	else
		server = from_server;
	if (server < 0)
		s = empty_string;
	else	
		switch (server_list[server].version) {
		case Server2_5:
			s = "IRC2.5";
			break;
		case Server2_6:
			s = "IRC2.6";
			break;
		case Server2_7:
			s = "IRC2.7";
			break;
		case Server2_8:
			s = "IRC2.8";
			break;
		case Server2_9:
			s = "IRC2.9";
			break;
		case Server2_10:
			s = "IRC2.10";
			break;
		case Server2_11:
			s = "IRC2.11";
			break;
		default:
			s = "IRC unknown";
			break;
		}

	malloc_strcpy((char **) &result, s);
	return (result);
}
#endif /* LITE */

u_char	*
function_onchannel(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	char	*nick;
	char	*channel = NULL;

 	if (from_server < 0 || !(nick = next_arg((char *) input, &channel)))
		malloc_strcpy((char **) &result, zero);
	else
		malloc_strcpy((char **) &result,
			is_on_channel(channel, from_server, nick) ? one : zero);
	return (result);
}

#ifndef LITE
u_char	*
function_pid(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	u_char	lbuf[32];	/* plenty big enough for %d */

	snprintf(CP(lbuf), sizeof lbuf, "%d", (int) getpid());
	malloc_strcpy((char **) &result, lbuf);
	return (result);
}

u_char	*
function_ppid(input)
	u_char	*input;
{
	u_char	*result = (u_char *) 0;
	u_char	lbuf[32];	/* plenty big enough for %d */

	snprintf(CP(lbuf), sizeof lbuf, "%d", (int) getppid());
	malloc_strcpy((char **) &result, lbuf);
	return (result);
}
#endif /* LITE */

/**************************** PATCHED by Flier ******************************/
/* Modified so if you call it $chanusers(#blah 1) it will return
   @nick if nick is channel operator
   +nick if nick is voiced
   %nick if nick is halfopped
   .nick otherwise */
#ifndef CELESCRP
/****************************************************************************/
u_char	*
function_chanusers(input)
	u_char	*input;
{
	ChannelList	*chan;
	NickList	*nicks;
	u_char	*result = (u_char *) 0;
	int	len = 0;
	int	notfirst = 0;
/**************************** PATCHED by Flier ******************************/
        int  nickst=0;
        char *channel;
/****************************************************************************/

/**************************** PATCHED by Flier ******************************/
	/*chan = lookup_channel((char *) input, from_server, CHAN_NOUNLINK);*/
        channel=new_next_arg((char *) input,(char **) &input);
	if (!channel) {
	    channel=get_channel_by_refnum(0);
	    if (!channel) return (u_char *) 0;
	}
        chan=lookup_channel(channel,from_server,CHAN_NOUNLINK);
        if (input && *input=='1') nickst=1;
/****************************************************************************/
	if ((ChannelList *) 0 == chan)
		return (u_char *) 0;

	for (nicks = chan->nicks; nicks; nicks = nicks->next)
/**************************** PATCHED by Flier ******************************/
        {
/****************************************************************************/
		len += (strlen(nicks->nick) + 1);
/**************************** PATCHED by Flier ******************************/
                if (nickst) len++;
        }
/****************************************************************************/
	result = (u_char *) new_malloc(len + 1);
	*result = '\0';

        for (nicks = chan->nicks; nicks; nicks = nicks->next)
	{
		if (notfirst)
			strcat((char *)result, " ");
		else
			notfirst = 1;
/**************************** PATCHED by Flier ******************************/
                if (nickst) {
                    if (nicks->chanop) strcat((char *) result,"@");
                    else if (nicks->halfop) strcat((char *) result,"%");
                    else if (nicks->hasvoice) strcat((char *) result,"+");
                    else strcat((char *) result,".");
                }
/****************************************************************************/
		strcat((char *)result, nicks->nick);
        }

        return (result);
}
/**************************** PATCHED by Flier ******************************/
#endif /* CELESCRP */
/****************************************************************************/

/*
 * strftime() patch from hari (markc@arbld.unimelb.edu.au)
 */
#ifdef HAVE_STRFTIME
u_char	*
function_strftime(input)
	u_char 	*input;
{
	char	result[128];
	time_t	ltime;
	char	*fmt = (char *) 0;

	ltime = atol((char *) input);
	fmt = (char *) input;
	/* skip the time field */
	while (isdigit(*fmt))
		++fmt; 
	if (*fmt && *++fmt)
	{
		struct tm	*tm;

		tm = localtime(&ltime);
		if (strftime(result, 128, fmt, tm))
		{
			u_char	*s = (u_char *) 0;

			malloc_strcpy((char **) &s, result);
			return s;
		}
		else
			return (u_char *) 0;
	}
	else
	{
		return (u_char *) 0;
	}
}
#endif


/*
 * idle() patch from scottr (scott.reynolds@plexus.com)
 */
u_char	*
function_idle(input)
	u_char 	*input;
{
	u_char	*result = (u_char *) 0;
	char	lbuf[20];

	snprintf(lbuf, sizeof lbuf, "%ld", (long)(time(0) - idle_time));
	malloc_strcpy((char **) &result, lbuf);
	return (result);
}

#ifndef LITE
u_char	*
function_urlencode(input)
	u_char *input;
{
	u_char	*result;
	u_char	*c;
	int	i = 0;
	
	for (c = input; *c; c++)
		if(*c == '+' || *c == '%' || *c == '&')
			i += 3;
		else
			++i;
	
	result = (u_char *) new_malloc(i + 1);

	for (i = 0, c = input; *c; c++)
	{
		if (*c == ' ')
			result[i++] = '+';
		else if (*c == '+')
		{
			result[i++] = '%';
			result[i++] = '2';
			result[i++] = 'B';
		}
		else if (*c == '&')
		{
			result[i++] = '%';
			result[i++] = '2';
			result[i++] = '6';
		}
		else
			result[i++] = *c;
	}
	result[i] = '\0';
	return (result);
}

u_char	*
function_shellfix(input)
	u_char *input;
{
	u_char	*result;
	u_char	*c;
	int	i = 2;
	
	for (c = input; *c; c++)
		if (*c == '\'')
			i += 4;
		else
			++i;
	
	result = (u_char *) new_malloc(i + 1);
	
	i = 0;
	result[i++] = '\'';
	for (c = input; *c; c++)
	{
		if (*c == '\'')
		{
			result[i++] = '\'';
			result[i++] = '\\';
			result[i++] = '\'';
			result[i++] = '\'';
		}
		else
			result[i++] = *c;
	}
	result[i++] = '\'';
	result[i] = '\0';
	return (result);
}

u_char	*
function_filestat(input)
	u_char *input;
{
	struct	stat statbuf;
	u_char	*result = (char *) 0;
	char	lbuf[40];   /* 40 should be enough */


	if (stat(input, &statbuf) == -1)
		malloc_strcpy((char **) &result, empty_string);
	else
	{
		snprintf(lbuf, sizeof lbuf, "%lu,%d,%d,%o,%s",
		    (unsigned long)statbuf.st_size,
		    (int)statbuf.st_uid,
		    (int)statbuf.st_gid,
		    statbuf.st_mode,
		    input);
		malloc_strcpy((char **) &result, lbuf);
	}
	return (result);
}
#endif

/**************************** Patched by Flier ******************************/
#ifndef LITE
u_char *function_open(words)
u_char *words;
{
    u_char *result=(char *) 0;
    char *filename=(char *) 0;
    char locbuf[mybufsize/64];

    filename=next_arg((char *) words,(char **) &words);
    if (words && *words) {
        *(words+1)='\0';
        upper(words);
        if (*words=='R') snprintf(locbuf,sizeof(locbuf),"%d",OpenFileRead(filename));
        else if (*words=='W' || *words=='A')
            snprintf(locbuf,sizeof(locbuf),"%d",OpenFileWrite(filename,words));
    }
    else strcpy(locbuf,"-1");
    malloc_strcpy((char **) &result,locbuf);
    return(result);
}

u_char *function_close(words)
u_char *words;
{
    char *args=(char *) 0;
    u_char *result=(char *) 0;
    char locbuf[mybufsize/64];

    if (words && *words) {
        args=next_arg((char *) words,(char **) &words);
        snprintf(locbuf,sizeof(locbuf),"%d",FileClose(atoi(args)));
    }
    else strcpy(locbuf,"-1");
    malloc_strcpy((char **) &result,locbuf);
    return(result);
}	

u_char *function_read(words)
u_char *words;
{
    char *args=(char *) 0;
    u_char *result=(char *) 0;

    if (words && *words) {
        args=next_arg((char *) words,(char **) &words);
        malloc_strcpy((char **) &result,FileRead(atoi(args)));
    }
    else malloc_strcpy((char **) &result,"-1");
    return(result);
}

u_char *function_write(words)
u_char *words;
{
    char *args=(char *) 0;
    u_char *result=(char *) 0;
    char locbuf[mybufsize/64];

    if (words && *words) {
        args=next_arg((char *) words,(char **) &words);
        if (words && *words) snprintf(locbuf,sizeof(locbuf),"%d",FileWrite(atoi(args),words));
        else strcpy(locbuf,"-1");
    }
    else strcpy(locbuf,"-1");
    malloc_strcpy((char **) &result,locbuf);
    return(result);
}

u_char *function_eof(words)
u_char *words;
{
    char *args=(char *) 0;
    u_char *result=(char *) 0;
    char locbuf[mybufsize/64];

    if (words && *words) {
        args=next_arg((char *) words,(char **) &words);
        snprintf(locbuf,sizeof(locbuf),"%d",FileEof(atoi(args)));
    }
    else strcpy(locbuf,"-1");
    malloc_strcpy((char **) &result,locbuf);
    return(result);
}

u_char *function_rename(words)
u_char *words;
{
    char *oldname=(char *) 0;
    char *newname=(char *) 0;
    u_char *result=(char *) 0;
    char locbuf[mybufsize/64];

    if (words && *words) {
        oldname=next_arg((char *) words,(char **) &words);
        newname=next_arg((char *) words,(char **) &words);
        if (newname && *newname) {
            snprintf(locbuf,sizeof(locbuf),"%d",rename(oldname,newname));
            malloc_strcpy((char **) &result,locbuf);
        }
        else malloc_strcpy((char **) &result,"-1");
    }
    else malloc_strcpy((char **) &result,"-1");
    return(result);
}	
#endif

u_char *function_intuhost(words)
u_char *words;
{
    u_char *result=(char *) 0;
    char *args=(char *) 0;
    char locbuf[mybufsize/2];
    NickList *joiner;

    if (words && *words) {
        args=next_arg((char *) words,(char **) &words);
        joiner=CheckJoiners(args,0,from_server,NULL);
        if (joiner && joiner->userhost) snprintf(locbuf,sizeof(locbuf),"%s!%s",joiner->nick,joiner->userhost);
        else strcpy(locbuf,"-1");
        malloc_strcpy((char **) &result,locbuf);
    }
    else malloc_strcpy((char **) &result,"-1");
    return(result);
}

u_char *function_checkuser(words)
u_char *words;
{
    u_char *result=(char *) 0;
    char *args=(char *) 0;
    char locbuf[2*mybufsize];
    struct friends *tmpfriend;

    if (words && *words) {
        args=next_arg((char *) words,(char **) &words);
        if (words && *words) {
            if ((tmpfriend=CheckUsers(args,words))) {
                *locbuf='\0';
                BuildPrivs(tmpfriend,locbuf);
                strmcat(locbuf," ",sizeof(locbuf));
                strmcat(locbuf,tmpfriend->userhost,sizeof(locbuf));
                strmcat(locbuf," ",sizeof(locbuf));
                strmcat(locbuf,tmpfriend->channels,sizeof(locbuf));
                malloc_strcpy((char **) &result,locbuf);
                return(result);
            }
        }
    }
    malloc_strcpy((char **) &result,"-1");
    return(result);
}

u_char *function_checkshit(words)
u_char *words;
{
    u_char *result=(char *) 0;
    char *args=(char *) 0;
    char locbuf[2*mybufsize];
    struct autobankicks *abk;

    if (words && *words) {
        args=next_arg((char *) words,(char **) &words);
        if (words && *words) {
            *locbuf='\0';
            if ((abk=CheckABKs(args,words))!=NULL) {
                if ((abk->shit)&1) strcat(locbuf,"K");
                if ((abk->shit)&2) strcat(locbuf,"B");
                if ((abk->shit)&4) strcat(locbuf,"I");
                if ((abk->shit)&8) strcat(locbuf,"P");
                if ((abk->shit)&16) strcat(locbuf,"D");
                strmcat(locbuf," ",sizeof(locbuf));
                strmcat(locbuf,abk->userhost,sizeof(locbuf));
                strmcat(locbuf," ",sizeof(locbuf));
                strmcat(locbuf,abk->channels,sizeof(locbuf));
                strmcat(locbuf," ",sizeof(locbuf));
                strmcat(locbuf,abk->reason,sizeof(locbuf));
            }
            else strcpy(locbuf,"-1");
            malloc_strcpy((char **) &result,locbuf);
        }
        else malloc_strcpy((char **) &result,"-1");
    }
    else malloc_strcpy((char **) &result,"-1");
    return(result);
}

u_char *function_stripansi(words)
u_char *words;
{
    u_char *result=(char *) 0;
    char locbuf[2*mybufsize];

    if (words && *words) {
        StripAnsi(words,locbuf,0);
        malloc_strcpy((char **) &result,locbuf);
    }
    else malloc_strcpy((char **) &result,empty_string);
    return(result);
}

/*u_char *function_pattern(words)
u_char *words;
{
    char    *tmpstr;
    char    *pattern;
    u_char  *result=(char *) 0;

    *tmpbuf='\0';
    if ((pattern=next_arg((char *) words,(char **) &words))) {
        while (((tmpstr=next_arg((char *) words,(char **) &words))!=NULL)) {
            if (wild_match(pattern,tmpstr)) {
                strcat(tmpbuf,tmpstr);
                strcat(tmpbuf," ");
            }
        }
        tmpbuf[strlen(tmpbuf)-1]='\0';
        malloc_strcpy((char **) &result,tmpbuf);
    } 
    else malloc_strcpy((char **) &result,empty_string);
    return(result);
}

u_char *function_chops(words)
u_char *words;
{
    u_char *nicks=(char *) 0;
    char *curr_chan;
    int  count=0;
    NickList *tmp;
    ChannelList *chan;

    *tmpbuf2='\0';
    if (words && *words) strmcpy(tmpbuf,next_arg((char *) words,(char **) &words),mybufsize/2);
    else *tmpbuf='\0';
    curr_chan=get_channel_by_refnum(0);
    if (!curr_chan) curr_chan=empty_string;
    if ((tmpbuf[0]==0) || (!strcmp(tmpbuf,"*"))) strmcpy(tmpbuf,curr_chan,mybufsize/2);
    if (tmpbuf[0]==0 || tmpbuf[0]=='0') {
        malloc_strcpy((char **) &nicks,tmpbuf2);
        return(nicks);
    }
    chan=lookup_channel(tmpbuf,curr_scr_win->server,0);
    if (!chan) {
        malloc_strcpy((char **) &nicks,tmpbuf2);
        return(nicks);
    }
    for (tmp=chan->nicks;tmp;tmp=tmp->next) {
        if (tmp->chanop) {
            count++;
            strcat(tmpbuf2,tmp->nick);
            strcat(tmpbuf2," ");
            if (count>50) {
                tmpbuf2[strlen(tmpbuf2)-1]='\0';
                malloc_strcat((char **) &nicks,tmpbuf2);
                *tmpbuf2='\0';
                count=0;
            }
        }
    }
    if (count) {
        tmpbuf2[strlen(tmpbuf2)-1]='\0';
        malloc_strcat((char **) &nicks,tmpbuf2);
    }
    return(nicks);
}

u_char *function_chnops(words)
u_char *words;
{
    u_char *nicks=(char *) 0;
    char *curr_chan;
    int  count=0;
    NickList *tmp;
    ChannelList *chan;

    *tmpbuf2='\0';
    if (words && *words) strmcpy(tmpbuf,next_arg((char *) words,(char **) &words),mybufsize/2);
    else *tmpbuf='\0';
    curr_chan=get_channel_by_refnum(0);
    if (!curr_chan) curr_chan=empty_string;
    if ((tmpbuf[0]==0) || (!strcmp(tmpbuf,"*"))) strmcpy(tmpbuf,curr_chan,mybufsize/2);
    if (tmpbuf[0]==0 || tmpbuf[0]=='0') {
        malloc_strcpy((char **) &nicks,tmpbuf2);
        return(nicks);
    }
    chan=lookup_channel(tmpbuf,curr_scr_win->server,0);
    if (!chan) {
        malloc_strcpy((char **) &nicks,tmpbuf2);
        return(nicks);
    }
    for (tmp=chan->nicks;tmp;tmp=tmp->next) {
        if (!(tmp->chanop)) {
            count++;
            strcat(tmpbuf2,tmp->nick);
            strcat(tmpbuf2," ");
            if (count>50) {
                tmpbuf2[strlen(tmpbuf2)-1]='\0';
                malloc_strcat((char **) &nicks,tmpbuf2);
                *tmpbuf2='\0';
                count=0;
            }
        }
    }
    if (count) {
        tmpbuf2[strlen(tmpbuf2)-1]='\0';
        malloc_strcat((char **) &nicks,tmpbuf2);
    }
    return(nicks);
}*/

#ifndef LITE
u_char *function_color(words)
u_char *words;
{
    u_char *result=(char *) 0;
#ifdef WANTANSI
    int  colnum=0;
    int  eventnum=0;
    char *tmp=(char *) 0;
    char *tmpstr=(char *) 0;
    char locbuf[2*mybufsize];
    
    if (words && *words) {
        tmp=next_arg((char *) words,(char **) &words);
        if (tmp && words && *words) {
            eventnum=atoi(tmp);
            colnum=atoi(words);
        }
        if (eventnum>=1 && eventnum<=NUMCMDCOLORS && colnum>=1 && colnum<=6) {
            eventnum--;
            malloc_strcpy((char **) &result,Colors[COLOFF]);
            switch (colnum) {
                case 1 : malloc_strcat((char **) &result,CmdsColors[eventnum].color1);
                break;
                case 2 : malloc_strcat((char **) &result,CmdsColors[eventnum].color2);
                break;
                case 3 : malloc_strcat((char **) &result,CmdsColors[eventnum].color3);
                break;
                case 4 : malloc_strcat((char **) &result,CmdsColors[eventnum].color4);
                break;
                case 5 : malloc_strcat((char **) &result,CmdsColors[eventnum].color5);
                break;
                case 6 : malloc_strcat((char **) &result,CmdsColors[eventnum].color6);
                break;
            }
        }
        else {
            malloc_strcpy((char **) &result,Colors[COLOFF]);
            strmcpy(locbuf,tmp,sizeof(locbuf));
            tmp=locbuf;
            for (;*tmp;tmp++)
                if (*tmp==',') {
                    *tmp++='\0';
                    if (!tmpstr) tmpstr=locbuf;
                    if ((colnum=ColorNumber(tmpstr))!=-1) 
                        malloc_strcat((char **) &result,Colors[colnum]);
                    tmpstr=tmp;
                }
            if (!tmpstr) tmpstr=locbuf;
            if (tmpstr)
                if ((colnum=ColorNumber(tmpstr))!=-1)
                    malloc_strcat((char **) &result,Colors[colnum]);
        }
    }
    else malloc_strcpy((char **) &result,empty_string);
    return(result);
#else
    malloc_strcpy((char **) &result,empty_string);
    return(result);
#endif
}
#endif /* LITE */

#if !defined(CELESCRP) && !defined(LITE)
u_char *function_uhost(input)
u_char *input;
{
    char *tmp;
    u_char *result=(char *) 0;
    
    if ((tmp=index(input,'!'))) malloc_strcpy((char **) &result,tmp+1);
    else malloc_strcpy((char **) &result,empty_string);
    return(result);
}

u_char *function_hhost(input)
u_char *input;
{
    char *tmp;
    u_char *result=(char *) 0;

    if ((tmp=index(input,'@'))) malloc_strcpy((char **) &result,tmp+1);
    else malloc_strcpy((char **) &result,empty_string);
    return(result);
}

u_char *function_topic(input)
u_char *input;
{
    u_char *result=(char *) 0;
    char *channel;
    char locbuf[2*mybufsize];
    ChannelList *chan;

    if ((channel=next_arg((char *) input,(char **) &channel)) &&
        (chan=lookup_channel(channel,from_server,0)) && chan->topicstr) {
        if (chan->topicwho) snprintf(locbuf,sizeof(locbuf),"%s %ld ",chan->topicwho,chan->topicwhen);
        else strcpy(locbuf,"unknown 0 ");
        strmcat(locbuf,chan->topicstr,sizeof(locbuf));
        malloc_strcpy((char **) &result,locbuf);
    }
    else malloc_strcpy((char **) &result,empty_string);
    return(result);
}

u_char *function_strlen(input)
u_char *input;
{
    u_char *result=(char *) 0;
    char locbuf[2*mybufsize];

    if (input && *input) {
        snprintf(locbuf,sizeof(locbuf),"%d",strlen(input));
        malloc_strcpy((char **) &result,locbuf);
    }
    else malloc_strcpy((char **) &result,"0");
    return(result);
}

u_char *function_strnum(input)
u_char *input;
{
    int  count=0;
    char *tmp=input;
    u_char *result=(char *) 0;
    char locbuf[2*mybufsize];

    if (tmp && *tmp) {
        while (*tmp) {
            while (*tmp && isspace(*tmp)) tmp++;
            if (*tmp) {
                count++;
                while (*tmp && !isspace(*tmp)) tmp++;
            }
            if (*tmp) tmp++;
        }
        snprintf(locbuf,sizeof(locbuf),"%d",count);
        malloc_strcpy((char **) &result,locbuf);
    }
    else malloc_strcpy((char **) &result,"0");
    return(result);
}
#endif /* !CELESCRP && !LITE */

#ifndef LITE
u_char *function_fsize(input)
u_char *input;
{
    int  fexists;
    char *filename=(char *) 0;
    u_char *result=(char *) 0;
    char locbuf[mybufsize/64];
    struct stat statbuf;

    if (input && *input) {
        if (!(filename=expand_twiddle(input))) malloc_strcpy(&filename,input);
        fexists=stat(filename,&statbuf);
        new_free(&filename);
        if (fexists==-1) malloc_strcpy((char **) &result,"-1");
        else {
            snprintf(locbuf,sizeof(locbuf),"%ld",statbuf.st_size);
            malloc_strcpy((char **) &result,locbuf);
        }
    }
    else malloc_strcpy((char **) &result,"-1");
    return(result);
}

/* Search and replace function --
   Usage:   $sar(command/search/replace/data)
   Commands:
		r - treat data as a variable name and 
		    return the replaced data to the variable
		g - Replace all instances, not just the first one
   The delimiter may be any character that is not a command (typically /)
   The delimiter MUST be the first character after the command
   Returns empty string on error
*/
#ifndef CELESCRP
u_char *function_sar(word)
u_char *word;
{
    int	variable=0,global=0,searchlen;
    unsigned int display=window_display;
    char delimiter;
    char *data=(char *) 0;
    char *value=(char *) 0;
    char *svalue;
    char *search=(char *) 0;
    u_char *result=(char *) 0;
    char *replace=(char *) 0;
    char *pointer=(char *) 0;
    Alias *tmp;

    while (((*word=='r') && (variable=1)) || ((*word=='g') && (global=1))) word++;
    if (!(word && *word)) {
        malloc_strcpy((char **) &result,empty_string);
        return(result);
    }
    delimiter=*word;
    search=word+1;
    if (!(replace=strchr(search,delimiter))) {
        malloc_strcpy((char **) &result,empty_string);
        return(result);
    }
    *replace++='\0';
    if (!(data=strchr(replace,delimiter))) {
        malloc_strcpy((char **) &result,empty_string);
        return(result);
    }
    *data++='\0';
    if (variable) {
        if ((tmp=find_alias(&(alias_list[VAR_ALIAS]),data,1,(int *) 0)))
            malloc_strcpy(&value,tmp->stuff);
    }
    else malloc_strcpy(&value,data);
    if (!value || !*value) {
        if (value) new_free(&value);
        malloc_strcpy((char **) &result,empty_string);
        return(result);
    }
    svalue=value;
    pointer=value;
    searchlen=strlen(search)-1;
    if (searchlen<0) searchlen=0;
    if (global) {
        while ((pointer=strstr(pointer,search))) {
            pointer[0]=pointer[searchlen]=0;
            pointer+=searchlen+1;
            malloc_strcat((char **) &result,value);
            malloc_strcat((char **) &result,replace);
            value=pointer;
            if (!*pointer) break;
        }
    } 
    else {
        if ((pointer=strstr(pointer,search))) {
            pointer[0]=pointer[searchlen]=0;
            pointer+=searchlen+1;
            malloc_strcat((char **) &result,value);
            malloc_strcat((char **) &result,replace);
            value=pointer;
        }
    }
    malloc_strcat((char **) &result,value);
    if (variable)  {
        window_display=0;
        add_alias(VAR_ALIAS,data,result);
        window_display=display;
    }
    new_free(&svalue);
    return(result);
}
#endif /* CELESCRP */
#endif /* LITE */

#ifdef COUNTRY
u_char *function_country(input)
u_char *input;
{
    int i;
    u_char *result=(char *) 0;
    char locbuf[mybufsize/64+1];
    struct domain_str {
        char *id;
        char *description;
    } domain[] = {
        { "AD", "Andorra" },
        { "AE", "United Arab Emirates" },
        { "AF", "Afghanistan" },
        { "AG", "Antigua and Barbuda" },
        { "AI", "Anguilla" },
        { "AL", "Albania" },
        { "AM", "Armenia" },
        { "AN", "Netherlands Antilles" },
        { "AO", "Angola" },
        { "AQ", "Antarctica" },
        { "AR", "Argentina" },
        { "AS", "American Samoa" },
        { "AT", "Austria" },
        { "AU", "Australia" },
        { "AW", "Aruba" },
        { "AZ", "Azerbaijan" },
        { "BA", "Bosnia and Herzegovina" },
        { "BB", "Barbados" },
        { "BD", "Bangladesh" },
        { "BE", "Belgium" },
        { "BF", "Burkina Faso" },
        { "BG", "Bulgaria" },
        { "BH", "Bahrain" },
        { "BI", "Burundi" },
        { "BJ", "Benin" },
        { "BM", "Bermuda" },
        { "BN", "Brunei Darussalam" },
        { "BO", "Bolivia" },
        { "BR", "Brazil" },
        { "BS", "Bahamas" },
        { "BT", "Bhutan" },
        { "BV", "Bouvet Island" },
        { "BW", "Botswana" },
        { "BY", "Belarus" },
        { "BZ", "Belize" },
        { "CA", "Canada" },
        { "CC", "Cocos Islands" },
        { "CF", "Central African Republic" },
        { "CG", "Congo" },
        { "CH", "Switzerland" },
        { "CI", "Cote D'ivoire" },
        { "CK", "Cook Islands" },
        { "CL", "Chile" },
        { "CM", "Cameroon" },
        { "CN", "China" },
        { "CO", "Colombia" },
        { "CR", "Costa Rica" },
        { "CS", "Former Czechoslovakia" },
        { "CU", "Cuba" },
        { "CV", "Cape Verde" },
        { "CX", "Christmas Island" },
        { "CY", "Cyprus" },
        { "CZ", "Czech Republic" },
        { "DE", "Germany" },
        { "DJ", "Djibouti" },
        { "DK", "Denmark" },
        { "DM", "Dominica" },
        { "DO", "Dominican Republic" },
        { "DZ", "Algeria" },
        { "EC", "Ecuador" },
        { "EE", "Estonia" },
        { "EG", "Egypt" },
        { "EH", "Western Sahara" },
        { "ER", "Eritrea" },
        { "ES", "Spain" },
        { "ET", "Ethiopia" },
        { "FI", "Finland" },
        { "FJ", "Fiji" },
        { "FK", "Falkland Islands" },
        { "FM", "Micronesia" },
        { "FO", "Faroe Islands" },
        { "FR", "France" },
        { "FX", "France, Metropolitan" },
        { "GA", "Gabon" },
        { "GB", "Great Britain" },
        { "GD", "Grenada" },
        { "GE", "Georgia" },
        { "GF", "French Guiana" },
        { "GH", "Ghana" },
        { "GI", "Gibraltar" },
        { "GL", "Greenland" },
        { "GM", "Gambia" },
        { "GN", "Guinea" },
        { "GP", "Guadeloupe" },
        { "GQ", "Equatorial Guinea" },
        { "GR", "Greece" },
        { "GS", "S. Georgia and S. Sandwich Isls." },
        { "GT", "Guatemala" },
        { "GU", "Guam" },
        { "GW", "Guinea-Bissau" },
        { "GY", "Guyana" },
        { "HK", "Hong Kong" },
        { "HM", "Heard and McDonald Islands" },
        { "HN", "Honduras" },
        { "HR", "Croatia" },
        { "HT", "Haiti" },
        { "HU", "Hungary" },
        { "ID", "Indonesia" },
        { "IE", "Ireland" },
        { "IL", "Israel" },
        { "IN", "India" },
        { "IO", "British Indian Ocean Territory" },
        { "IQ", "Iraq" },
        { "IR", "Iran" },
        { "IS", "Iceland" },
        { "IT", "Italy" },
        { "JM", "Jamaica" },
        { "JO", "Jordan" },
        { "JP", "Japan" },
        { "KE", "Kenya" },
        { "KG", "Kyrgyzstan" },
        { "KH", "Cambodia" },
        { "KI", "Kiribati" },
        { "KM", "Comoros" },
        { "KN", "St. Kitts and Nevis" },
        { "KP", "North Korea" },
        { "KR", "South Korea" },
        { "KW", "Kuwait" },
        { "KY", "Cayman Islands" },
        { "KZ", "Kazakhstan" },
        { "LA", "Laos" },
        { "LB", "Lebanon" },
        { "LC", "Saint Lucia" },
        { "LI", "Liechtenstein" },
        { "LK", "Sri Lanka" },
        { "LR", "Liberia" },
        { "LS", "Lesotho" },
        { "LT", "Lithuania" },
        { "LU", "Luxembourg" },
        { "LV", "Latvia" },
        { "LY", "Libya" },
        { "MA", "Morocco" },
        { "MC", "Monaco" },
        { "MD", "Moldova" },
        { "MG", "Madagascar" },
        { "MH", "Marshall Islands" },
        { "MK", "Macedonia" },
        { "ML", "Mali" },
        { "MM", "Myanmar" },
        { "MN", "Mongolia" },
        { "MO", "Macau" },
        { "MP", "Northern Mariana Islands" },
        { "MQ", "Martinique" },
        { "MR", "Mauritania" },
        { "MS", "Montserrat" },
        { "MT", "Malta" },
        { "MU", "Mauritius" },
        { "MV", "Maldives" },
        { "MW", "Malawi" },
        { "MX", "Mexico" },
        { "MY", "Malaysia" },
        { "MZ", "Mozambique" },
        { "NA", "Namibia" },
        { "NC", "New Caledonia" },
        { "NE", "Niger" },
        { "NF", "Norfolk Island" },
        { "NG", "Nigeria" },
        { "NI", "Nicaragua" },
        { "NL", "Netherlands" },
        { "NO", "Norway" },
        { "NP", "Nepal" },
        { "NR", "Nauru" },
        { "NT", "Neutral Zone" },
        { "NU", "Niue" },
        { "NZ", "New Zealand" },
        { "OM", "Oman" },
        { "PA", "Panama" },
        { "PE", "Peru" },
        { "PF", "French Polynesia" },
        { "PG", "Papua New Guinea" },
        { "PH", "Philippines" },
        { "PK", "Pakistan" },
        { "PL", "Poland" },
        { "PM", "St. Pierre and Miquelon" },
        { "PN", "Pitcairn" },
        { "PR", "Puerto Rico" },
        { "PT", "Portugal" },
        { "PW", "Palau" },
        { "PY", "Paraguay" },
        { "QA", "Qatar" },
        { "RE", "Reunion" },
        { "RO", "Romania" },
        { "RU", "Russian Federation" },
        { "RW", "Rwanda" },
        { "SA", "Saudi Arabia" },
        { "SB", "Solomon Islands" },
        { "SC", "Seychelles" },
        { "SD", "Sudan" },
        { "SE", "Sweden" },
        { "SG", "Singapore" },
        { "SH", "St. Helena" },
        { "SI", "Slovenia" },
        { "SJ", "Svalbard and Jan Mayen Islands" },
        { "SK", "Slovak Republic" },
        { "SL", "Sierra Leone" },
        { "SM", "San Marino" },
        { "SN", "Senegal" },
        { "SO", "Somalia" },
        { "SR", "Suriname" },
        { "ST", "Sao Tome and Principe" },
        { "SU", "Former USSR" },
        { "SV", "El Salvador" },
        { "SY", "Syria" },
        { "SZ", "Swaziland" },
        { "TC", "Turks and Caicos Islands" },
        { "TD", "Chad" },
        { "TF", "French Southern Territories" },
        { "TG", "Togo" },
        { "TH", "Thailand" },
        { "TJ", "Tajikistan" },
        { "TK", "Tokelau" },
        { "TM", "Turkmenistan" },
        { "TN", "Tunisia" },
        { "TO", "Tonga" },
        { "TP", "East Timor" },
        { "TR", "Turkey" },
        { "TT", "Trinidad and Tobago" },
        { "TV", "Tuvalu" },
        { "TW", "Taiwan" },
        { "TZ", "Tanzania" },
        { "UA", "Ukraine" },
        { "UG", "Uganda" },
        { "UK", "United Kingdom" },
        { "UM", "US Minor Outlying Islands" },
        { "US", "United States of America" },
        { "UY", "Uruguay" },
        { "UZ", "Uzbekistan" },
        { "VA", "Vatican City State" },
        { "VC", "St. Vincent and the grenadines" },
        { "VE", "Venezuela" },
        { "VG", "British Virgin Islands" },
        { "VI", "US Virgin Islands" },
        { "VN", "Vietnam" },
        { "VU", "Vanuatu" },
        { "WF", "Wallis and Futuna Islands" },
        { "WS", "Samoa" },
        { "YE", "Yemen" },
        { "YT", "Mayotte" },
        { "YU", "Yugoslavia" },
        { "ZA", "South Africa" },
        { "ZM", "Zambia" },
        { "ZR", "Zaire" },
        { "ZW", "Zimbabwe" },
        { "COM", "Commercial" },
        { "EDU", "Educational Institution" },
        { "GOV", "Government" },
        { "INT", "International" },
        { "MIL", "Military" },
        { "NET", "Network" },
        { "ORG", "Non-Profit Organization" },
        { "RPA", "Old School ARPAnet" },
        { "ATO", "Nato Fiel" },
        { "MED", "United States Medical" },
        { "ARPA", "Reverse DNS" },
        { NULL, NULL }
    };

    if (input && *input) {
        strmcpy(locbuf,input,mybufsize/64);
        upper(locbuf);
        for (i=0;domain[i].id;i++)
            if (!strcmp(domain[i].id,locbuf)) {
                malloc_strcpy((char **) &result,domain[i].description);
                return(result);
            }
    }
    malloc_strcpy((char **) &result,"Unknown");
    return(result);
}
#endif

#if !defined(CELESCRP) && !defined(LITE)
u_char *function_cdccslots(input)
u_char *input;
{
    int  slots=0;
    u_char *result=(char *) 0;
    char locbuf[mybufsize/64];

    slots=CdccLimit-TotalSendDcc();
    if (slots<0) slots=0;
    snprintf(locbuf,sizeof(locbuf),"%d",slots);
    malloc_strcpy((char **) &result,locbuf);
    return(result);
}

u_char *function_cdccqslots(input)
u_char *input;
{
    int  slots=0;
    u_char *result=(char *) 0;
    char locbuf[mybufsize/64];

    if (CdccQueueLimit) slots=CdccQueueLimit-TotalQueue();
    else slots=10;
    if (slots<0) slots=0;
    snprintf(locbuf,sizeof(locbuf),"%d",slots);
    malloc_strcpy((char **) &result,locbuf);
    return(result);
}

u_char *function_url(input)
u_char *input;
{
    int urlnum=99;
    int showtarget=0;
    u_char *result=(char *) 0;
    struct urlstr *tmpurl=urllist;

    if (!tmpurl) malloc_strcpy((char **) &result,"0");
    else {
        if (input && *input) {
            char *tmpstr=new_next_arg(input,(char **)&input);
            urlnum=atoi(tmpstr);
            if (input && *input) showtarget=1;
        }
        while (tmpurl && urlnum--) tmpurl=tmpurl->next;
        if (tmpurl) {
            malloc_strcpy((char **) &result,tmpurl->urls);
            if (showtarget && tmpurl->source) {
                malloc_strcat((char **) &result," ");
                malloc_strcat((char **) &result,tmpurl->source);
            }
        }
        else malloc_strcpy((char **) &result,empty_string);
    }
    return(result);
}

u_char *function_strstr(input)
u_char *input;
{
    u_char *result=(char *) 0;
    char *tmpstr=(char *) 0;
    char *findstr=(char *) 0;
    char locbuf[2*mybufsize+1];

    if (input && *input) {
        strmcpy(locbuf,&input[1],2*mybufsize);
        if ((tmpstr=index(locbuf,*input))) {
            *tmpstr++='\0';
            if (*tmpstr) tmpstr++;
            else tmpstr=(char *) 0;
        }
        if (tmpstr) findstr=strstr(locbuf,tmpstr);
    }
    if (!findstr) findstr=empty_string;
    malloc_strcpy((char **) &result,findstr);
    return(result);
}

u_char *function_szvar(input)
u_char *input;
{
    int  i;
    u_char *result=(char *) 0;
    char locbuf[mybufsize+1];
    struct commands {
        char *command;
        int  type;  /* 1=numeric   2=string   3=on channels/off */
        int  *ivar;
        char **svar;
    } command_list[]= {
        { "EXTMES"         , 1, &ExtMes          , NULL                   },
        { "NHPROT"         , 3, &NHProt          , &NHProtChannels        },
        /* defban is character that should be returned as string */
        { "BANTYPE"        , 2, NULL             , NULL                   },
        /* Cdcc limit should return CdccLimit and CdccQueueLimit */
        { "CDCCLIMIT"      , 1, &CdccLimit       , NULL                   },
        { "CDCCIDLE"       , 1, &CdccIdle        , NULL                   },
        { "CDCCAUTOGET"    , 1, &AutoGet         , NULL                   },
        { "CDCCSECURE"     , 1, &Security        , NULL                   },
        { "CDCCPTIME"      , 1, &PlistTime       , NULL                   },
        { "CDCCNTIME"      , 1, &NlistTime       , NULL                   },
        { "CDCCCHANNELS"   , 2, NULL             , &CdccChannels          },
        { "CDCCLONGSTATUS" , 1, &LongStatus      , NULL                   },
        { "CDCCOVERWRITE"  , 1, &CdccOverWrite   , NULL                   },
        { "CDCCSTATUS"     , 1, &ShowDCCStatus   , NULL                   },
        { "CDCCSTATS"      , 1, &CdccStats       , NULL                   },
        { "CDCCWARNING"    , 1, &DCCWarning      , NULL                   },
        { "CDCCULDIR"      , 2, NULL             , &CdccUlDir             },
        { "CDCCDLDIR"      , 2, NULL             , &CdccDlDir             },
        { "DEOPS"          , 1, &DeopSensor      , NULL                   },
        { "KICKS"          , 1, &KickSensor      , NULL                   },
        { "NICKS"          , 1, &NickSensor      , NULL                   },
        { "DEOPT"          , 1, &MDopTimer       , NULL                   },
        { "KICKT"          , 1, &KickTimer       , NULL                   },
        { "NICKT"          , 1, &NickTimer       , NULL                   },
        { "AWAYT"          , 1, &AutoAwayTime    , NULL                   },
        { "IGTIME"         , 1, &IgnoreTime      , NULL                   },
#ifdef EXTRAS
        { "IDLETIME"       , 1, &IdleTime        , NULL                   },
#endif
        { "NPROT"          , 3, &NickWatch       , &NickWatchChannels     },
        { "DPROT"          , 3, &MDopWatch       , &MDopWatchChannels     },
        { "KPROT"          , 3, &KickWatch       , &KickWatchChannels     },
        { "AREJOIN"        , 3, &AutoRejoin      , &AutoRejoinChannels    },
        { "AJOIN"          , 3, &AutoJoinOnInv   , &AutoJoinChannels      },
#if defined(EXTRAS) || defined(FLIER)
        { "AUTOINV"        , 3, &AutoInv         , &AutoInvChannels       },
#endif
        /* Floodp should return FloodProt, FloodMessages and FloodSeconds */
        { "FLOODP"         , 1, &FloodProt       , NULL                   },
        { "SERVNOTICE"     , 1, &ServerNotice    , NULL                   },
        { "CTCPCLOAK"      , 1, &CTCPCloaking    , NULL                   },
        { "FAKE"           , 3, &ShowFakes       , &ShowFakesChannels     },
        { "SHOWAWAY"       , 3, &ShowAway        , &ShowAwayChannels      },
        { "KICKOPS"        , 3, &KickOps         , &KickOpsChannels       },
        { "KICKONFLOOD"    , 3, &KickOnFlood     , &KickOnFloodChannels   },
        { "SHOWNICK"       , 1, &ShowNick        , NULL                   },
        { "KICKONBAN"      , 3, &KickOnBan       , &KickOnBanChannels     },
        { "REPWORD"        , 2, NULL             , &AutoReplyBuffer       },
        { "ORIGNICK"       , 3, &OrigNickChange  , &OrigNick              },
        { "NTFYMODE"       , 1, &NotifyMode      , NULL                   },
        { "URLCATCH"       , 1, &URLCatch        , NULL                   },
        { "EGO"            , 1, &Ego             , NULL                   },
        { "AUTOCOMPL"      , 3, &AutoNickCompl   , &AutoReplyString       },
        { "BITCH"          , 3, &Bitch           , &BitchChannels         },
        { "FRLIST"         , 3, &FriendList      , &FriendListChannels    },
#ifdef EXTRAS
        { "IDLEKICK"       , 3, &IdleKick        , &IdleKickChannels      },
        { "CHSIGNOFF"      , 3, &ShowSignoffChan , &SignoffChannels       },
#endif
        { "COMPRESS"       , 3, &CompressModes   , &CompressModesChannels },
        { "STAMP"          , 1, &Stamp           , NULL                   },
        { "ARINWIN"        , 1, &ARinWindow      , NULL                   },
        { "BKLIST"         , 3, &BKList          , &BKChannels            },
#ifdef WANTANSI
        { "MIRC"           , 1, &DisplaymIRC     , NULL                   },
#endif
#ifdef EXTRAS
        { "SHOWSIGN"       , 1, &ShowSignAllChan , NULL                   },
#endif
        { "EXTPUB"         , 1, &ExtPub          , NULL                   },
        { "CHANLOG"        , 3, &ChanLog         , &ChanLogChannels       },
        { "CHANLOGDEST"    , 2, NULL             , &ChanLogDir            },
        { "CHANLOGPREFIX"  , 2, NULL             , &ChanLogPrefix         },
        { "CHANLOGPOST"    , 2, NULL             , &ChanLogPostfix        },
#ifdef EXTRAS
        { "NICKCHAN"       , 1, &ShowNickAllChan , NULL                   },
#endif
        { "AWAYENCR"       , 1, &AwayEncrypt     , NULL                   },
        { NULL             , 0, NULL             , NULL                   }
    };

    if (input && *input) {
        strmcpy(locbuf,(char *) input,mybufsize);
        upper(locbuf);
        for (i=0;command_list[i].command;i++)
            if (!strcmp(command_list[i].command,locbuf)) break;
        if (!(command_list[i].command)) {
            malloc_strcpy((char **) &result,empty_string);
            return(result);
        }
        *locbuf='\0';
        switch (command_list[i].type) {
            case 1:
                /* Cdcc limit is special case */
                if (!strcmp(command_list[i].command,"CDCCLIMIT"))
                    snprintf(locbuf,sizeof(locbuf),"%d %d",*(command_list[i].ivar),CdccQueueLimit);
                /* Floodp is special case */
                else if (!strcmp(command_list[i].command,"FLOODP"))
                    snprintf(locbuf,sizeof(locbuf),"%d %d %d",
                            *(command_list[i].ivar),FloodMessages,FloodSeconds);
                else snprintf(locbuf,sizeof(locbuf),"%d",*(command_list[i].ivar));
                break;
            case 2:
                /* Bantype is special case */
                if (!strcmp(command_list[i].command,"BANTYPE")) {
                    *locbuf=tolower(defban);
                    *(locbuf+1)='\0';
                }
                else if (*(command_list[i].svar)) strmcpy(locbuf,*(command_list[i].svar),sizeof(locbuf));
                break;
            case 3:
                if (*(command_list[i].ivar) && *(command_list[i].svar)) {
                    snprintf(locbuf,sizeof(locbuf),"%d %s",*(command_list[i].ivar),*(command_list[i].svar));
                    /* Orignick is special case */
                    if (OrigNickQuiet && !strcmp(command_list[i].command,"ORIGNICK"))
                        *locbuf='2';
                }
                else strcpy(locbuf,"0");
                /* Nhprot is special case */
                if (!strcmp(command_list[i].command,"NHPROT"))
                    snprintf(locbuf+strlen(locbuf),sizeof(locbuf)-strlen(locbuf)," %s",
                            NHDisp == 0 ? "q" : (NHDisp == 1 ? "m" : "f"));
                break;
        }
        if (*locbuf) {
            malloc_strcpy((char **) &result,locbuf);
            return(result);
        }
    }
    malloc_strcpy((char **) &result,empty_string);
    return(result);
}
#endif /* !CELESCRP && !LITE */

u_char *function_stamp(input)
u_char *input;
{
    u_char *result = NULL;
    char *stampbuf = TimeStamp(0);

    if (stampbuf) malloc_strcpy((char **) &result, stampbuf);
    else malloc_strcpy((char **) &result,empty_string);
    return(result);
}

/* Removes all aliases */
void DumpAliases(type)
int type;
{
    Alias *tmp;
    Alias *tmpdel;

    for (tmp=alias_list[type];tmp;) {
        tmpdel=tmp;
        tmp=tmp->next;
        new_free(&(tmpdel->name));
        new_free(&(tmpdel->stuff));
        new_free(&tmpdel);
    }
    alias_list[type]=(Alias *) 0;
}

/* Really removes structure */
void DumpAssign(name)
char *name;
{
    int found=0;
    int namelen;
    char locbuf[mybufsize];
    Alias *tmp;
    Alias *tmprem;

    snprintf(locbuf,sizeof(locbuf),"%s.",name);
    namelen=strlen(locbuf);
    for (tmp=alias_list[VAR_ALIAS];tmp;) {
        tmprem=tmp;
        tmp=tmp->next;
        if (!strncmp(tmprem->name,locbuf,namelen))
            if ((tmprem=find_alias(&(alias_list[VAR_ALIAS]),tmprem->name,1,NULL))) {
                new_free(&(tmprem->name));
                new_free(&(tmprem->stuff));
                new_free(&tmprem);
                found=1;
            }
    }
    if (found) say("Assign structure %s removed",name);
    else say("Assign structure %s not found",name);
}

/* Clears assigned structure */
#ifndef LITE
void Purge(command,args,subargs)
char *command;
char *args;
char *subargs;
{
    char *name;

    if (!(args && *args)) {
        PrintUsage("PURGE name");
        return;
    }
    name=new_next_arg(args,&args);
    upper(name);
    DumpAssign(name);
}
#endif
/****************************************************************************/
