/*
 * functions.c -- Built-in functions for ircII
 *
 * Written by Michael Sandrof
 * Copyright(c) 1990 Michael Sandrof
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 *
 */

#include "irc.h"
#include "alias.h"
#include "alist.h"
#include "array.h"
#include "dcc.h"
#include "commands.h"
#include "files.h"
#include "history.h"
#include "hook.h"
#include "input.h"
#include "ircaux.h"
#include "names.h"
#include "output.h"
#include "list.h"
#include "parse.h"
#include "screen.h"
#include "server.h"
#include "status.h"
#include "vars.h"
#include "window.h"
#include "ircterm.h"
#include "notify.h"
#include "misc.h"
#include "userlist.h"
#include "numbers.h"
#include "ignore.h"
#include "hash2.h"
#include "struct.h"
#include "cset.h"

#include "bitchx"
#include <sys/stat.h>

#ifdef HAVE_REGEX_H
#include <regex.h>
#endif
#ifdef HAVE_UNAME
#include <sys/utsname.h>
#endif

#include "tcl_bx.h"

static	char	*alias_detected (void);
static	char	*alias_sent_nick (void);
static	char	*alias_recv_nick (void);
static	char	*alias_msg_body (void);
static	char	*alias_joined_nick (void);
static	char	*alias_public_nick (void);
static	char	*alias_dollar (void);
static	char	*alias_channel (void);
static	char	*alias_server (void);
static	char	*alias_query_nick (void);
static	char	*alias_target (void);
static	char	*alias_nick (void);
static	char	*alias_invite (void);
static	char	*alias_cmdchar (void);
static	char	*alias_line (void);
	char	*alias_away (void);
static	char	*alias_oper (void);
static	char	*alias_chanop (void);
static	char	*alias_modes (void);
static	char	*alias_buffer (void);
static	char	*alias_time (void);
static	char	*alias_version (void);
static	char	*alias_currdir (void);
static	char	*alias_current_numeric (void);
static	char	*alias_server_version (void);
static  char	*alias_show_userhost (void);
static	char	*alias_show_realname (void);
static	char	*alias_online (void);
static	char 	*alias_idle (void);
static	char	*alias_version_str (void);
static	char	*alias_version_str1 (void);
static	char	*alias_thingansi (void);
static	char	*alias_uptime (void);
static	char	*alias_serverport (void);
static	char	*alias_tclsupport (void);
static	char	*alias_current_network (void);

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

static	BuiltIns built_in[] =
{
	{ '.',		alias_sent_nick },
	{ ',',		alias_recv_nick },
	{ ':',		alias_joined_nick },
	{ ';',		alias_public_nick },
	{ '`',		alias_uptime },
	{ '$',		alias_dollar },
	{ 'A',		alias_away },
	{ 'B',		alias_msg_body },
	{ 'C',		alias_channel },
	{ 'D',		alias_detected },
	{ 'E',		alias_idle },
	{ 'F',		alias_online },
	{ 'G',		alias_thingansi },
	{ 'H', 		alias_current_numeric },
	{ 'I',		alias_invite },
	{ 'J',		alias_version_str },
	{ 'K',		alias_cmdchar },
	{ 'L',		alias_line },
	{ 'M',		alias_modes },
	{ 'N',		alias_nick },
	{ 'O',		alias_oper },
	{ 'P',		alias_chanop },
	{ 'Q',		alias_query_nick },
	{ 'R',		alias_server_version },
	{ 'S',		alias_server },
	{ 'T',		alias_target },
	{ 'U',		alias_buffer },
	{ 'V',		alias_version },
	{ 'W',		alias_currdir },
	{ 'X',		alias_show_userhost },
	{ 'Y',		alias_show_realname },
	{ 'Z',		alias_time },
	{ 'a',		alias_version_str1 },
	{ 'n',		alias_current_network },
	{ 's',		alias_serverport },
	{ 'v',		alias_tclsupport },
	{ 0,	 	NULL }
};

/* the 30 "standard" functions */
static	char	*function_channels 	(char *);
static	char	*function_connect 	(char *);
static	char	*function_curpos 	(char *);
	char	*function_decode 	(unsigned char *);
static	char	*function_encode 	(unsigned char *);
static	char	*function_index 	(char *);
static	char	*function_ischannel 	(char *);
static	char	*function_ischanop 	(char *);
static	char	*function_left 		(char *);
static	char	*function_listen 	(char *);
static	char	*function_match 	(char *);
static	char	*function_mid 		(char *);
static	char	*function_notify	(char *);
static	char	*function_pid 		(char *);
static	char	*function_ppid 		(char *);
static	char	*function_rand 		(char *);
static	char	*function_right 	(char *);
static	char	*function_rindex 	(char *);
static	char	*function_rmatch 	(char *);
static	char	*function_servers 	(char *);
static	char	*function_srand 	(char *);
static	char	*function_stime 	(char *);
static	char	*function_strip 	(char *);
static	char	*function_tdiff 	(char *);
static	char	*function_tdiff2 	(char *);
static	char	*function_time 		(char *);
static	char	*function_tolower 	(char *);
static	char	*function_toupper 	(char *);
static	char	*function_userhost 	(char *);
static	char	*function_winnum 	(char *);
static	char	*function_winnam 	(char *);
static	char	*function_winnames 	(char *);
static	char	*function_word 		(char *);
static	char	*function_utime		(char *);
static	char	*function_umode		(char *);

/* CDE added functions */
	char	*function_eleet		(char *);
	char	*function_cool		(char *);
	char	*function_cool2		(char *);
	char 	*function_annoy		(char *);
static	char 	*function_uptime	(char *);
static  char	*function_cluster	(char *);
static  char	*function_checkshit	(char *);
static	char	*function_checkuser	(char *);
static	char	*function_rot13		(char *);
	char	*function_addtabkey	(char *);
	char	*function_gettabkey	(char *);
static	char	*function_lastnotice	(char *);
static	char	*function_lastmessage	(char *);
static  char	*function_help		(char *);
static	char	*function_isuser	(char *);
/* Thanks Jordy */
static 	char	*function_pad		(char *);
static	char	*function_isban		(char *);
static	char	*function_isop		(char *);
static	char	*function_isvoice	(char *);
static	char	*function_randomnick	(char *);

static	char	*function_openserver	(char *);
static	char	*function_readserver	(char *);
static	char	*function_writeserver	(char *);
static	char	*function_closeserver	(char *);
static	char	*function_getreason	(char *);

/* the 53 "extended" functions */
static	char *	function_after 		(char *);
static	char *	function_afterw 	(char *);
static	char *	function_aliasctl	(char *);
static	char *	function_ascii 		(char *);
static	char *	function_before 	(char *);
static	char *	function_beforew 	(char *);
static	char *	function_center 	(char *);
static	char *	function_cexist		(char *);
static	char *	function_currchans	(char *);
static	char *	function_channelmode	(char *);
static	char *	function_channelnicks	(char *);
static	char *	function_chngw 		(char *);
static	char *	function_chops 		(char *);
static	char *	function_chr 		(char *);
static	char *	function_close 		(char *);
static	char *	function_common 	(char *);
static	char *	function_convert 	(char *);
static	char *	function_copattern 	(char *);
static	char *	function_crypt 		(char *);
static	char *	function_diff 		(char *);
static	char *	function_epic 		(char *);
static	char *	function_eof 		(char *);
static	char *	function_fnexist	(char *);
static	char *	function_glob	 	(char *);
static	char *	function_fexist 	(char *);
static	char *	function_filter 	(char *);
static	char *	function_fromw 		(char *);
static	char *	function_fsize	 	(char *);
static	char *	function_geom		(char *);
static	char *	function_info		(char *);
static	char *	function_insertw 	(char *);
static	char *	function_iptoname 	(char *);
static	char *	function_isalpha 	(char *);
static	char *	function_isdigit 	(char *);
static	char *	function_jot 		(char *);
static	char *	function_key 		(char *);
static	char *	function_lastserver	(char *);
static	char *	function_leftw 		(char *);
static	char *	function_mkdir		(char *);
static	char *	function_midw 		(char *);
static	char *	function_nametoip 	(char *);
static	char *	function_nochops 	(char *);
static	char *	function_notw 		(char *);
static	char *	function_numonchannel 	(char *);
static	char *	function_numwords 	(char *);
static	char *	function_numsort 	(char *);
static	char *	function_onchannel 	(char *);
static	char *	function_open 		(char *);
static	char *	function_pass		(char *);
static	char *	function_pattern 	(char *);
static	char *	function_read 		(char *);
static	char *	function_remw 		(char *);
static	char *	function_rename 	(char *);
static	char *	function_restw 		(char *);
static	char *	function_reverse 	(char *);
static	char *	function_revw 		(char *);
static	char *	function_rfilter 	(char *);
static	char *	function_rightw 	(char *);
static	char *	function_rmdir 		(char *);
static	char *	function_rpattern 	(char *);
static	char *	function_sar 		(char *);
static	char *	function_server_version (char *);
static	char *	function_servername	(char *);
static	char *	function_sort		(char *);
static	char *	function_split 		(char *);
static	char *	function_splice 	(char *);
static	char *	function_stripansi	(char *);
static	char *	function_stripansicodes	(char *);
static	char *	function_stripmirc	(char *);
static	char *  function_strftime	(char *);
static	char *	function_strlen		(char *);
static	char *	function_tow 		(char *);
static	char *	function_translate 	(char *);
static	char *	function_truncate 	(char *);
static	char *	function_unlink 	(char *);
static	char *	function_umask		(char *);
static	char *	function_which 		(char *);
static	char *	function_winserv	(char *);
static	char *	function_winsize	(char *);
static	char *	function_write 		(char *);
static	char *	function_writeb		(char *);
static	char *  function_idle		(char *);
static	char *	function_flash		(char *);
static	char *	function_repeat		(char *);
static	char *  function_bcopy		(char *);
	char *	function_cparse		(char *);
static	char *	function_chmod		(char *);
static	char *	function_twiddle	(char *);
static	char *	function_uniq		(char *);
static	char *	function_uhost 		(char *);
static	char *	function_numdiff	(char *);
	char *	function_getkey		(char *);
static	char *  function_winvisible	(char *);
static	char *	function_mircansi	(char *);
static	char *	function_banonchannel	(char *);
static	char *	function_chanwin	(char *);
static	char *	function_gethost	(char *);
static	char *	function_getenv		(char *);
static	char *	function_getvar		(char *);
static	char *	function_status		(char *);
	char *  function_push		(char *);
	char *  function_pop		(char *);
	char *  function_shift		(char *);
	char *  function_unshift	(char *);
static	char *	function_get_info	(char *);
static	char *	function_set_info	(char *);
static	char *	function_statsparse	(char *);
static	char *	function_absstrlen	(char *);
static	char *	function_findw		(char *);
static	char *	function_countansi	(char *);
static	char *	function_strstr		(char *);
static	char *	function_longip		(char *);	
static	char *	function_rword		(char *);
static	char *	function_winlen		(char *);
static	char *	function_isignored	(char *);
static	char *	function_channel	(char *);
static	char *	function_ftime		(char *);
static	char *	function_irclib		(char *);
static	char *	function_winbound	(char *);
static	char *	function_rstrstr	(char *);
static	char *	function_country	(char *);
static	char *	function_servernick	(char *);
static	char *	function_winchan	(char *);
static	char *	function_fparse		(char *);
static	char *	function_isconnected	(char *);
static	char *	function_bmatch		(char *);
static	char *	function_regcomp	(char *);
static	char *	function_regexec	(char *);
static	char *	function_regerror	(char *);
static	char *	function_regfree	(char *);
	char *	function_cdcc		(char *);
	char *	function_sendcdcc	(char *);
static	char *	function_count		(char *);
static	char *	function_msar		(char *);
static	char *	function_getcset	(char *);
	
static	char *	function_leftpc		(char *);
static	char *	function_mask		(char *);
static	char *	function_querywin	(char *);
static	char *	function_uname		(char *);
static	char *	function_winrefs	(char *);

static	char *	function_getgid        (char *);
static	char *	function_getlogin      (char *);
static	char *	function_getpgrp       (char *);
static	char *	function_getuid        (char *);
static	char *	function_iscurchan     (char *);
static	char *	function_printlen      (char *);
static	char *	function_remws         (char *);
static	char *	function_uhc		(char *);	
static	char *	function_deuhc		(char *);	
static	char *	function_getfsets	(char *);
static	char *	function_rest		(char *);
static	char *	function_isnumber	(char *);

/* 
 * This is the built-in function list.  This list *must* be sorted because
 * it is binary searched.   See the code for each function to see how it
 * is used.  Or see the help files.  Or see both.  Or look at the code
 * and see how it REALLY works, irregardless of the documentation >;-)
 */
static BuiltInFunctions built_in_functions[] =
{
	{ "ABSSTRLEN",		function_absstrlen	},
	{ "ADDTABKEY",		function_addtabkey	},
	{ "AFTER",              function_after 		},
	{ "AFTERW",             function_afterw 	},
	{ "ALIASCTL",		function_aliasctl	},
	{ "ANNOY",		function_annoy		},
	{ "ASCII",              function_ascii 		},
	{ "BANONCHANNEL",	function_banonchannel	},
	{ "BCOPY",		function_bcopy		},
	{ "BEFORE",             function_before 	},
	{ "BEFOREW",            function_beforew 	},
	{ "BITCHX",		function_epic		},
	{ "BMATCH",		function_bmatch		},
	{ "CENTER",		function_center 	},
	{ "CEXIST",		function_cexist		},
	{ "CHANMODE",		function_channelmode	},
	{ "CHANNEL",		function_channel	},
	{ "CHANNICKS",		function_channelnicks	},
	{ "CHANUSERS",		function_onchannel 	},
	{ "CHANWIN",		function_chanwin	},
	{ "CHECKSHIT",		function_checkshit	},
	{ "CHECKUSER",		function_checkuser	},
	{ "CHMOD",		function_chmod		},
	{ "CHNGW",              function_chngw 		},
	{ "CHOPS",              function_chops 		},
	{ "CHR",                function_chr 		},
	{ "CLOSE",		function_close 		},
#ifndef BITCHX_LITE
	{ "CLOSESOCKET",	function_closeserver	},
#endif
	{ "CLUSTER",		function_cluster	},
	{ "COMMON",             function_common 	},
	{ "CONNECT",		function_connect 	},
	{ "CONVERT",		function_convert 	},
	{ "COOL",		function_cool		},
	{ "COOL2",		function_cool2		},
	{ "COPATTERN",          function_copattern 	},
	{ "COUNT",		function_count		},
	{ "COUNTANSI",		function_countansi	},
	{ "COUNTRY",		function_country	},
	{ "CPARSE",		function_cparse		},
	{ "CRYPT",		function_crypt		},
	{ "CURPOS",		function_curpos 	},
	{ "CURRCHANS",		function_currchans	},
	{ "DECODE",	  (bf *)function_decode 	},
	{ "DELARRAY",           function_delarray 	},
	{ "DELITEM",            function_delitem 	},
	{ "DEUHC",		function_deuhc		},
	{ "DIFF",               function_diff 		},
	{ "ELEET",		function_eleet		},
	{ "ENCODE",	  (bf *)function_encode 	},
	{ "EOF",		function_eof 		},
	{ "EPIC",		function_epic		},
	{ "FEXIST",             function_fexist 	},
	{ "FILTER",             function_filter 	},
	{ "FINDITEM",           function_finditem 	},
	{ "FINDW",		function_findw		},
	{ "FLASH",		function_flash		},
	{ "FNEXIST",		function_fnexist	},
	{ "FPARSE",		function_fparse		},
	{ "FROMW",              function_fromw 		},
	{ "FSIZE",		function_fsize		},
	{ "FTIME",		function_ftime		},
	{ "GEOM",		function_geom		},
	{ "GETARRAYS",          function_getarrays 	},
	{ "GETCDCC",		function_cdcc		},
	{ "GETCSET",		function_getcset	},
	{ "GETGID",		function_getgid		},
	{ "GETENV",		function_getenv		},
	{ "GETFSETS",		function_getfsets	},
	{ "GETHOST",		function_gethost	},
	{ "GETINFO",		function_get_info	},
	{ "GETITEM",            function_getitem 	},
	{ "GETKEY",		function_getkey		},
	{ "GETLOGIN",		function_getlogin	},
	{ "GETMATCHES",         function_getmatches 	},
	{ "GETPGRP",            function_getpgrp        },
	{ "GETREASON",		function_getreason	}, 
	{ "GETRMATCHES",        function_getrmatches 	},
	{ "GETTABKEY",		function_gettabkey	},
	{ "GETTMATCH",		function_gettmatch	},
	{ "GETUID",             function_getuid         },
	{ "GETVAR",		function_getvar		},
	{ "GLOB",		function_glob		},
	{ "HELP",		function_help		},
	{ "IDLE",		function_idle		},
	{ "IFINDFIRST",         function_ifindfirst 	},
	{ "IFINDITEM",          function_ifinditem 	},
	{ "IGETITEM",           function_igetitem 	},
	{ "INDEX",		function_index 		},
	{ "INDEXTOITEM",        function_indextoitem 	},
	{ "INFO",		function_info		},
	{ "INSERTW",            function_insertw 	},
	{ "IPTONAME",		function_iptoname 	},
	{ "IRCLIB",		function_irclib		},
	{ "ISALPHA",		function_isalpha 	},
	{ "ISBAN",		function_isban		},
	{ "ISCHANNEL",		function_ischannel 	},
	{ "ISCHANOP",		function_ischanop 	},
	{ "ISCONNECTED",	function_isconnected	},
	{ "ISCURCHAN",          function_iscurchan      },
	{ "ISDIGIT",		function_isdigit 	},
	{ "ISIGNORED",		function_isignored	},
	{ "ISNUMBER",		function_isnumber	},
	{ "ISOP",		function_isop		},
	{ "ISUSER",		function_isuser		},
	{ "ISVOICE",		function_isvoice	},
	{ "ITEMTOINDEX",        function_itemtoindex 	},
	{ "JOT",                function_jot 		},
	{ "KEY",                function_key 		},
	{ "LASTMESSAGE",	function_lastmessage	},
	{ "LASTNOTICE",		function_lastnotice	},
	{ "LASTSERVER",		function_lastserver	},
	{ "LEFT",		function_left 		},
	{ "LEFTPC",		function_leftpc		},
	{ "LEFTW",              function_leftw 		},
	{ "LISTARRAY",		function_listarray	},
	{ "LISTEN",		function_listen 	},
	{ "LONGIP",		function_longip		},
	{ "MASK",		function_mask		},
	{ "MATCH",		function_match 		},
	{ "MATCHITEM",          function_matchitem 	},
	{ "MID",		function_mid 		},
	{ "MIDW",               function_midw 		},
#ifndef BITCHX_LITE1
	{ "MIRCANSI",		function_mircansi	},
#endif
	{ "MKDIR",		function_mkdir		},
	{ "MSAR",		function_msar		},
	{ "MYCHANNELS",		function_channels 	},
	{ "MYSERVERS",		function_servers 	},
	{ "NAMETOIP",		function_nametoip 	},
	{ "NOCHOPS",            function_nochops 	},
	{ "NOTIFY",		function_notify		},
	{ "NOTW",               function_notw 		},
	{ "NUMARRAYS",          function_numarrays 	},
	{ "NUMDIFF",		function_numdiff	},
	{ "NUMITEMS",           function_numitems 	},
	{ "NUMONCHANNEL",	function_numonchannel 	},
	{ "NUMSORT",		function_numsort	},
	{ "NUMWORDS",		function_numwords	},
	{ "ONCHANNEL",          function_onchannel 	},
	{ "OPEN",		function_open 		},
#ifndef BITCHX_LITE
	{ "OPENSOCKET",		function_openserver	},
#endif
	{ "PAD",		function_pad		},
	{ "PASS",		function_pass		},
	{ "PATTERN",            function_pattern 	},
	{ "PID",		function_pid 		},
	{ "POP",		function_pop 		},
	{ "PPID",		function_ppid 		},
	{ "PRINTLEN",		function_printlen	},
	{ "PUSH",		function_push 		},
	{ "QUERYWIN",		function_querywin	},
	{ "RAND",		function_rand 		},
	{ "RANDOMNICK",		function_randomnick	},
	{ "READ",		function_read 		},
	{ "REGCOMP",		function_regcomp	},
	{ "REGERROR",		function_regerror	},
	{ "REGEXEC",		function_regexec	},
	{ "REGFREE",		function_regfree	},
#ifndef BITCHX_LITE
	{ "READSOCKET",		function_readserver	},
#endif
	{ "REMW",               function_remw 		},
	{ "REMWS",		function_remws		},
	{ "RENAME",		function_rename 	},
	{ "REPEAT",		function_repeat		},
	{ "REST",		function_rest		},
	{ "RESTW",              function_restw 		},
	{ "REVERSE",            function_reverse 	},
	{ "REVW",               function_revw 		},
	{ "RFILTER",            function_rfilter 	},
	{ "RIGHT",		function_right 		},
	{ "RIGHTW",             function_rightw 	},
	{ "RINDEX",		function_rindex 	},
	{ "RMATCH",		function_rmatch 	},
	{ "RMATCHITEM",         function_rmatchitem 	},
	{ "RMDIR",		function_rmdir 		},
	{ "ROT13",		function_rot13		},
	{ "RPATTERN",           function_rpattern 	},
	{ "RSTRSTR",		function_rstrstr	},
	{ "RWORD",		function_rword		},
	{ "SAR",		function_sar 		},
	{ "SENDCDCC",		function_sendcdcc	},
	{ "SERVERNAME",		function_servername	},
	{ "SERVERNICK",		function_servernick	},
	{ "SETINFO",		function_set_info	},
	{ "SETITEM",            function_setitem 	},
	{ "SHIFT",		function_shift 		},
	{ "SORT",		function_sort		},
	{ "SPLICE",		function_splice 	},
	{ "SPLIT",		function_split 		},
	{ "SRAND",		function_srand 		},
	{ "STATSPARSE",		function_statsparse	},
	{ "STATUS",		function_status		},
	{ "STIME",		function_stime 		},
	{ "STRFTIME",		function_strftime	},
	{ "STRIP",		function_strip 		},
	{ "STRIPANSI",		function_stripansi	},
	{ "STRIPANSICODES",	function_stripansicodes	},
#ifndef BITCHX_LITE1
	{ "STRIPMIRC",		function_stripmirc	},
#endif
	{ "STRLEN",		function_strlen		},
	{ "STRSTR",		function_strstr		},
	{ "TDIFF",		function_tdiff 		},
	{ "TDIFF2",		function_tdiff2 	},
	{ "TIME",		function_time 		},
	{ "TOLOWER",		function_tolower 	},
	{ "TOUPPER",		function_toupper 	},
	{ "TOW",                function_tow 		},
	{ "TR",			function_translate 	},
	{ "TRUNC",		function_truncate 	},
	{ "TWIDDLE",		function_twiddle	},
	{ "UHC",		function_uhc		},
	{ "UHOST",		function_uhost		},
	{ "UMASK",		function_umask		},
	{ "UNAME",		function_uname		},
	{ "UNIQ",		function_uniq		},
	{ "UNLINK",		function_unlink 	},
	{ "UNSHIFT",		function_unshift 	},
	{ "UPTIME",		function_uptime		},
	{ "USERHOST",		function_userhost 	},
	{ "USERMODE",		function_umode		},
	{ "UTIME",		function_utime	 	},
	{ "VERSION",		function_server_version },
	{ "WHICH",		function_which 		},
	{ "WINBOUND",		function_winbound	},
	{ "WINCHAN",		function_winchan	},
	{ "WINLEN",		function_winlen		},
	{ "WINNAM",		function_winnam 	},
	{ "WINNICKLIST",	function_winnames 	},
	{ "WINNUM",		function_winnum 	},
	{ "WINREFS",		function_winrefs	},
	{ "WINSERV",		function_winserv	},
	{ "WINSIZE",		function_winsize	},
	{ "WINVISIBLE",		function_winvisible	},
	{ "WORD",		function_word 		},
	{ "WRITE",		function_write 		},
	{ "WRITEB",		function_writeb		},
#ifndef BITCHX_LITE
	{ "WRITESOCKET",	function_writeserver	},
#endif
	{ NULL,			NULL 			}
};

#define	NUMBER_OF_FUNCTIONS (sizeof(built_in_functions) / sizeof(BuiltInFunctions)) - 2

#include "hash2.h"

#define FUNCTION_HASHSIZE 251
HashEntry functions[FUNCTION_HASHSIZE] = { { NULL } };

int done_init_functions = 0;


static int	func_exist (char *name)
{
	int cnt, pos;
	char *tmp = alloca(strlen(name) + 1);

	strcpy(tmp, name);
	upper(tmp);

	find_fixed_array_item(built_in_functions, sizeof(BuiltInFunctions), NUMBER_OF_FUNCTIONS + 1, tmp, &cnt, &pos);
	if (cnt < 0)
		return 1;

	return 0;
}

int in_cparse  = 0;
extern time_t start_time;

#ifdef WANT_TCL

void add_tcl_alias (Tcl_Interp *interp, void *func)
{
int i = 0;
char str[80];
	while (built_in_functions[i].func)
	{
		sprintf(str, "_%s", built_in_functions[i].name);
		Tcl_CreateCommand(interp, lower(str), func?func:built_in_functions[i].func, NULL, NULL);
		i++;
	}
}

#endif

typedef struct _built_in_new {
	struct _built_in_new *next;
	char *name;
	char *(*func)(char *);
} NewBuiltInFunctions;
	
static inline void move_link_to_top(NewBuiltInFunctions *tmp, NewBuiltInFunctions *prev, HashEntry *location)
{
	if (prev) 
	{
		NewBuiltInFunctions *old_list;
		old_list = (NewBuiltInFunctions *) location->list;
		location->list = (void *) tmp;
		prev->next = tmp->next;
		tmp->next = old_list;
	}
}

static void init_functions(void)
{
int i;
unsigned long hash;
NewBuiltInFunctions *new;

	for (i = 0; i <= NUMBER_OF_FUNCTIONS; i++)
	{
		if (!built_in_functions[i].func)
			break;
		hash = hash_nickname(built_in_functions[i].name, FUNCTION_HASHSIZE);
		new = (NewBuiltInFunctions *)new_malloc(sizeof(NewBuiltInFunctions));
		new->name = built_in_functions[i].name;
		new->func = built_in_functions[i].func;
		new->next = (NewBuiltInFunctions *)functions[hash].list;
		functions[hash].list = (void *) new;
		functions[hash].links++;
	}
	done_init_functions++;
}

BUILT_IN_COMMAND(debugfunc)
{
NewBuiltInFunctions *ptr;
int i;
	for (i = 0; i < FUNCTION_HASHSIZE; i++)
	{
		if (!(ptr = (NewBuiltInFunctions *)functions[i].list))
			continue;
		while (ptr)
		{
			put_it("DEBUG_FUNC[%d]: %s  %d links  %d hits", i, ptr->name, functions[i].links, functions[i].hits);
			ptr = ptr->next;
		}
	}
	
}

BuiltInFunctions *find_func_alias(char *name)
{
unsigned long hash = 0;
HashEntry *location;
register NewBuiltInFunctions *tmp, *prev = NULL;
static BuiltInFunctions new;

	if (!done_init_functions)
		init_functions();
	hash = hash_nickname(name, FUNCTION_HASHSIZE);
	location = &functions[hash];	
	for (tmp = (NewBuiltInFunctions *)location->list; tmp; tmp = tmp->next)
	{
		if (!my_stricmp(name, tmp->name))
		{
			move_link_to_top(tmp, prev, location);
			location->hits++;
			new.name = tmp->name;
			new.func = tmp->func;
			return &new;
		}
	}
	return NULL;
#if 0
	while (built_in_functions[i].func && i <= NUMBER_OF_FUNCTIONS)
	{
		if (!my_stricmp(name, built_in_functions[i].name))
			return &built_in_functions[i];
		i++;
	}
#endif
	return NULL;
}

char **get_builtins(char *name, int *cnt)
{
char *last_match = NULL;
int matches_size = 5;
int i = 0;
int len;
char **matches = NULL;
#ifdef WANT_DLL	
BuiltInDllFunctions *dll = NULL;
#endif	

	len = strlen(name);
	*cnt = 0;
	matches = RESIZE(matches, char *, matches_size);
        while (built_in_functions[i].func && i <= NUMBER_OF_FUNCTIONS)
	{
		if (strncmp(name, built_in_functions[i].name, len) == 0)
		{
			matches[*cnt] = NULL;
			malloc_strcpy(&(matches[*cnt]), built_in_functions[i].name);
			last_match = matches[*cnt];
			if (++(*cnt) == matches_size)
			{
				matches_size += 5;
				matches = (char	**) RESIZE(matches, char *, matches_size);
			}
		}
		else if (*cnt)
			break;
		i++;
	}
#ifdef WANT_DLL
	for (dll = dll_functions; dll; dll = dll->next)
	{
		if (strncasecmp(name, dll->name, len) == 0)
		{
			matches[*cnt] = NULL;
			malloc_strcpy(&(matches[*cnt]), dll->name);
			if (++(*cnt) == matches_size)
			{
				matches_size += 5;
				matches = (char	**) RESIZE(matches, char *, matches_size);
			}
		}
	}
#endif
	return matches;
}

char	*built_in_alias (char c)
{
	BuiltIns	*tmp;

	for (tmp = built_in;tmp->name;tmp++)
		if (c == tmp->name)
			return tmp->func();

	return m_strdup(empty_string);
}

char	*call_function (char *name, char *args, int *args_flag)
{
extern	char	*check_tcl_alias (char *command, char *args);
	char	*tmp;
	char	*result = NULL;
	char	*debug_copy = NULL;
	BuiltInFunctions *funcptr = NULL;
#ifdef WANT_DLL	
	register BuiltInDllFunctions *dll = NULL;
#endif	
	char	*lparen, *rparen;

	if ((lparen = strchr(name, '(')))
	{
		if ((rparen = MatchingBracket(lparen + 1, '(', ')')))
			*rparen++ = 0;
		else
			yell("Unmatched lparen in function call [%s]", name);

		*lparen++ = 0;
 	}
	else
		lparen = empty_string;

	tmp = expand_alias(lparen, args, args_flag, NULL);
	if (get_int_var(DEBUG_VAR) & DEBUG_FUNCTIONS)
	{
		debug_copy = alloca(strlen(tmp) + 1);
		strcpy(debug_copy, tmp);
	}
	upper(name);
#ifdef WANT_DLL
	for (dll = dll_functions; dll; dll = dll->next)
		if (!strcasecmp(name, dll->name))
			break;
	if (dll)
		result = (dll->func)(tmp);
	else 
#endif
	{
#ifdef WANT_TCL
		if (!(result = check_tcl_alias(name, tmp)))
#endif
		{
			if ((funcptr = find_func_alias(name)))
				result = funcptr->func(tmp);
			else
				result = call_user_function(name, tmp);
		}
	}

	if (debug_copy)
		yell("Function %s(%s) returned %s", name, debug_copy, result);
	new_free(&tmp);
	return result;
}



/* built in expando functions */
static	char	*alias_version_str1	(void) { return m_strdup(_VERSION_); }
static	char	*alias_line 		(void) { return m_strdup(get_input()); }
static	char	*alias_buffer 		(void) { return m_strdup(cut_buffer); }
static	char	*alias_time 		(void) { return m_strdup(update_clock(GET_TIME)); }
static	char	*alias_dollar 		(void) { return m_strdup("$"); }
static	char	*alias_detected 	(void) { return m_strdup(last_notify_nick); }
static	char	*alias_nick 		(void) { return m_strdup(current_window->server != -1? get_server_nickname(current_window->server) : empty_string); }
	char	*alias_away		(void) { return m_strdup(get_server_away(current_window->server)); }
static	char	*alias_sent_nick 	(void) { return m_strdup((sent_nick) ? sent_nick : empty_string); }
static	char	*alias_recv_nick 	(void) { return m_strdup((recv_nick) ? recv_nick : empty_string); }
static	char	*alias_msg_body 	(void) { return m_strdup((sent_body) ? sent_body : empty_string); }
static	char	*alias_joined_nick 	(void) { return m_strdup((joined_nick) ? joined_nick : empty_string); }
static	char	*alias_public_nick 	(void) { return m_strdup((public_nick) ? public_nick : empty_string); }
static  char    *alias_show_realname 	(void) { return m_strdup(realname); }
static	char	*alias_version_str 	(void) { return m_strdup(irc_version); }
static	char	*alias_invite 		(void) { return m_strdup((invite_channel) ? invite_channel : empty_string); }
static	char	*alias_oper 		(void) { return m_strdup(get_server_operator(from_server) ?  get_string_var(STATUS_OPER_VAR) : empty_string); }
static	char	*alias_version 		(void) { return m_strdup(internal_version); }
static  char    *alias_online 		(void) { return m_sprintf("%ld",(long)start_time); }
static  char    *alias_idle 		(void) { return m_sprintf("%ld",time(NULL)-idle_time); }
static  char    *alias_show_userhost 	(void) { return m_sprintf("%s@%s",username, hostname); }
static	char	*alias_current_numeric	(void) { return m_sprintf("%03d", -current_numeric); }
static	char	*alias_thingansi	(void) { return m_sprintf("%s", numeric_banner()); }
static	char	*alias_uptime		(void) { return m_sprintf("%s", convert_time(time(NULL)-start_time)); }
#ifdef WANT_TCL
extern char tcl_versionstr[];
static	char	*alias_tclsupport	(void) { return m_strdup(tcl_versionstr); }
#else
static	char	*alias_tclsupport	(void) { return m_strdup(empty_string); }
#endif
static	char	*alias_currdir  	(void)
{
	char 	*tmp = (char *)new_malloc(MAXPATHLEN+1);
	return getcwd(tmp, MAXPATHLEN);
}

static	char	*alias_channel 		(void) 
{ 
	char	*tmp; 
	char	buffer[BIG_BUFFER_SIZE+1];
	if ((tmp = get_current_channel_by_refnum(0)))
	{
		strcpy(buffer, tmp);
		tmp = double_quote(tmp, "{}()\"", buffer);
		return m_strdup(tmp);
	}
	else
		return m_strdup(zero);	
/*	return m_strdup((tmp = get_current_channel_by_refnum(0)) ? tmp : zero);*/
}

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

static	char	*alias_current_network	(void) 
{
	return m_strdup((parsing_server_index != -1) ?
		         get_server_network(parsing_server_index) :
		         (get_window_server(0) != -1) ?
			        get_server_network(get_window_server(0)) : empty_string);
}

static	char	*alias_serverport 		(void) 
{
	return m_sprintf("%d", (parsing_server_index != -1) ?
		         server_list[parsing_server_index].port :
		         (get_window_server(0) != -1) ?
			        server_list[get_window_server(0)].port : 0);
}

static	char	*alias_query_nick 	(void)
{
	char	*tmp;
	return m_strdup((tmp = (char *)query_nick()) ? tmp : empty_string);
}

static	char	*alias_target 		(void)
{
	char	*tmp;
	return m_strdup((tmp = get_target_by_refnum(0)) ? tmp : empty_string);
}

static	char	*alias_cmdchar 		(void)
{
	char	*cmdchars, tmp[2];

	if ((cmdchars = get_string_var(CMDCHARS_VAR)) == NULL)
		cmdchars = DEFAULT_CMDCHARS;
	tmp[0] = cmdchars[0];
	tmp[1] = 0;
	return m_strdup(tmp);
}

static	char	*alias_chanop 		(void)
{
	char	*tmp;
	return m_strdup(((tmp = get_current_channel_by_refnum(0)) && get_channel_oper(tmp, from_server)) ?
		"@" : empty_string);
}

static	char	*alias_modes 		(void)
{
	char	*tmp;
	return m_strdup((tmp = get_current_channel_by_refnum(0)) ?
		get_channel_mode(tmp, from_server) : empty_string);
}

static	char	*alias_server_version  (void)
{
	return function_server_version(ltoa(current_window->server));
}


/*	*	*	*	*	*	*	*	*	*
		These are the built-in functions.

	About 80 of them are here, the rest are in array.c.  All of the
	stock client's functions are supported, as well as about 60 more.
	Most of the 30 stock client's functions have been re-written for
	optimization reasons, and also to further distance ircii's code
	from EPIC.
 *	*	*	*	*	*	*	*	*	*/

/* 
 * These are defined to make the construction of the built-in functions
 * easier and less prone to bugs and unexpected behaviors.  As long as
 * you consistently use these macros to do the dirty work for you, you
 * will never have to do bounds checking as the macros do that for you. >;-) 
 *
 * Yes, i realize it makes the code slightly less efficient, but i feel that 
 * the cost is minimal compared to how much time i have spent over the last 
 * year debugging these functions and the fact i wont have to again. ;-)
 */

#define EMPTY empty_string
#define EMPTY_STRING m_strdup(EMPTY)
#define RETURN_EMPTY return EMPTY_STRING
#define RETURN_IF_EMPTY(x) if (empty( x )) RETURN_EMPTY
#define GET_INT_ARG(x, y) {RETURN_IF_EMPTY(y); x = atol(safe_new_next_arg(y, &y));}
#define GET_FLOAT_ARG(x, y) {RETURN_IF_EMPTY(y); x = atof(safe_new_next_arg(y, &y));}
#define GET_STR_ARG(x, y) {RETURN_IF_EMPTY(y); x = new_next_arg(y, &y);RETURN_IF_EMPTY(x);}
#define RETURN_STR(x) return m_strdup(x ? x : EMPTY)
#define RETURN_MSTR(x) return ((x) ? (x) : EMPTY_STRING);
#define RETURN_INT(x) return m_strdup(ltoa(x))

#define BUILT_IN_FUNCTION(x, y) static char * x (char * y)

/*
 * Usage: $left(number text)
 * Returns: the <number> leftmost characters in <text>.
 * Example: $left(5 the quick brown frog) returns "the q"
 *
 * Note: the difference between $[10]foo and $left(10 foo) is that the former
 * is padded and the latter is not.
 */
BUILT_IN_FUNCTION(function_left, word)
{
	long	count;

	GET_INT_ARG(count, word);
	RETURN_IF_EMPTY(word);
	if (count < 0)
		RETURN_EMPTY;
		
	if (strlen(word) > count)
		word[count] = 0;

	RETURN_STR(word);
}

char    *function_getkey(char *input)
{
	char *temp;
	RETURN_IF_EMPTY(input);
	temp = get_channel_key(input, current_window->server);
	RETURN_STR(temp);
}

/*
 * Usage: $right(number text)
 * Returns: the <number> rightmost characters in <text>.
 * Example: $right(5 the quick brown frog) returns " frog"
 */
BUILT_IN_FUNCTION(function_right, word)
{
	long	count;

	GET_INT_ARG(count, word);
	RETURN_IF_EMPTY(word);
	if (count < 0)
		RETURN_EMPTY;
		
	if (strlen(word) > count)
		word += strlen(word) - count;

	RETURN_STR(word);
}

/*
 * Usage: $mid(start number text)
 * Returns: the <start>th through <start>+<number>th characters in <text>.
 * Example: $mid(3 4 the quick brown frog) returns " qui"
 *
 * Note: the first character is numbered zero.
 */
BUILT_IN_FUNCTION(function_mid, word)
{
	long	start, length;

	GET_INT_ARG(start, word);
	GET_INT_ARG(length, word);
	RETURN_IF_EMPTY(word);

	if (start < strlen(word))
	{
		word += start;
		if (length < 0)
			RETURN_EMPTY;
		if (length < strlen(word))
			word[length] = 0;
	}
	else
		word = EMPTY;

	RETURN_STR(word);
}

#if defined(HAVE_GETTIMEOFDAY) && !defined(OLD_RANDOM_BEHAVIOR)
static unsigned long randl (void)
{
	struct timeval tp1;
	get_time(&tp1);
	return (unsigned long) tp1.tv_usec;
}
#endif

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

#define RAND_A 16807L
#define RAND_M 2147483647L
#define RAND_Q 127773L
#define RAND_R 2836L

static	unsigned long	randm(unsigned long l)
{
/* 
     If we have gettimeofday(2), we use it, because its 'leet.
	-- basically how it works is we take two samplings of 
	   gettimeofday().  Since who knows how long apart the samples
	   will be, we can be assured theyre *NOT* psudeo-random.  Then
	   we take the lower 16 bits of each sample, and then pack them
	   into a long, and viola, a random number.
	-- oh.  since we know that the usec field of timeval is microseconds,
	   the lower 16 bits change at a constant rate, which we cant say
	   for the 17th bit. >;-)
 */
#if defined(HAVE_GETTIMEOFDAY) && !defined(OLD_RANDOM_BEHAVIOR)
	unsigned long t1, t2, t;

	t1 = randl();
	t2 = randl();
	t = (t1 & 65535) * 65536 + (t2 & 65535);
	return t;
#else
	static	u_long	z = 0;
	long	t;

	if (!z)
		z = (u_long) getuid();
	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)
			z = (u_long) getuid();
		else
			z = l;
		return 0;
	}
#endif
}

/*
 * Usage: $rand(max)
 * Returns: A random number from zero to max-1.
 * Example: $rand(10) might return any number from 0 to 9.
 */
BUILT_IN_FUNCTION(function_rand, word)
{
	long	tempin;

	GET_INT_ARG(tempin, word);
	if (tempin == 0)
		tempin = (unsigned long) -1;	/* This is cheating. :P */
	RETURN_INT(randm(0L) % tempin);
}

/*
 * Usage: $srand(seed)
 * Returns: Nothing.
 * Side effect: seeds the random number generater.
 * Note: the argument is ignored.
 */
BUILT_IN_FUNCTION(function_srand, word)
{
	randm((long) time(NULL));
	RETURN_EMPTY;
}

/*
 * Usage: $time()
 * Returns: The number of seconds that has elapsed since Jan 1, 1970, GMT.
 * Example: $time() returned something around 802835348 at the time I
 * 	    wrote this comment.
 */
BUILT_IN_FUNCTION(function_time, input)
{
	RETURN_INT(time(NULL));
}

/*
 * Usage: $stime(time)
 * Returns: The human-readable form of the date based on the <time> argument.
 * Example: $stime(1000) returns what time it was 1000 seconds from the epoch.
 * 
 * Note: $stime() is really useful when you give it the argument $time(), ala
 *       $stime($time()) is the human readable form for now.
 */
BUILT_IN_FUNCTION(function_stime, input)
{
	time_t	ltime;

	GET_INT_ARG(ltime, input);
	RETURN_STR(my_ctime(ltime));
}

/*
 * Usage: $tdiff(seconds)
 * Returns: The time that has elapsed represented in days/hours/minutes/seconds
 *          corresponding to the number of seconds passed as the argument.
 * Example: $tdiff(3663) returns "1 hour 1 minute 3 seconds"
 */
BUILT_IN_FUNCTION(function_tdiff, input)
{
	time_t	ltime;

	time_t	days = 0,
		hours = 0,
		minutes = 0,
		seconds = 0;
	char	*tstr, *tmp;
	char	*after = NULL;

	tmp = alloca(strlen(input) + 80);
	*tmp = 0;

	ltime = (time_t)strtol(input, &after, 10);
	tstr = tmp;

	if (after > input)
	{
		seconds = ltime % 60;
		ltime = (ltime - seconds) / 60;
		minutes = ltime % 60;
		ltime = (ltime - minutes) / 60;
		hours = ltime % 24;
		days = (ltime - hours) / 24;

		if (days)
		{
			sprintf(tstr, "%ld day%s ", days, plural(days));
			tstr += strlen(tstr);
		}
		if (hours)
		{
			sprintf(tstr, "%ld hour%s ", hours, plural(hours));
			tstr += strlen(tstr);
		}
		if (minutes)
		{
			sprintf(tstr, "%ld minute%s ", minutes, plural(minutes));
			tstr += strlen(tstr);
		}
	}
	if (seconds || (!days && !hours && !minutes) || (*after == '.' && is_number(after + 1)))
	{
		u_long number = 0;
		/*
		 * If we have a decmial point, and is_number() returns 1,
		 * then we know that we have a real, authentic number AFTER
		 * the decmial point.  As long as it isnt zero, we want it.
		 */
		if (*after == '.')
			number = (u_long) atol(after + 1);

		/*
		 * *IF* and *ONLY IF* either the seconds OR the decmial
		 * part of the number is non zero do we tack on the seconds
		 * argument.  If both are zero, we do nothing.
		 */
		if (seconds != 0 || number != 0)
		{
			if (seconds == 1 && number == 0)
				sprintf(tstr, "%ld second%s", seconds, plural(seconds));
			else
				sprintf(tstr, "%ld%s second%s", seconds, after && *after ? after:empty_string, plural(seconds));
		}
	}
	else
		*--tstr = 0;

	RETURN_STR(tmp);
}

/*
 * Usage: $index(characters text)
 * Returns: The number of leading characters in <text> that do not occur 
 *          anywhere in the <characters> argument.
 * Example: $index(f three fine frogs) returns 6 (the 'f' in 'fine')
 *          $index(frg three fine frogs) returns 2 (the 'r' in 'three')
 */
BUILT_IN_FUNCTION(function_index, input)
{
	char	*schars;
	char	*iloc;

	GET_STR_ARG(schars, input);
	iloc = sindex(input, schars);
	RETURN_INT(iloc ? iloc - input : -1);
}

/*
 * Usage: $rindex(characters text)
 * Returns: The number of leading characters in <text> that occur before the
 *          *last* occurance of any of the characters in the <characters> 
 *          argument.
 * Example: $rindex(f three fine frogs) returns 12 (the 'f' in 'frogs')
 *          $rindex(frg three fine frogs) returns 15 (the 'g' in 'frogs')
 */
BUILT_IN_FUNCTION(function_rindex, word)
{
	char	*chars, *last;

	/* need to find out why ^x doesnt work */
	GET_STR_ARG(chars, word);
	last = rsindex(word + strlen(word) - 1, word, chars, 1);
	RETURN_INT(last ? last - word : -1);
}

/*
 * Usage: $match(pattern list of words)
 * Returns: if no words in the list match the pattern, it returns 0.
 *	    Otherwise, it returns the number of the word that most
 *	    exactly matches the pattern (first word is numbered one)
 * Example: $match(f*bar foofum barfoo foobar) returns 3
 *	    $match(g*ant foofum barfoo foobar) returns 0
 *
 * Note: it is possible to embed spaces inside of a word or pattern simply
 *       by including the entire word or pattern in quotation marks. (")
 */
BUILT_IN_FUNCTION(function_match, input)
{
	char	*pattern, 	*word;
	long	current_match,	best_match = 0,	match = 0, match_index = 0;

	GET_STR_ARG(pattern, input);

	while (input && *input)
	{
		while (input && my_isspace(*input))
			input++;
		match_index++;
		GET_STR_ARG(word, input);
		if ((current_match = wild_match(pattern, word)) > best_match)
		{
			match = match_index;
			best_match = current_match;
		}
	}

	RETURN_INT(match);
}

/*
 * Usage: $rmatch(word list of patterns)
 * Returns: if no pattern in the list matches the word, it returns 0.
 *	    Otherwise, it returns the number of the pattern that most
 *	    exactly matches the word (first word is numbered one)
 * Example: $rmatch(foobar f*bar foo*ar g*ant) returns 2 
 *	    $rmatch(booya f*bar foo*ar g*ant) returns 0
 * 
 * Note: It is possible to embed spaces into a word or pattern simply by
 *       including the entire word or pattern within quotation marks (")
 */
BUILT_IN_FUNCTION(function_rmatch, input)
{
	char	*pattern,	*word;
	int	current_match,	best_match = 0,	match = 0, rmatch_index = 0;

	GET_STR_ARG(word, input);

	while (input && *input)
	{
		while (input && my_isspace(*input))
			input++;
		rmatch_index++;
		GET_STR_ARG(pattern, input);
		if ((current_match = wild_match(pattern, word)) > best_match)
		{
			match = rmatch_index;
			best_match = current_match;
		}
		/* WARNING WARNING HACK IN PROGRESS WARNING WARNING */
		while (input && my_isspace(*input))
			input++;
	}

	RETURN_INT(match);
}

/*
 * Usage: $userhost()
 * Returns: the userhost (if any) of the most previously recieved message.
 * Caveat: $userhost() changes with every single line that appears on
 *         your screen, so if you want to save it, you will need to assign
 *         it to a variable.
 */
BUILT_IN_FUNCTION(function_userhost, input)
{
	if (input && *input)
	{
		char *retval = NULL;
		char *nick;
		const char *userhost;

		while (input && *input)
		{
			GET_STR_ARG(nick, input);
			if ((userhost = fetch_userhost(from_server, nick)))
				m_s3cat(&retval, space, userhost);
			else
				m_s3cat(&retval, space, unknown_userhost);
		}
		return retval;		/* DONT USE RETURN_STR HERE! */
	}
	RETURN_STR(FromUserHost);
}

/* 
 * Usage: $strip(characters text)
 * Returns: <text> with all instances of any characters in the <characters>
 *	    argument removed.
 * Example: $strip(f free fine frogs) returns "ree ine rogs"
 *
 * Note: it can be difficult (actually, not possible) to remove spaces from
 *       a string using this function.  To remove spaces, simply use this:
 *		$tr(/ //$text)
 *
 *	 Actually, i recommend not using $strip() at all and just using
 *		$tr(/characters//$text)
 *	 (but then again, im biased. >;-)
 */
BUILT_IN_FUNCTION(function_strip, input)
{
	char	*result;
	char	*chars;
	char	*cp, *dp;

	GET_STR_ARG(chars, input);
	RETURN_IF_EMPTY(input);

	result = (char *)new_malloc(strlen(input) + 1);
	for (cp = input, dp = result; *cp; cp++)
	{
		/* This is expensive -- gotta be a better way */
		if (!strchr(chars, *cp))
			*dp++ = *cp;
	}
	*dp = '\0';

	return result;		/* DONT USE RETURN_STR HERE! */
}

/*
 * Usage: $encode(text)
 * Returns: a string, uniquely identified with <text> such that the string
 *          can be used as a variable name.
 * Example: $encode(fe fi fo fum) returns "GGGFCAGGGJCAGGGPCAGGHFGN"
 *
 * Note: $encode($decode(text)) returns text (most of the time)
 *       $decode($encode(text)) also returns text.
 */
static char * function_encode (unsigned char * input)
{
	char	*result;
	int	i = 0;

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

	return result;		/* DONT USE RETURN_STR HERE! */
}


/*
 * Usage: $decode(text)
 * Returns: If <text> was generated with $encode(), it returns the string
 *          you originally encoded.  If it wasnt, you will probably get
 *	    nothing useful in particular.
 * Example: $decode(GGGFCAGGGJCAGGGPCAGGHFGN) returns "fe fi fo fum"
 *
 * Note: $encode($decode(text)) returns "text"
 *       $decode($encode(text)) returns "text" too.
 *
 * Note: Yes.  $decode(plain-text) does compress the data by a factor of 2.
 *       But it ignores non-ascii text, so use this as compression at your
 *	 own risk and peril.
 */
char *function_decode(unsigned char * input)
{
	char	*result;
	int	i = 0;

	result = (char *)new_malloc(strlen((char *)input) / 2 + 1);

	while (input[0] && input[1])
	{
		/* oops, this isnt quite right. */
		result[i] = ((input[0] - 0x41) << 4) | (input[1] - 0x41);
		input += 2;
		i++;
	}
	result[i] = '\0';

	return result;		/* DONT USE RETURN_STR HERE! */
}


/*
 * Usage: $ischannel(text)
 * Returns: If <text> could be a valid channel name, 1 is returned.
 *          If <text> is an illegal channel name, 0 is returned.
 *
 * Note: Contrary to popular belief, this function does NOT determine
 * whether a given channel name is in use!
 */
BUILT_IN_FUNCTION(function_ischannel, input)
{
	RETURN_INT(is_channel(input));
}

/*
 * Usage: $ischanop(nick channel)
 * Returns: 1 if <nick> is a channel operator on <channel>
 *          0 if <nick> is not a channel operator on <channel>
 *			* O R *
 *	      if you are not on <channel>
 *
 * Note: Contrary to popular belief, this function can only tell you
 *       who the channel operators are for channels you are already on!
 *
 * Boo Hiss:  This should be $ischanop(channel nick <nick...nick>)
 *	      and return a list (1 1 ... 0), which would allow us to
 *	      call is_chanop() without ripping off the nick, and allow 
 *	      us to abstract is_chanop() to take a list. oh well... 
 *	      Too late to change it now. :/
 */
BUILT_IN_FUNCTION(function_ischanop, input)
{
	char	*nick;

	GET_STR_ARG(nick, input);
	RETURN_INT(is_chanop(input, nick));
}


/*
 * Usage: $word(number text)
 * Returns: the <number>th word in <text>.  The first word is numbered zero.
 * Example: $word(3 one two three four five) returns "four" (think about it)
 */
BUILT_IN_FUNCTION(function_word, word)
{
	int	cvalue;
	char	*w_word;

	GET_INT_ARG(cvalue, word);
	if (cvalue < 0)
		RETURN_EMPTY;

	while (cvalue-- > 0)
		GET_STR_ARG(w_word, word);

	GET_STR_ARG(w_word, word);
	RETURN_STR(w_word);
}


/*
 * Usage: $winnum()
 * Returns: the index number for the current window
 * 
 * Note: returns -1 if there are no windows open (ie, in dumb mode)
 */
BUILT_IN_FUNCTION(function_winnum, input)
{
	Window *win = NULL;
	if (!(win = *input ? get_window_by_desc(input) : current_window))
		RETURN_INT(-1);
	RETURN_INT(win->refnum);
}

BUILT_IN_FUNCTION(function_winnam, input)
{
	Window *win = NULL;
	if (!(win = *input ? get_window_by_desc(input) : current_window))
		RETURN_EMPTY;
	RETURN_STR(win->name);
}

BUILT_IN_FUNCTION(function_winnames, input)
{
Window *win = NULL;
char *s = NULL;
	if (*input)
		win = get_window_by_desc(input);
	else
		win = current_window;
	if (!win)
		RETURN_EMPTY;

	return (s = get_nicklist_by_window(win)) ? s : m_strdup(empty_string);
}

BUILT_IN_FUNCTION(function_connect, input)
{
	char	*host;
	int	port;

	GET_STR_ARG(host, input);
	GET_INT_ARG(port, input);

	return dcc_raw_connect(host, port);	/* DONT USE RETURN_STR HERE! */
}


BUILT_IN_FUNCTION(function_listen, input)
{
	int	port = 0;
	char	*result;

	/* Oops. found by CrowMan, listen() has a default. erf. */
	if (input && *input)
	{
		char *tmp, *ptr;
		if ((tmp = new_next_arg(input, &input)))
		{
			port = strtoul(tmp, &ptr, 10);
			if (ptr == tmp)
				RETURN_EMPTY;	/* error. */
		}
	}

	result = dcc_raw_listen(port);
	RETURN_STR(result);			/* DONT REMOVE RESULT! */
}

BUILT_IN_FUNCTION(function_toupper, input)
{
	return (upper(m_strdup(input)));
}

BUILT_IN_FUNCTION(function_tolower, input)
{
	return (lower(m_strdup(input)));
}

BUILT_IN_FUNCTION(function_curpos, input)
{
	RETURN_INT(current_window->screen->buffer_pos);
}

BUILT_IN_FUNCTION(function_channels, input)
{
	long	winnum;
	Window  *window = current_window;

	if (isdigit(*input))
	{
		GET_INT_ARG(winnum, input);
		window = get_window_by_refnum(winnum);
	}
	if (window->server == -1)
		RETURN_EMPTY;
	return create_channel_list(window);	/* DONT USE RETURN_STR HERE! */
}

BUILT_IN_FUNCTION(function_servers, input)
{
	return create_server_list(input);		/* DONT USE RETURN_STR HERE! */
}

BUILT_IN_FUNCTION(function_pid, input)
{
	RETURN_INT(getpid());
}

BUILT_IN_FUNCTION(function_ppid, input)
{
	RETURN_INT(getppid());
}

/*
 * strftime() patch from hari (markc@arbld.unimelb.edu.au)
 */
BUILT_IN_FUNCTION(function_strftime, input)
{
	char		result[128];
	time_t		ltime;
	struct tm	*tm;

	if (isdigit(*input))
		ltime = strtoul(input, &input, 10);
	else
		ltime = time(NULL);

	while (*input && my_isspace(*input))
		++input; 

	if (!*input)
		return m_strdup(empty_string);


	tm = localtime(&ltime);

	if (!strftime(result, 128, input, tm))
		return m_strdup(empty_string);

	return m_strdup(result);
}

BUILT_IN_FUNCTION(function_idle, input)
{
	return alias_idle();
}



/* The new "added" functions */

/* $before(chars string of text)
 * returns the part of "string of text" that occurs before the
 * first instance of any character in "chars"
 * EX:  $before(! hop!jnelson@iastate.edu) returns "hop"
 */
BUILT_IN_FUNCTION(function_before, word)
{
	char	*pointer = NULL;
	char	*chars;
	char	*tmp;
	long	numint;

	GET_STR_ARG(tmp, word);			/* DONT DELETE TMP! */
	numint = atol(tmp);

	if (numint)
	{
		GET_STR_ARG(chars, word);
	}
	else
	{
		numint = 1;
		chars = tmp;
	}

	if (numint < 0 && strlen(word))
		pointer = word + strlen(word) - 1;

	pointer = search(word, &pointer, chars, numint);

	if (!pointer)
		RETURN_EMPTY;

	*pointer = '\0';
	RETURN_STR(word);
}

/* $after(chars string of text)
 * returns the part of "string of text" that occurs after the 
 * first instance of any character in "chars"
 * EX: $after(! hop!jnelson@iastate.edu)  returns "jnelson@iastate.edu"
 */
BUILT_IN_FUNCTION(function_after, word)
{
	char	*chars;
	char	*pointer = NULL;
	char 	*tmp;
	long	numint;

	GET_STR_ARG(tmp, word);
	numint = atol(tmp);

	if (numint)
		chars = new_next_arg(word, &word);
	else
	{
		numint = 1;
		chars = tmp;
	}

	if (numint < 0 && strlen(word))
		pointer = word + strlen(word) - 1;

	pointer = search(word, &pointer, chars, numint);

	if (!pointer || !*pointer)
		RETURN_EMPTY;

	RETURN_STR(pointer + 1);
}

/* $leftw(num string of text)
 * returns the left "num" words in "string of text"
 * EX: $leftw(3 now is the time for) returns "now is the"
 */
BUILT_IN_FUNCTION(function_leftw, word)
{
	int value;
 
	GET_INT_ARG(value, word);
	if (value < 1)
		RETURN_EMPTY;

	return (extract(word, 0, value-1));	/* DONT USE RETURN_STR HERE! */
}

/* $rightw(num string of text)
 * returns the right num words in "string of text"
 * EX: $rightw(3 now is the time for) returns "the time for"
 */
BUILT_IN_FUNCTION(function_rightw, word)
{
	int     value;

	GET_INT_ARG(value, word);
	if (value < 1)
		RETURN_EMPTY;
		
	return extract2(word, -value, EOS); 
}


/* $midw(start num string of text)
 * returns "num" words starting at word "start" in the string "string of text"
 * NOTE: The first word is word #0.
 * EX: $midw(2 2 now is the time for) returns "the time"
 */
BUILT_IN_FUNCTION(function_midw, word)
{
	int     start, num;

	GET_INT_ARG(start, word);
	GET_INT_ARG(num, word);

	if (num < 1)
		RETURN_EMPTY;

	return extract(word, start, (start + num - 1));
}

/* $notw(num string of text)
 * returns "string of text" with word number "num" removed.
 * NOTE: The first word is numbered 0.
 * EX: $notw(3 now is the time for) returns "now is the for"
 */
BUILT_IN_FUNCTION(function_notw, word)
{
	char    *booya = NULL;
	int     where;
	
	GET_INT_ARG(where, word);

	/* An illegal word simply returns the string as-is */
	if (where < 0)
		RETURN_STR(word);

	if (where > 0)
	{
		char *part1, *part2;
		part1 = extract(word, 0, (where - 1));
		part2 = extract(word, (where + 1), EOS);

		booya = m_strdup(part1);
		m_s3cat_s(&booya, space, part2);
		new_free(&part1);
		new_free(&part2);
	}
	else /* where == 0 */
		booya = extract(word, 1, EOS);

	return booya;				/* DONT USE RETURN_STR HERE! */
}

/* $restw(num string of text)
 * returns "string of text" that occurs starting with and including
 * word number "num"
 * NOTE: the first word is numbered 0.
 * EX: $restw(3 now is the time for) returns "time for"
 */
BUILT_IN_FUNCTION(function_restw, word)
{
	int     where;
	
	GET_INT_ARG(where, word);
	if (where < 0)
		RETURN_EMPTY;
	return extract(word, where, EOS);
}

/* $remw(word string of text)
 * returns "string of text" with the word "word" removed
 * EX: $remw(the now is the time for) returns "now is time for"
 */
BUILT_IN_FUNCTION(function_remw, word)
{
	char 	*word_to_remove;
	int	len;
	char	*str;

	GET_STR_ARG(word_to_remove, word);
	len = strlen(word_to_remove);

	str = stristr(word, word_to_remove);
	for (; str && *str; str = stristr(str + 1, word_to_remove))
	{
		if (str == word || isspace(str[-1]))
		{
			if (!str[len] || isspace(str[len]))
			{
				if (!str[len])
				{
					if (str != word)
						str--;
					*str = 0;
				}
				else if (str > word)
				{
					char *safe = (char *)alloca(strlen(str));
					strcpy(safe, str + len);
					strcpy(str - 1, safe);
				}
				else 
				{
					char *safe = (char *)alloca(strlen(str));
					strcpy(safe, str + len + 1);
					strcpy(str, safe);
				}
				break;
			}
		}
	}

	RETURN_STR(word);
}

/* $insertw(num word string of text)
 * returns "string of text" such that "word" is the "num"th word
 * in the string.
 * NOTE: the first word is numbered 0.
 * EX: $insertw(3 foo now is the time for) returns "now is the foo time for"
 */
BUILT_IN_FUNCTION(function_insertw, word)
{
	int     where;
	char    *what;
	char    *booya= NULL;
	char 	*str1, *str2;

	GET_INT_ARG(where, word);
	
	/* If the word goes at the front of the string, then it
	   already is: return it. ;-) */
	if (where < 1)
		booya = m_strdup(word);
	else
	{
		GET_STR_ARG(what, word);
		str1 = extract(word, 0, (where - 1));
		str2 = extract(word, where, EOS);
		booya = m_strdup(str1);
		if (str1 && *str1)
			booya = m_2dup(str1, space);
		malloc_strcat(&booya, what);
		m_s3cat_s(&booya, space, str2);
		new_free(&str1);
		new_free(&str2);
	}

	return booya;				/* DONT USE RETURN_STR HERE! */
}

/* $chngw(num word string of text)
 * returns "string of text" such that the "num"th word is removed 
 * and replaced by "word"
 * NOTE: the first word is numbered 0
 * EX: $chngw(3 foo now is the time for) returns "now is the foo for"
 */
BUILT_IN_FUNCTION(function_chngw, word)
{
	int     which;
	char    *what;
	char    *booya= NULL;
	char	*str1, *str2;
	
	GET_INT_ARG(which, word);
	GET_STR_ARG(what, word);

	if (which < 0)
		RETURN_STR(word);

	/* hmmm. if which is 0, extract does the wrong thing. */
	str1 = extract(word, 0, which - 1);
	str2 = extract(word, which + 1, EOS);
	booya = m_strdup(str1);
	if (str1 && *str1)
		booya = m_2dup(str1, space);
	malloc_strcat(&booya, what);
	m_s3cat_s(&booya, space, str2);
	new_free(&str1);
	new_free(&str2);

	return (booya);
}


/* $common (string of text / string of text)
 * Given two sets of words seperated by a forward-slash '/', returns
 * all words that are found in both sets.
 * EX: $common(one two three / buckle my two shoe one) returns "one two"
 * NOTE: returned in order found in first string.
 */
BUILT_IN_FUNCTION(function_common, word)
{
	char    *left = NULL, **leftw = NULL,
		*right = NULL, **rightw = NULL,	*booya = NULL;
	int	leftc, lefti;
	int	rightc, righti;
	
	left = word;
	if ((right = strchr(word,'/')) == NULL)
		RETURN_EMPTY;

 	*right++ = 0;
	leftc = splitw(left, &leftw);
	rightc = splitw(right, &rightw);

	for (lefti = 0; lefti < leftc; lefti++)
	{
		for (righti = 0; righti < rightc; righti++)
		{
			if (rightw[righti] && !my_stricmp(leftw[lefti], rightw[righti]))
			{
				m_s3cat(&booya, space, leftw[lefti]);
				rightw[righti] = NULL;
			}
		}
	}
	new_free((char **)&leftw);
	new_free((char **)&rightw);
	if (!booya)
		RETURN_EMPTY;

	return (booya);				/* DONT USE RETURN_STR HERE! */
}

/* $diff(string of text / string of text)
 * given two sets of words, seperated by a forward-slash '/', returns
 * all words that are not found in both sets
 * EX: $diff(one two three / buckle my two shoe)
 * returns "one two three buckle my shoe"
 */
BUILT_IN_FUNCTION(function_diff, word)
{
	char 	*left = NULL, **leftw = NULL,
	     	*right = NULL, **rightw = NULL, *booya = NULL;
	int 	lefti, leftc,
	    	righti, rightc;
	int 	found;

	left = word;
	if ((right = strchr(word,'/')) == NULL)
		RETURN_EMPTY;

	*right++ = 0;
	leftc = splitw(left, &leftw);
	rightc = splitw(right, &rightw);

	for (lefti = 0; lefti < leftc; lefti++)
	{
		found = 0;
		for (righti = 0; righti < rightc; righti++)
		{
			if (rightw[righti] && !my_stricmp(leftw[lefti], rightw[righti]))
			{
				found = 1;
				rightw[righti] = NULL;
			}
		}
		if (!found)
			m_s3cat(&booya, space, leftw[lefti]);
	}

	for (righti = 0; righti < rightc; righti++)
	{
		if (rightw[righti])
			m_s3cat(&booya, space, rightw[righti]);
	}

	new_free((char **)&leftw);
	new_free((char **)&rightw);

	if (!booya)
		RETURN_EMPTY;

	return (booya);
}

/* $pattern(pattern string of words)
 * given a pattern and a string of words, returns all words that
 * are matched by the pattern
 * EX: $pattern(f* one two three four five) returns "four five"
 */
BUILT_IN_FUNCTION(function_pattern, word)
{
	char    *blah;
	char    *booya = NULL;
	char    *pattern;

	GET_STR_ARG(pattern, word);
	while (((blah = new_next_arg(word, &word)) != NULL))
	{
		if (wild_match(pattern, blah))
			m_s3cat(&booya, space, blah);
	}
	RETURN_MSTR(booya);
}

/* $filter(pattern string of words)
 * given a pattern and a string of words, returns all words that are 
 * NOT matched by the pattern
 * $filter(f* one two three four five) returns "one two three"
 */
BUILT_IN_FUNCTION(function_filter, word)
{
	char    *blah;
	char    *booya = NULL;
	char    *pattern;

	GET_STR_ARG(pattern, word);
	while ((blah = new_next_arg(word, &word)) != NULL)
	{
		if (!wild_match(pattern, blah))
			m_s3cat(&booya, space, blah);
	}
	RETURN_MSTR(booya);
}

/* $rpattern(word list of patterns)
 * Given a word and a list of patterns, return all patterns that
 * match the word.
 * EX: $rpattern(jnelson@iastate.edu *@* jnelson@* f*@*.edu)
 * returns "*@* jnelson@*"
 */
BUILT_IN_FUNCTION(function_rpattern, word)
{
	char    *blah;
	char    *booya = NULL;
	char    *pattern;

	GET_STR_ARG(blah, word);
	while ((pattern = new_next_arg(word, &word)) != NULL)
	{
		if (wild_match(pattern, blah))
			m_s3cat(&booya, space, pattern);
	}
	RETURN_MSTR(booya);
}

/* $rfilter(word list of patterns)
 * given a word and a list of patterns, return all patterns that
 * do NOT match the word
 * EX: $rfilter(jnelson@iastate.edu *@* jnelson@* f*@*.edu)
 * returns "f*@*.edu"
 */
BUILT_IN_FUNCTION(function_rfilter, word)
{
	char    *blah;
	char    *booya = NULL;
	char    *pattern;

	GET_STR_ARG(blah, word);
	while ((pattern = new_next_arg(word, &word)) != NULL)
	{
		if (!wild_match(pattern, blah))
			m_s3cat(&booya, space, pattern);
	}
	RETURN_MSTR(booya);
}

/* $copattern(pattern var_1 var_2)
 * Given a pattern and two variable names, it returns all words
 * in the variable_2 corresponding to any words in variable_1 that
 * are matched by the pattern
 * EX: @nicks = [hop IRSMan skip]
 *     @userh = [jnelson@iastate.edu irsman@iastate.edu sanders@rush.cc.edu]
 *     $copattern(*@iastate.edu userh nicks) 
 *	returns "hop IRSMan"
 */
BUILT_IN_FUNCTION(function_copattern, word)
{
	char	*booya = NULL,
		*pattern = NULL,
		*firstl = NULL, *firstlist = NULL, *firstel = NULL,
		*secondl = NULL, *secondlist = NULL, *secondel = NULL;
	char 	*sfirstl, *ssecondl;

	GET_STR_ARG(pattern, word);
	GET_STR_ARG(firstlist, word);
	GET_STR_ARG(secondlist, word);

	firstl = get_variable(firstlist);
	secondl = get_variable(secondlist);
                
	sfirstl = firstl;
	ssecondl = secondl;

	while ((firstel = new_next_arg(firstl, &firstl)))
	{
		if (!(secondel = new_next_arg(secondl, &secondl)))
			break;

		if (wild_match(pattern, firstel))
			m_s3cat(&booya, space, secondel);
	}
	new_free(&sfirstl);
	new_free(&ssecondl);

	if (!booya) 
		RETURN_EMPTY;

	return (booya);
}

/* $beforew(pattern string of words)
 * returns the portion of "string of words" that occurs before the 
 * first word that is matched by "pattern"
 * EX: $beforew(three one two three o leary) returns "one two"
 */
BUILT_IN_FUNCTION(function_beforew, word)
{
	int     where;
	char	*lame = NULL;
	char	*placeholder;

	lame = (char *)alloca(strlen(word) + 1);
	strcpy(lame, word);
	where = my_atol((placeholder = function_rmatch(word)));
	new_free(&placeholder);

	if (where < 1)
	{
		RETURN_EMPTY;
	}
	placeholder = extract(lame, 1, where - 1);
	return placeholder;
}
		
/* Same as above, but includes the word being matched */
BUILT_IN_FUNCTION(function_tow, word)
{
	int     where;
	char	*lame = NULL;
	char	*placeholder;

	lame = (char *)alloca(strlen(word) + 1);
	strcpy(lame, word);
	where = my_atol((placeholder = function_rmatch(word)));
	new_free(&placeholder);

	if (where < 1)
		RETURN_EMPTY;
	placeholder = extract(lame, 1, where);
	return placeholder;
}

/* Returns the string after the word being matched */
BUILT_IN_FUNCTION(function_afterw, word)
{
	int     where;
	char	*lame = NULL;
	char	*placeholder;

	lame = (char *)alloca(strlen(word) + 1);
	strcpy(lame, word);
	where = my_atol((placeholder = function_rmatch(word)));
	new_free(&placeholder);

	if (where < 1)
		RETURN_EMPTY;
	placeholder = extract(lame, where + 1, EOS);
	return placeholder;
}

/* Returns the string starting with the word being matched */
BUILT_IN_FUNCTION(function_fromw, word)
{
	int     where;
	char 	*lame = NULL;
	char	*placeholder;

	lame = (char *)alloca(strlen(word) + 1);
	strcpy(lame, word);
	where = my_atol((placeholder = function_rmatch(word)));
	new_free(&placeholder);

	if (where < 1)
		RETURN_EMPTY;

	placeholder = extract(lame, where, EOS);
	return placeholder;
}

/* Cut and paste a string */
BUILT_IN_FUNCTION(function_splice, word)
{
	char    *variable;
	int	start;
	int	length;
	char 	*left_part = NULL;
	char	*middle_part = NULL;
	char	*right_part = NULL;
	char	*old_value = NULL;
	char	*new_value = NULL;
	int 	old_display = window_display;
	int	num_words;

	GET_STR_ARG(variable, word);
	GET_INT_ARG(start, word);
	GET_INT_ARG(length, word);

	old_value = get_variable(variable);
	num_words = word_count(old_value);

	if (start < 0)
	{
		if ((length += start) <= 0)
			RETURN_EMPTY;
		start = 0;
	}

	if (start >= num_words)
	{
		left_part = m_strdup(old_value);
		middle_part = m_strdup(empty_string);
		right_part = m_strdup(empty_string);
	}

	else if (start + length >= num_words)
	{
		left_part = extract(old_value, 0, start - 1);
		middle_part = extract(old_value, start, EOS);
		right_part = m_strdup(empty_string);
	}

	else
	{
		left_part = extract(old_value, 0, start - 1);
		middle_part = extract(old_value, start, start + length - 1);
		right_part = extract(old_value, start + length, EOS);
	}

	new_value = NULL;
	malloc_strcpy(&new_value, left_part);
	if (new_value && *new_value && word && *word)
		malloc_strcat(&new_value, space);
	if (word && *word)
		malloc_strcat(&new_value, word);
	if (new_value && *new_value && *right_part && *right_part)
		malloc_strcat(&new_value, space);
	if (right_part && *right_part)
		malloc_strcat(&new_value, right_part);

	window_display = 0;
	add_var_alias(variable, new_value);
	window_display = old_display;

	new_free(&old_value);
	new_free(&new_value);
	new_free(&left_part);
	new_free(&right_part);
	return middle_part;
}
	
BUILT_IN_FUNCTION(function_numonchannel, word)
{
	char		*channel;
	ChannelList	*chan = NULL; /* XXX */
	NickList	*tmp = NULL;
	int             counter = 0;

	/* this does the right thing CDE */	
	channel = next_arg(word, &word);
	if ((chan = lookup_channel(channel, current_window->server, 0)))
	{
		for (tmp = next_nicklist(chan, NULL); tmp; tmp = next_nicklist(chan, tmp))
			counter++;
	}
	RETURN_INT(counter);
}

extern NickList *sorted_nicklist(ChannelList *);
extern void clear_sorted_nicklist(NickList **);

BUILT_IN_FUNCTION(function_onchannel, word)
{
	char		*channel;
	ChannelList     *chan = NULL; /* XXX */
	NickList        *tmp = NULL;
	char		*nicks = NULL;

	/* DO NOT use new_next_arg() in here. NULL is a legit value to pass CDE*/
	channel = next_arg(word, &word);
	if ((chan = lookup_channel(channel, current_window->server, 0)))
	{
		NickList *list=NULL;
		list = sorted_nicklist(chan);
		for (tmp = list; tmp; tmp = tmp->next)
			m_s3cat(&nicks, space, tmp->nick);
		clear_sorted_nicklist(&list);
		return (nicks ? nicks : m_strdup(empty_string));
	}
	else
	{
		nicks = channel;
		channel = next_arg(word, &word);
		RETURN_INT(is_on_channel(channel, current_window->server, nicks) ? 1 : 0);
	}
}

BUILT_IN_FUNCTION(function_channelnicks, word)
{
	char		*channel;
	ChannelList     *chan = NULL; /* XXX */
	NickList        *tmp = NULL;
	char		*nicks = NULL;

	channel = next_arg(word, &word);
	if ((chan = lookup_channel(channel, current_window->server, 0)))
	{
		NickList *list = NULL;
		list = sorted_nicklist(chan);
		for (tmp = list; tmp; tmp = tmp->next)
			m_s3cat(&nicks, tmp->nick, ",");
		if (nicks)
			chop(nicks, 1);
		clear_sorted_nicklist(&list);
		return (nicks ? nicks : m_strdup(empty_string));
	}
	else
	{
		nicks = channel;
		channel = next_arg(word, &word);
		RETURN_INT(is_on_channel(channel, current_window->server, nicks) ? 1 : 0);
	}
}

BUILT_IN_FUNCTION(function_chops, word)
{
	char		*channel;
	ChannelList     *chan;
	NickList        *tmp;
	char            *nicks = NULL;

	channel = next_arg(word, &word);
	if ((chan = lookup_channel(channel,current_window->server,0)))
	{
		NickList *list = NULL;
		list = sorted_nicklist(chan);
		for (tmp = list; tmp; tmp = tmp->next)
			if (tmp->chanop) 
				m_s3cat(&nicks, space, tmp->nick);
		clear_sorted_nicklist(&list);
	}
	return (nicks ? nicks : m_strdup(empty_string));
}        

BUILT_IN_FUNCTION(function_nochops, word)
{
	ChannelList     *chan;
	NickList        *tmp;
	char            *nicks = NULL;
	char		*channel;

	channel = next_arg(word, &word);
	if ((chan = lookup_channel(channel,current_window->server,0)))
	{
		NickList *list = NULL;
		list = sorted_nicklist(chan);
		for (tmp = list; tmp; tmp = tmp->next)
			if (!tmp->chanop) 
				m_s3cat(&nicks, space, tmp->nick);
		clear_sorted_nicklist(&list);
	}
	return (nicks ? nicks : m_strdup(empty_string));
}

BUILT_IN_FUNCTION(function_key, word)
{
	char	*channel;
	char    *booya = NULL;
	char 	*key;

	do
	{
		channel = next_arg(word, &word);
		if ((!channel || !*channel) && booya)
			break;

		key = get_channel_key(channel, current_window->server);
		m_s3cat(&booya, space, (key && *key) ? key : "*");
	}
	while (word && *word);

	return (booya ? booya : m_strdup(empty_string));
}

BUILT_IN_FUNCTION(function_revw, words)
{
	char	*booya = NULL;

	while (words && *words)
		m_s3cat(&booya, space, last_arg(&words));
	if (!booya)
		RETURN_EMPTY;

	return booya;
}

BUILT_IN_FUNCTION(function_reverse, words)
{
	int     length = strlen(words);
	char    *booya = NULL;
	int     x = 0;

	booya = (char *)new_malloc(length+1);
	for (length--; length >= 0; length--,x++)
		booya[x] = words[length];
	booya[x] = '\0';
	return (booya);
}

BUILT_IN_FUNCTION(function_strstr, input)
{
char *p, *word;
	GET_STR_ARG(word, input);
	RETURN_IF_EMPTY(input);
	if ((p = stristr(input, word)))
		RETURN_STR(p);
	else
		RETURN_EMPTY;
}

BUILT_IN_FUNCTION(function_rstrstr, input)
{
char *p, *word;
	GET_STR_ARG(word, input);
	RETURN_IF_EMPTY(input);
	if ((p = rstristr(input, word)))
		RETURN_STR(p);
	else
		RETURN_EMPTY;
}

BUILT_IN_FUNCTION(function_jot, input)
{
	int     start = 0;
	int     stop = 0;
	int     interval = 1;
	int     counter;
	char    *booya = NULL;
	int	range;
	
        GET_INT_ARG(start,input)
        GET_INT_ARG(stop, input)
        if (input && *input)
                GET_INT_ARG(interval, input)
        else
                interval = 1;

        if (interval < 0) 
                interval = -interval;

	range = abs(stop - start);
	if (range < 9)
		booya = new_malloc(2 * range);
	else if (range < 99)
		booya = new_malloc(3 * range);
	else if (range < 999)
		booya = new_malloc(4 * range);
	else if (range < 9999)
		booya = new_malloc(5 * range);
	else
		RETURN_EMPTY;

        if (start < stop)
	{
		strcpy(booya, ltoa(start));
		for (counter = start + 1; counter < stop; counter += interval)
		{
			strcat(booya, space);
			strcat(booya, ltoa(counter));
		}
	}
        else
	{
		strcpy(booya, ltoa(start));
		for (counter = start - 1; counter >= stop; counter -= interval)
		{
			strcat(booya, space);
			strcat(booya, ltoa(counter));
		}
	}

        return booya;
}

extern char    *function_shift(char *word)
{
	char    *value = NULL;
	char    *var    = NULL;
	char	*booya 	= NULL;
	int     old_display = window_display;
	char    *placeholder;

	GET_STR_ARG(var, word);

	if (word && *word)
		RETURN_STR(var);

	value = get_variable(var);

	if (!value || !*value)
	{
		new_free(&value);
		RETURN_EMPTY;
	}

	placeholder = value;
	booya = m_strdup(new_next_arg(value, &value));
	/* do not free value at this point */
	if (var)
	{
		window_display = 0;
		add_var_alias(var, value);
		window_display = old_display;
	}
	new_free(&placeholder);
	if (!booya)
		RETURN_EMPTY;
	return booya;
}

extern char    *function_unshift(char *word)
{
	char    *value = NULL;
	char    *var    = NULL;
	char	*booya  = NULL;
	int     old_display = window_display;

	GET_STR_ARG(var, word);
	value = get_variable(var);
	if (!word || !*word)
		return value;

	booya = m_strdup(word);
	m_s3cat_s(&booya, space, value);

	window_display = 0;
	add_var_alias(var, booya);
	window_display = old_display;
	new_free(&value);
	return booya;
}

extern char    *function_push(char *word)
{
	char    *value = NULL;
	char    *var    = NULL;
	int     old_display = window_display;

	GET_STR_ARG(var, word);
	value = get_variable(var);
	m_s3cat(&value, space, word);
	window_display = 0;
	add_var_alias(var, value);
	window_display = old_display;
	return value;
}

extern char	*function_pop(char *word)
{
	char *value	= NULL;
	char *var	= NULL;
	char *pointer	= NULL;
	int   old_display = window_display;
	char *blech     = NULL;

	GET_STR_ARG(var, word);

	if (word && *word)
	{
		pointer = strrchr(word, ' ');
		RETURN_STR(pointer ? pointer : word);
	}

	value = get_variable(var);
	if (!value || !*value)
	{
		new_free(&value);
		RETURN_EMPTY;
	}

	if (!(pointer = strrchr(value, ' ')))
	{
		window_display = 0;
		add_var_alias(var, empty_string); /* dont forget this! */
		window_display = old_display;
		return value;	/* one word -- return it */
	}

	*pointer++ = '\0';
	window_display = 0;
	add_var_alias(var, value);
	window_display = old_display;

	/* because pointer points to value, we *must* make a copy of it
	 * *before* we free value! (And we cant forget to free value, either) 
	 */
	blech = m_strdup(pointer);
	new_free(&value);
	return blech;
}


/* Search and replace function --
   Usage:   $sar(c/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
*/
BUILT_IN_FUNCTION(function_sar, word)
{
	register char    delimiter;
	register char	*pointer	= NULL;
	char    *search         = NULL;
	char    *replace        = NULL;
	char    *data		= NULL;
	char	*value		= NULL;
	char	*booya		= NULL;
	int	variable = 0,global = 0,searchlen,oldwindow = window_display;
	char *(*func) (const char *, const char *) = strstr;
	char 	*svalue;

	while (((*word == 'r') && (variable = 1)) || ((*word == 'g') && (global = 1)) || ((*word == 'i') && (func = stristr)))
		word++;

	RETURN_IF_EMPTY(word);

	delimiter = *word;
	search = word + 1;
	if ((replace = strchr(search, delimiter)) == 0)
		RETURN_EMPTY;

	*replace++ = 0;
	if ((data = strchr(replace,delimiter)) == 0)
		RETURN_EMPTY;

	*data++ = '\0';

	value = (variable == 1) ? get_variable(data) : m_strdup(data);

	if (!value || !*value)
	{
		new_free(&value);
		RETURN_EMPTY;
	}

	pointer = svalue = value;
	searchlen = strlen(search) - 1;
	if (searchlen < 0)
		searchlen = 0;
	if (global)
	{
		while ((pointer = func(pointer,search)) != NULL)
		{
			pointer[0] = pointer[searchlen] = 0;
			pointer += searchlen + 1;
			m_e3cat(&booya, value, replace);
			value = pointer;
			if (!*pointer)
				break;
		}
	} 
	else
	{
		if ((pointer = func(pointer,search)) != NULL)
		{
			pointer[0] = pointer[searchlen] = 0;
			pointer += searchlen + 1;
			m_e3cat(&booya, value, replace);
			value = pointer;
		}
	}

	malloc_strcat(&booya, value);
	if (variable) 
	{
		window_display = 0;
		add_var_alias(data, booya);
		window_display = oldwindow;
	}
	new_free(&svalue);
	return (booya);
}

/* Search and replace function --
   Usage:   $msar(c/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
*/
BUILT_IN_FUNCTION(function_msar, word)
{
	register char    delimiter;
	register char	*pointer	= NULL;
	char    *search         = NULL;
	char    *replace        = NULL;
	char    *data		= NULL;
	char	*value		= NULL;
	char	*booya		= NULL;
	char	*p		= NULL;
	int	variable = 0,global = 0,searchlen,oldwindow = window_display;
	char *(*func) (const char *, const char *) = strstr;
	char 	*svalue;

	while (((*word == 'r') && (variable = 1)) || ((*word == 'g') && (global = 1)) || ((*word == 'i') && (func = stristr)))
		word++;

	RETURN_IF_EMPTY(word);

	delimiter = *word;
	search = word + 1;
	if (!(replace = strchr(search, delimiter)))
		RETURN_EMPTY;

	*replace++ = 0;
	if (!(data = strchr(replace,delimiter)))
		RETURN_EMPTY;

	*data++ = 0;

	if (!(p = strrchr(data, delimiter)))
		value = (variable == 1) ? get_variable(data) : m_strdup(data);
	else
	{
		*p++ = 0;
		value = (variable == 1) ? get_variable(p) : m_strdup(p);
	}
	
	if (!value || !*value)
	{
		new_free(&value);
		RETURN_EMPTY;
	}

	pointer = svalue = value;

	do 
	{
		searchlen = strlen(search) - 1;
		if (searchlen < 0)
			searchlen = 0;
		if (global)
		{
			while ((pointer = func(pointer,search)))
			{
				pointer[0] = pointer[searchlen] = 0;
				pointer += searchlen + 1;
				m_e3cat(&booya, value, replace);
				value = pointer;
				if (!*pointer)
					break;
			}
		} 
		else
		{
			if ((pointer = func(pointer,search)))
			{
				pointer[0] = pointer[searchlen] = 0;
				pointer += searchlen + 1;
				m_e3cat(&booya, value, replace);
				value = pointer;
			}
		}
		malloc_strcat(&booya, value);
		if (data && *data)
		{
			search = data;
			if ((replace = strchr(data, delimiter)))
			{
				*replace++ = 0;
				if ((data = strchr(replace, delimiter)))
					*data++ = 0;
			}
			new_free(&svalue);
			pointer = svalue = value = booya;
			booya = NULL;
			if (!replace || !search)
				break;
		} else 
			break;
	} while (1);

	if (variable) 
	{
		window_display = 0;
		add_var_alias(data, booya);
		window_display = oldwindow;
	}
	new_free(&svalue);
	return (booya);
}

BUILT_IN_FUNCTION(function_center, word)
{
	int	length,pad,width;
	char 	*padc;

	if (!word || !*word)
		RETURN_EMPTY;

	width = atoi(new_next_arg(word, &word));
	RETURN_IF_EMPTY(word);
	length = strlen(word);
	
	if ((pad = width - length) < 0)
		RETURN_STR(word);

	pad /= 2;
	padc = (char *)new_malloc(width+1);
	memset(padc, ' ', pad);
	padc[pad] = '\0';

	return strcat(padc, word);
}

BUILT_IN_FUNCTION(function_split, word)
{
	char	*chrs;
	register char	*pointer;

	chrs = next_arg(word, &word);
	pointer = word;
	while ((pointer = sindex(pointer,chrs)))
		*pointer++ = ' ';

	RETURN_STR(word);
}

BUILT_IN_FUNCTION(function_chr, word)
{
	char aboo[BIG_BUFFER_SIZE];
	char *ack = aboo;
	char *blah;

	while ((blah = next_arg(word, &word)))
		*ack++ = (char)atoi(blah);

	*ack = '\0';
	RETURN_STR(aboo);
}

BUILT_IN_FUNCTION(function_ascii, word)
{
	char *aboo = NULL;

	if (!word || !*word)
		RETURN_EMPTY;

	aboo = m_strdup(ltoa((long) *word));
	while (*++word)
		m_3cat(&aboo, space, ltoa((long) *word));
		
	return (aboo);
}

BUILT_IN_FUNCTION(function_which, word)
{
	char *file1;

#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_STR_ARG(file1, word);

	if ((file1 = path_search(file1, (word && *word) ? word :
		get_string_var(LOAD_PATH_VAR))))
	{
		RETURN_STR(file1);
	}
	else
	{
		new_free(&file1);
		RETURN_EMPTY;
	}
}


BUILT_IN_FUNCTION(function_isalpha, words)
{
	if (((*words >= 'a') && (*words <= 'z')) ||
	    ((*words >= 'A') && (*words <= 'Z')))
		RETURN_INT(1);
	else
		RETURN_INT(0);
}

BUILT_IN_FUNCTION(function_isdigit, words)
{
	if (((*words >= '0') && (*words <= '9')) ||
	    ((*words == '-') && ((*(words+1) >= '0') || (*(words+1) <= '9'))))
		RETURN_INT(1);
	else
		RETURN_INT(0);
}

BUILT_IN_FUNCTION(function_open, words)
{
	char *filename;
	char *mode;
	char *bin_mode = NULL;
	
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_STR_ARG(filename, words);
	GET_STR_ARG(mode, words);
	if (in_cparse)
		RETURN_EMPTY;
	if (words && *words)
		bin_mode = words;
		
	if (*mode == 'R')
	{
		if (bin_mode && *bin_mode == 'B')
			RETURN_INT(open_file_for_read(filename));
		else
			RETURN_INT(open_file_for_read(filename));
	}
	else if (*mode == 'W')
	{
		if (bin_mode && *bin_mode == 'B')
			RETURN_INT(open_file_for_bwrite(filename));
		else
			RETURN_INT(open_file_for_write(filename));
	}
	RETURN_EMPTY;	
}

BUILT_IN_FUNCTION(function_close, words)
{
	RETURN_IF_EMPTY(words);
	if (in_cparse)
		RETURN_INT(0);
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	RETURN_INT(file_close(atoi(new_next_arg(words, &words))));
}	

BUILT_IN_FUNCTION(function_write, words)
{
	char *fdc;
	if (in_cparse)
		RETURN_INT(0);
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_STR_ARG(fdc, words);
	RETURN_INT(file_write(atoi(fdc), words));
}

BUILT_IN_FUNCTION(function_writeb, words)
{
	char *fdc;
	if (in_cparse)
		RETURN_INT(0);
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_STR_ARG(fdc, words);
	RETURN_INT(file_writeb(atoi(fdc), words));
}

BUILT_IN_FUNCTION(function_read, words)
{
	char *fdc = NULL, *numb = NULL;
	if (in_cparse)
		RETURN_INT(0);

#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_STR_ARG(fdc, words);
	if (words && *words)
		GET_STR_ARG(numb, words);

	if (numb)
		return file_readb (atoi(fdc), atoi(numb));
	else
		return file_read (atoi(fdc));
}

BUILT_IN_FUNCTION(function_eof, words)
{
	RETURN_IF_EMPTY(words);
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	RETURN_INT(file_eof(atoi(new_next_arg(words, &words))));
}


BUILT_IN_FUNCTION(function_iptoname, words)
{
	char *ret = ip_to_host(words);
	RETURN_STR(ret);
}

BUILT_IN_FUNCTION(function_nametoip, words)
{
	char *ret = host_to_ip(words);
	RETURN_STR(ret);
}

BUILT_IN_FUNCTION(function_convert, words)
{
	char *ret = one_to_another(words);
	RETURN_STR(ret);
}

BUILT_IN_FUNCTION(function_translate, words)
{
register char *	oldc;
	char *	newc, 
	     *	text,
		delim;
register char *	ptr;
	int 	size_old, 
		size_new,
		x;

	RETURN_IF_EMPTY(words);

	oldc = words;
	/* First character can be a slash.  If it is, we just skip over it */
	delim = *oldc++;
	newc = strchr(oldc, delim);

	if (!newc)
		RETURN_EMPTY;	/* no text in, no text out */

	text = strchr(++newc, delim);

	if (newc == oldc)
		RETURN_EMPTY;

	if (!text)
		RETURN_EMPTY;
	*text++ = '\0';

	if (newc == text)
	{
		*newc = '\0';
		newc = empty_string;
	}
	else
		newc[-1] = 0;

	/* this is cheating, but oh well, >;-) */
	text = m_strdup(text);

	size_new = strlen(newc);
	size_old = strlen(oldc);

	for (ptr = text; ptr && *ptr; ptr++)
	{
		for (x = 0;x < size_old;(void)x++)
		{
			if (*ptr == oldc[x])
			{
				/* Check to make sure we arent
				   just eliminating the character.
				   If we arent, put in the new char,
				   otherwise strcpy it away */
				if (size_new)
					*ptr = newc[(x<size_new)?x:size_new-1];
				else
				{
					ov_strcpy (ptr, ptr+1);
					ptr--;				
				}
				break;
			}
		}
	}
	return text;
}

BUILT_IN_FUNCTION(function_server_version, word)
{
	int servnum;
	int version;

	servnum = ((word && *word) ? atoi(next_arg(word, &word)) : primary_server);

	if (servnum > number_of_servers)
		RETURN_STR("unknown");

	version = get_server_version(servnum);

	if (version == Server2_8) 		RETURN_STR("2.8");
	else if (version == Server_u2_8) 	RETURN_STR("u2.8");
	else if (version == Server_u2_9) 	RETURN_STR("u2.9");
	else if (version == Server_u2_10)	RETURN_STR("u2.10");
	else if (version == Server2_9) 		RETURN_STR("2.9");
	else if (version == Server_u3_0)	RETURN_STR("u3.0");

	RETURN_STR("Unknown");
}

BUILT_IN_FUNCTION(function_unlink, words)
{
	char *	expanded;
	int 	failure = 0;
	if (in_cparse)
		RETURN_INT(0);
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	while (words && *words)
	{
		expanded = expand_twiddle(new_next_arg(words, &words));
		failure -= unlink(expanded);	
		new_free(&expanded);
	}
	RETURN_INT(failure);
}

BUILT_IN_FUNCTION(function_rename, words)
{
	char *	filename1, 
	     *	filename2;
	char *expanded1, *expanded2;
	int 	failure = 0;
	if (in_cparse)
		RETURN_INT(failure);
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_STR_ARG(filename1, words);
	GET_STR_ARG(filename2, words);
	expanded1 = expand_twiddle(filename1);
	expanded2 = expand_twiddle(filename2);
	failure = rename(expanded1, expanded2);
	new_free(&expanded1); new_free(&expanded2);
	RETURN_INT(failure);
}

BUILT_IN_FUNCTION(function_rmdir, words)
{
	int 	failure = 0;
	char	*expanded;
	
	if (in_cparse)
		RETURN_INT(failure);
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	while (words && *words)
	{
		expanded = expand_twiddle(new_next_arg(words, &words));
		failure -= rmdir(expanded);
		new_free(&expanded);
	}
	
	RETURN_INT(failure);
}

BUILT_IN_FUNCTION(function_truncate, words)
{
	int		num = 0;
	double		value = 0;
	char		buffer[BIG_BUFFER_SIZE],
			format[1024];

	GET_INT_ARG(num, words);
	GET_FLOAT_ARG(value, words);

	if (num < 0)
	{
		float foo;
		int end;

		sprintf(format, "%%.%de", -num-1);
		sprintf(buffer, format, value);
		foo = atof(buffer);
		sprintf(buffer, "%f", foo);
		end = strlen(buffer) - 1;
		if (end == 0)
			RETURN_EMPTY;
		while (buffer[end] == '0')
			end--;
		if (buffer[end] == '.')
			end--;
		buffer[end+1] = 0;
	}
	else if (num > 0)
	{
		sprintf(format, "%%10.%dlf", num);
		sprintf(buffer, format, value);
	}
	else
		RETURN_EMPTY;

	while (*buffer == ' ')
		strcpy(buffer, buffer+1);

	RETURN_STR(buffer);
}


/*
 * Apprantly, this was lifted from a CS client.  I reserve the right
 * to replace this code in future versions. (hop)
 */
/*
	I added this little function so that I can have stuff formatted
	into days, hours, minutes, seconds; but with d, h, m, s abreviations.
		-Taner
*/

BUILT_IN_FUNCTION(function_tdiff2, input)
{
	time_t	ltime;
	time_t	days,
		hours,
		minutes,
		seconds;
	char	tmp[80];
	char	*tstr;

	GET_INT_ARG(ltime, input);

	seconds = ltime % 60;
	ltime = (ltime - seconds) / 60;
	minutes = ltime%60;
	ltime = (ltime - minutes) / 60;
	hours = ltime % 24;
	days = (ltime - hours) / 24;
	tstr = tmp;

	if (days)
	{
		sprintf(tstr, "%ldd ", days);
		tstr += strlen(tstr);
	}
	if (hours)
	{
		sprintf(tstr, "%ldh ", hours);
		tstr += strlen(tstr);
	}
	if (minutes)
	{
		sprintf(tstr, "%ldm ", minutes);
		tstr += strlen(tstr);
	}
	if (seconds || (!days && !hours && !minutes))
	{
		sprintf(tstr, "%lds", seconds);
		tstr += strlen(tstr);
	}
	else
		*--tstr = 0;	/* chop off that space! */

	RETURN_STR(tmp);
}


/* 
 * Apparantly, this was lifted from a CS client.  I reserve the right 
 * to replace this code in a future release.
 */
BUILT_IN_FUNCTION(function_utime, input)
{
	struct  timeval         tp;
	get_time(&tp);
	return m_sprintf("%ld %ld",(unsigned long)tp.tv_sec, (unsigned long)tp.tv_usec);
}


/*
 * This inverts any ansi sequence present in the string
 * from: Scott H Kilau <kilau@prairie.NoDak.edu>
 */
BUILT_IN_FUNCTION(function_stripansi, input)
{
	register char	*cp;
	for (cp = input; *cp; cp++)
		if (*cp < 31 && *cp > 13)
			if (*cp != 15 && *cp !=22)
				*cp = (*cp & 127) | 64;
	RETURN_STR(input);
}

#if 0
BUILT_IN_FUNCTION(function_stripansicodes, input)
{
	RETURN_IF_EMPTY(input);
	RETURN_STR(stripansicodes(input));
}
#endif

#ifndef BITCHX_LITE1
/*
 * Function to strip mirc color codes by Colten Edwards.
 * 
 */
BUILT_IN_FUNCTION(function_stripmirc, input)
{
register char *rs, *cp, *ret;
int ctrl_c = 0;

	ret = input;
	for (cp = input; *cp;)
	{
		if (ctrl_c)
		{
			rs = cp;
			*rs = 0;
			cp++;
			if (*cp)
				while(isdigit(*cp)) cp++;
			if (*cp && *cp ==',' && *(cp+1) && isdigit(*(cp+1))) 
			{
				cp++;
				while (isdigit(*cp)) cp++;
			}
			memcpy(rs, cp, strlen(cp)+1);
			ctrl_c = 0;
		} else if (*cp == 3)
			ctrl_c = 1;
		else 
			cp++;
	}
	*cp = 0;
	return m_strdup(ret);
}
#endif

BUILT_IN_FUNCTION(function_servername, input)
{
	int sval;
	char *which;

	GET_INT_ARG(sval, input);

	/* garbage in, garbage out. */
	if (sval < 0 || sval >= number_of_servers)
		RETURN_EMPTY;

	/* First we try to see what the server thinks it name is */
	which = server_list[sval].itsname;

	/* Next we try what we think its name is */
	if (!which)
		which = server_list[sval].name;

	/* Ok. i give up, return a null. */
	RETURN_STR(which);
}

BUILT_IN_FUNCTION(function_lastserver, input)
{
	RETURN_INT(last_server);
}

/*
 * Date: Sat, 26 Apr 1997 13:41:11 +1200
 * Author: IceKarma (ankh@canuck.gen.nz)
 * Contributed by: author
 *
 * Usage: $winchan(#channel <server refnum|server name>)
 * Given a channel name and either a server refnum or a direct server
 * name or an effective server name, this function will return the 
 * refnum of the window where the channel is the current channel (on that
 * server if appropriate.)
 *
 * Returns -1 (an invalid window refnum) on error.
 */
BUILT_IN_FUNCTION(function_chanwin, input)
{
	int servnum = -1;
	char *chan, *serv = NULL;
 	Window *winp = NULL;	/* XXXX */

	if (!input || !*input || !(chan = new_next_arg(input, &input)))
		RETURN_INT(-1);

	if ((serv = new_next_arg(input, &input)))
	{
		if (my_isdigit(serv))
			servnum = my_atol(serv);
		else
			servnum = find_in_server_list(serv, 0);
	}

	while ((traverse_all_windows(&winp)))
	{
		if (winp->current_channel && 
		    !my_stricmp(winp->current_channel, chan))
		{
			if (servnum > -1 && winp->server != servnum)
				continue;
			RETURN_INT(winp->refnum);
 		}
	}

	RETURN_INT(-1);
}

/*
 * The reverse of the above.  You give it a window refnum or name, and 
 * it tells you what the current channel is on that window.
 */
BUILT_IN_FUNCTION(function_winchan, input)
{
	Window *win;

	if (!(win = *input ? get_window_by_desc(input) : current_window))
		RETURN_EMPTY;

	RETURN_STR(win->current_channel);
}



BUILT_IN_FUNCTION(function_winserv, input)
{
	int win = 0;
	char *tmp;
	Window *winp;

	if (input && *input)
	{
		if ((tmp = new_next_arg(input, &input)))
			win = atoi(tmp);
	}
	if ((winp = get_window_by_refnum(win)))
		RETURN_INT(winp->server);

	RETURN_INT(-1);
}

BUILT_IN_FUNCTION(function_numwords, input)
{
	RETURN_INT(word_count(input));
}

BUILT_IN_FUNCTION(function_strlen, input)
{
	RETURN_INT(strlen(input));
}

/* 
 * Next two contributed by Scott H Kilau (sheik), who for some reason doesnt 
 * want to take credit for them. *shrug* >;-)
 *
 * Deciding not to be controversial, im keeping the original (contributed)
 * semantics of these two functions, which is to return 1 on success and
 * -1 on error.  If you dont like it, then tough. =)  I didnt write it, and
 * im not going to second guess any useful contributions. >;-)
 */
BUILT_IN_FUNCTION(function_fexist, words)
{
        char	FileBuf[BIG_BUFFER_SIZE+1];
	char	*filename, *fullname;

#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	*FileBuf = 0;
	if ((filename = new_next_arg(words, &words)))
	{
		if (*filename == '/')
			strcpy(FileBuf, filename);

		else if (*filename == '~') 
		{
			if (!(fullname = expand_twiddle(filename)))
				RETURN_INT(-1);

			strmcpy(FileBuf, fullname, BIG_BUFFER_SIZE);
			new_free(&fullname);
		}
		else 
		{
			getcwd(FileBuf, BIG_BUFFER_SIZE);
			strmcat(FileBuf, "/", BIG_BUFFER_SIZE);
			strmcat(FileBuf, filename, BIG_BUFFER_SIZE);
		}
		if (access(FileBuf, R_OK) == -1)
			RETURN_INT(-1);

		else
			RETURN_INT(1);
	}
	RETURN_INT(-1);
}

/* XXXX - ugh. do we really have to do a access() call first? */
BUILT_IN_FUNCTION(function_fsize, words)
{
        char	FileBuf[BIG_BUFFER_SIZE+1];
	char	*filename, *fullname;
        struct  stat    stat_buf;
	off_t	filesize = 0;

#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	filename = next_arg(words, &words);
	*FileBuf = 0;
	if (filename) 
	{
		if (*filename == '/')
			strcpy(FileBuf, filename);

		else if (*filename == '~') 
		{
			if (!(fullname = expand_twiddle(filename)))
				RETURN_INT(-1);

			strmcpy(FileBuf, fullname, BIG_BUFFER_SIZE);
			new_free(&fullname);
		}
		else 
		{
			getcwd(FileBuf, BIG_BUFFER_SIZE);
			strmcat(FileBuf, "/", BIG_BUFFER_SIZE);
			strmcat(FileBuf, filename, BIG_BUFFER_SIZE);
		}

		if ((stat(FileBuf, &stat_buf)) != -1)
		{
			filesize = stat_buf.st_size;
			RETURN_INT(filesize);
		}
	}
	RETURN_INT(-1);
}

/* 
 * Contributed by CrowMan
 * I changed two instances of "RETURN_INT(result)"
 * (where result was a null pointer) to RETURN_STR(empty_string)
 * because i dont think he meant to return a null pointer as an int value.
 */
/*
 * $crypt(password seed)
 * What it does: Returns a 13-char encrypted string when given a seed and
 *    password. Returns zero (0) if one or both args missing. Additional
 *    args ignored.
 * Caveats: Password truncated to 8 chars. Spaces allowed, but password
 *    must be inside "" quotes.
 * Credits: Thanks to Strongbow for showing me how crypt() works.
 * This cheap hack by: CrowMan
 */
BUILT_IN_FUNCTION(function_crypt, words)
{
        char pass[9] = "\0";
        char seed[3] = "\0";
        char *blah, *bleh, *crypt _((const char *, const char *));

	GET_STR_ARG(blah, words)
	GET_STR_ARG(bleh, words)
	strmcpy(pass, blah, 8);
	strmcpy(seed, bleh, 2);

#if defined(__EMX__) || defined(__OPENNT)
	RETURN_STR(empty_string);
#else
	RETURN_STR(crypt(pass, seed));
#endif
}

BUILT_IN_FUNCTION(function_info, words)
{
/*
	char *which;
	extern char *compile_info;
	extern char *info_c_sum;

	GET_STR_ARG(which, words);

	     if (!my_strnicmp(which, "C", 1))
		RETURN_STR(compile_info);
	else if (!my_strnicmp(which, "S", 1))
		RETURN_STR(info_c_sum);

	else
*/
		return m_sprintf("%s+%s", version, compile_time_options);
	/* more to be added as neccesary */
}

/*
 * Based on a contribution made a very long time ago by wintrhawk
 */
BUILT_IN_FUNCTION(function_channelmode, word)
{
	char	*channel;
	char    *booya = NULL;
	char	*mode;

	do
	{
		channel = new_next_arg(word, &word);
		if ((!channel || !*channel) && booya)
			break;

		mode = get_channel_mode(channel, current_window->server);
		m_s3cat(&booya, space, (mode && *mode) ? mode : "*");
	}
	while (word && *word);

	return (booya ? booya : m_strdup(empty_string));
}

BUILT_IN_FUNCTION(function_geom, words)
{
	/* Erf. CO and LI are ints. (crowman) */
	return m_sprintf("%d %d", TI_cols, TI_lines);
}

BUILT_IN_FUNCTION(function_pass, words)
{
	char *lookfor;
	char *final, *ptr;

	GET_STR_ARG(lookfor, words);
	final = (char *)new_malloc(strlen(words) + 1);
	ptr = final;

 	while (*words)
	{
		if (strchr(lookfor, *words))
			*ptr++ = *words;
		words++;
	}

	*ptr = 0;
	return final;
}

BUILT_IN_FUNCTION(function_uptime, input)
{
	time_t  ltime;
	time_t  days,hours,minutes,seconds;
	struct  timeval         tp;
	static time_t timestart = 0;
	time_t timediff;
	char buffer[BIG_BUFFER_SIZE+1];

	*buffer = '\0';

	get_time(&tp);
	if (timestart == 0)  
	{
		timestart = tp.tv_sec;
		timediff = 0;
	} else
		timediff = tp.tv_sec - timestart;

	ltime = timediff;
	seconds = ltime % 60;
	ltime = (ltime - seconds) / 60;
	minutes = ltime%60;
	ltime = (ltime - minutes) / 60;
	hours = ltime % 24;
	days = (ltime - hours) / 24;
	sprintf(buffer, "%ldd %ldh %ldm %lds", days, hours, minutes, seconds);
	RETURN_STR(buffer);
}

char *function_eleet _((char *input))
{
	char temp[BIG_BUFFER_SIZE];
	register int  tindex = 0;

	RETURN_IF_EMPTY(input);

	strmcpy(temp,input, sizeof(temp)-1);
	while(temp[tindex])
	{
		switch(temp[tindex])
		{
			case 'e':
			case 'E':
				temp[tindex] = '3';
				break;
			case 'i':
			case 'I':
				temp[tindex] = '1';
				break;
			case 't':
			case 'T':
				temp[tindex] = '+';
				break;
			case 'o':
			case 'O':
				temp[tindex] = '0';
				break;
			case 'c':
			case 'C':
				temp[tindex] = '(';
				break;
			case 's':
			case 'S':
				temp[tindex] = '5';
				break;
			case 'l':
			case 'L':
				temp[tindex] = '|';
				break;
			case 'a':
			case 'A':			
				temp[tindex] = '4';
				break;
			default:
				break;
		}
		tindex++;
	}
	RETURN_STR(temp);
}

char *function_cool _((char *input))
{
	char temp[BIG_BUFFER_SIZE];
	register int  cindex = 0;

	RETURN_IF_EMPTY(input);
	strmcpy(temp, input, sizeof(temp)-1);
	while(*(temp + cindex))
	{
		temp[cindex] = (char)toupper((int)temp[cindex]);
		cindex++;
		if (temp[cindex])
		{
			temp[cindex] = (char)tolower((int)temp[cindex]);
			cindex++;
		}
	}
	RETURN_STR(temp);
}

char *function_cool2 _((char *input))
{
	char temp[BIG_BUFFER_SIZE + 1];
	register int  cindex = 0;

	RETURN_IF_EMPTY(input);
	strmcpy(temp, input, BIG_BUFFER_SIZE);
	while(temp[cindex])
	{
		switch(tolower(temp[cindex]))
		{
			case 'a':
			case 'e':
			case 'i':
			case 'o':
			case 'u':
				temp[cindex] = (char)tolower((int)temp[cindex]);
				break;
			default:
				temp[cindex] = (char)toupper((int)temp[cindex]);
		}
		cindex++;
	}
	RETURN_STR(temp);
}

char *function_annoy _((char * input))
{
	char temp[BIG_BUFFER_SIZE];
	register int aindex = 0, rnum, r2num;

	RETURN_IF_EMPTY(input);

	strmcpy(temp, input, sizeof(temp)-1);
	while (*input)
	{
		rnum = (rand() % 4);
		r2num = (rand() % 3);
		switch(rnum)
		{
			case 0:
				if (r2num == 0)
					temp[aindex++] = *input;
				else if (r2num == 1)
					temp[aindex++] = (char)tolower((int)*input);
				else if (r2num == 2)
					temp[aindex++] = (char)toupper((int)*input);
				input++;
				break;
			case 1:
				temp[aindex++] = '\002';
                                if (r2num == 0)
                                        temp[aindex++] = *input;
                                else if (r2num == 1)
                                        temp[aindex++] = (char)tolower((int)*input);
                                else if (r2num == 2)
                                        temp[aindex++] = (char) toupper((int)*input);
				temp[aindex++] = '\002';
				input++;
				break;
			case 2:
                                temp[aindex++] = '\026';
                                if (r2num == 0)
                                        temp[aindex++] = *input;
                                else if (r2num == 1)
                                        temp[aindex++] = (char)tolower((int)*input);
                                else if (r2num == 2)
                                        temp[aindex++] = (char)toupper((int)*input);
                                temp[aindex++] = '\026';
                                input++;
                                break;
        		case 3:
                                temp[aindex++] = '\037';
                                if (r2num == 0)
                                        temp[aindex++] = *input;
                                else if (r2num == 1)
                                        temp[aindex++] = (char)tolower((int)*input);
                                else if (r2num == 2)
                                        temp[aindex++] = (char)toupper((int)*input);
                                temp[aindex++] = '\037';
                                input++;
                                break;
		}
	}
	RETURN_STR(temp);
}

BUILT_IN_FUNCTION(function_cluster, input)
{
char *q;
	RETURN_IF_EMPTY(input);
	if ((q = cluster(input)))
		RETURN_STR(q);
	else
		RETURN_EMPTY;
}

BUILT_IN_FUNCTION(function_checkshit, input)
{
char *uh = NULL;
char *channel = NULL;
register ShitList *tmp;
int matched = 0;
	GET_STR_ARG(uh, input);
	GET_STR_ARG(channel, input);	

	for (tmp = shitlist_list; tmp; tmp = tmp->next)
	{
		if (wild_match(tmp->filter, uh) && check_channel_match(tmp->channels, channel))
		{
			matched = 1;
			break;
		}
	}

	if (tmp && matched)
		return m_sprintf("%d %s %s %s", tmp->level, tmp->channels, tmp->filter, tmp->reason);
	RETURN_EMPTY;
}

BUILT_IN_FUNCTION(function_get_info, input)
{
	char *nick;
	register UserList *tmp;
	void *needed = NULL;
	int size = -1;
	GET_STR_ARG(nick, input);
	for (tmp = next_userlist(NULL, &size, &needed); tmp; tmp = next_userlist(tmp, &size, &needed))
	{
		if (!my_stricmp(nick, tmp->nick) || wild_match(nick, tmp->nick))
		{
			if (tmp->comment)
				RETURN_STR(tmp->comment);
		}
	}
	RETURN_EMPTY;
}

BUILT_IN_FUNCTION(function_set_info, input)
{
	char *nick;
	register UserList *tmp;
	void *location = NULL;
	int size = -1;
	int done = 0;
	GET_STR_ARG(nick, input);
	for (tmp = next_userlist(NULL, &size, &location); tmp; tmp = next_userlist(tmp, &size, &location))
	{
		if (!my_stricmp(tmp->nick, nick) || wild_match(nick, tmp->nick))
		{
			if (input && *input)
				malloc_strcpy(&tmp->comment, input);
			else
				new_free(&tmp->comment);
			done++;
		}
	}
	if (done)
		RETURN_INT(1);
	RETURN_INT(0);
}

BUILT_IN_FUNCTION(function_checkuser, input)
{
char *uh = NULL;
char *channel = NULL;
register UserList *tmp;
	GET_STR_ARG(uh, input);
	GET_STR_ARG(channel, input);	
	if (!uh || !*uh || !channel || !*channel)
		RETURN_EMPTY;
	if ((tmp = find_bestmatch("*", uh, channel, NULL)))
		return m_sprintf("%d %s %s %s", tmp->flags, tmp->host, tmp->channels, tmp->password?tmp->password:empty_string);
	RETURN_EMPTY;
}

BUILT_IN_FUNCTION(function_rot13, input)
{
	char temp[BIG_BUFFER_SIZE];
	register char *p = NULL;
	int rotate = 13;
	strmcpy(temp, input, sizeof(temp)-1);
	for (p = temp; *p; p++) {
		if (*p >= 'A' && *p <='Z')
			*p = (*p - 'A' + rotate) % 26 + 'A';
		else if (*p >= 'a' && *p <= 'z')
			*p = (*p - 'a' + rotate) % 26 + 'a';
	}
	RETURN_STR(temp);
}

BUILT_IN_FUNCTION(function_flash, input )
{
int num;
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_INT_ARG(num, input);
	switch (num)
	{
		case 3:	RETURN_STR("**\030B00");
		case 4:	RETURN_STR("\033**EMSI_IRQ8E08");
		case 5: RETURN_STR("[[999999999999999C[1;2;3;4;5m");
		case 6: RETURN_STR("[[99999999999C");
		case 2:	RETURN_STR("\033[2J\033#8\033[?3;5h\033(0");
		case 1:
		default: RETURN_STR("\033#8\033[?3;5h\033(0]");
			break;
	}
	RETURN_EMPTY;
}


BUILT_IN_FUNCTION(function_repeat, words)
{
	register int num;
	char *final = NULL;

	GET_INT_ARG(num, words);
	if (num < 1)
		RETURN_EMPTY;

	final = (char *)new_malloc(strlen(words) * num + 1);
	for (; num > 0; num--)
		strcat(final, words);
	if (strlen(final) > BIG_BUFFER_SIZE)
		final[BIG_BUFFER_SIZE] = 0;
	return final;
}


BUILT_IN_FUNCTION(function_bcopy, words)
{
	int from, to;
	if (in_cparse)
		RETURN_INT(0);
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_INT_ARG(from, words);
	GET_INT_ARG(to, words);	
	RETURN_INT(file_copy(from, to)); 	
}

BUILT_IN_FUNCTION(function_epic, words)
{
	RETURN_INT(1);
}

BUILT_IN_FUNCTION(function_winsize, words)
{
	int refnum;
	Window *win;

	if (words && *words)
	{
		GET_INT_ARG(refnum, words);
		win = get_window_by_refnum(refnum);
	}
	else
		win = current_window;

	if (!win)
		RETURN_EMPTY;
		
	RETURN_INT(win->display_size);
}

BUILT_IN_FUNCTION(function_umode, words)
{
	int servnum;

	if (words && *words)
	{
		GET_INT_ARG(servnum, words);
	}
	else
		servnum = from_server;

	RETURN_STR(get_umode(servnum));
}


BUILT_IN_FUNCTION(function_lastnotice, words)
{
	int count = 0;
	char *str = NULL;
	GET_INT_ARG(count, words);
	if (count >= MAX_LAST_MSG)
		count = 0;
	RETURN_IF_EMPTY(last_notice[count].last_msg);
	malloc_sprintf(&str, "%s %s %s %s %s", last_notice[count].time, last_notice[count].from, last_notice[count].uh, last_notice[count].to, last_notice[count].last_msg);
	return str;
}

BUILT_IN_FUNCTION(function_lastmessage, words)
{
	int count = 0;
	char *str = NULL;
	GET_INT_ARG(count, words);
	if (count >= MAX_LAST_MSG)
		count = 0;
	RETURN_IF_EMPTY(last_msg[count].last_msg);
	malloc_sprintf(&str, "%s %s %s %s %s", last_msg[count].time, last_msg[count].from, last_msg[count].uh, last_msg[count].to, last_msg[count].last_msg);
	return str;
}

char *function_addtabkey _((char *words))
{
char *arrayname = NULL;
char *nick = NULL;

	GET_STR_ARG(nick, words);
	if (words && *words)
		arrayname = new_next_arg(words, &words);
	addtabkey(nick, NULL, arrayname ? 1: 0);
	RETURN_EMPTY;
}

char *function_gettabkey _((char *words))
{
char *arrayname = NULL;
NickTab *nick = NULL;
int direction;

	GET_INT_ARG(direction, words);
	
	if (words && *words)
		arrayname = new_next_arg(words, &words);
	
	if (arrayname && !my_stricmp(arrayname, "AUTOREPLY"))
		nick = gettabkey(direction, 1, NULL);
	else
		nick = gettabkey(direction, 0, NULL);
	
	return nick ? m_strdup(nick->nick) : m_strdup("");
}

static int sort_it (const void *one, const void *two)
{
	return my_stricmp(*(char **)one, *(char **)two);
}

BUILT_IN_FUNCTION(function_sort, words)
{
	int wordc;
	char **wordl;

	wordc = splitw(words, &wordl);
	qsort((void *)wordl, wordc, sizeof(char *), sort_it);
	return unsplitw(&wordl, wordc);	/* DONT USE RETURN_STR() HERE */
}

BUILT_IN_FUNCTION(function_notify, words)
{
	int showon = -1, showserver = from_server;
	char *firstw;

	while (words && *words)
	{
		firstw = new_next_arg(words, &words);
 		if (!my_strnicmp(firstw, "on", 2))
 		{
 			showon = 1;
 			continue;
		}
		if (!my_strnicmp(firstw, "off", 3))
		{
			showon = 0;
			continue;
		}
		if (!my_strnicmp(firstw, "serv", 4))
		{
			GET_INT_ARG(showserver, words);
 		}
	}
 
	/* dont use RETURN_STR() here. */
	return get_notify_nicks(showserver, showon);
}

static int num_sort_it (const void *one, const void *two)
{
	char *oneptr = *(char **)one;
	char *twoptr = *(char **)two;
	long val1, val2;

	while (*oneptr && *twoptr)
	{
		while (*oneptr && *twoptr && !isdigit(*oneptr) && !isdigit(*twoptr))
		{
			if (*oneptr != *twoptr)
				return (*oneptr - *twoptr);
			oneptr++, twoptr++;
		}

		if (!*oneptr || !*twoptr)
			break;

		val1 = strtol(oneptr, (char **)&oneptr, 10);
		val2 = strtol(twoptr, (char **)&twoptr, 10);
		if (val1 != val2)
			return val1 - val2;
	}
	return (*oneptr - *twoptr);
}

BUILT_IN_FUNCTION(function_numsort, words)
{
	int wordc;
	char **wordl;

	wordc = splitw(words, &wordl);
	qsort((void *)wordl, wordc, sizeof(char *), num_sort_it);

	return unsplitw(&wordl, wordc);	/* DONT USE RETURN_STR() HERE */
}

#ifdef NEED_GLOB
#define glob bsd_glob
#define globfree bsd_globfree
#endif

BUILT_IN_FUNCTION(function_glob, word)
{
#ifdef INCLUDE_GLOB_FUNCTION
	char *path, *path2, *retval = NULL;
	int numglobs, i;
	glob_t globbers;

#ifdef PUBLIC_ACCESS
	RETURN_EMPTY;
#endif
	memset(&globbers, 0, sizeof(glob_t));
	while (word && *word)
	{
		GET_STR_ARG(path, word);
		path2 = expand_twiddle(path);
		if (!path2)
			path2 = path;

		numglobs = glob(path2, GLOB_MARK, NULL, &globbers);
		if (numglobs < 0)
			RETURN_INT(numglobs);
		for (i = 0; i < globbers.gl_pathc; i++)
			m_s3cat(&retval, space, globbers.gl_pathv[i]);

		globfree(&globbers);
	}
	RETURN_IF_EMPTY(retval);
	return retval;
#else
	RETURN_EMPTY;
#endif
}

BUILT_IN_FUNCTION(function_mkdir, words)
{
	int 	failure = 0;
	char *expanded;

#ifdef PUBLIC_ACCESS
	RETURN_EMPTY;
#endif
	while (words && *words)
	{
		expanded = expand_twiddle(new_next_arg(words, &words));
		failure -= mkdir(expanded, 0777);
		new_free(&expanded);
	}

	RETURN_INT(failure);
}

BUILT_IN_FUNCTION(function_umask, words)
{
	int new_umask;
	GET_INT_ARG(new_umask, words);
	RETURN_INT(umask(new_umask));
}

extern char *get_help_topic _((char *, int));
extern int  read_file _((FILE *, int));

BUILT_IN_FUNCTION(function_help, words)
{
char *filename = NULL, *subject = NULL;
static int first_time = 1;
FILE *help_file;
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_STR_ARG(subject, words);
	if (words && *words)
		words = next_arg(filename, &words);
	else
		malloc_strcpy(&filename, get_string_var(SCRIPT_HELP_VAR));
	if (first_time && filename)
	{
		char *new_file = NULL;
		malloc_strcpy(&new_file, filename);
	        if (!(help_file = uzfopen(&new_file, ".", 0)))
        	{
			new_free(&new_file);
			RETURN_EMPTY;
		}
		first_time = 0;
		new_free(&new_file);
		read_file(help_file, 1);
		fclose(help_file);
	} else if (first_time) RETURN_EMPTY;
	return get_help_topic(subject, (filename) ? 1 : 0);
}

BUILT_IN_FUNCTION(function_isuser, words)
{
char *nick = NULL;
char *uhost = NULL;
char *channel = NULL;
register UserList *User;
register ShitList *Shit;

	GET_STR_ARG(nick, words);
	GET_STR_ARG(uhost, words);
	if (words && *words)
		channel = words;
	else
		channel = "*";
	if ((User = lookup_userlevelc("*", uhost, channel, NULL)))
		return m_sprintf("USER %d %s %s", User->flags, User->host, User->channels);
	if ((Shit = nickinshit(nick, uhost)) && check_channel_match(Shit->channels, channel))
		return m_sprintf("SHIT %s %d %s", Shit->filter, Shit->level, Shit->channels);
	RETURN_EMPTY;
}

/*
$pad(N string of text goes here)
if N is negative, it'll pad to the right 
if N is positive, it'll pad to the left

so something like: $pad(20 letters) would output:
            letters
and $pad(20 some string) would be:
        some string
GREAT way to allign shit, and if you use $curpos() can can add that and
figure out the indent :p
hohoho, aren't we ingenious, better yet, you can do a strlen() on the
string youw anna output, then - the curpos, and if its over, grab all
words to the right of that position and output them [so its on one line]
then indent, grab the next section of words, and then the next and then
next

   Jordy (jordy@thirdwave.net) 19960622 
*/

BUILT_IN_FUNCTION(function_pad, word)
{
	int	length = 0;
	GET_INT_ARG(length, word);
	return m_sprintf("%*s", length, word);
}

BUILT_IN_FUNCTION(function_isban, word)
{
	char *channel;
	char *ban;
	ShitList *Ban;
	GET_STR_ARG(channel, word);
	GET_STR_ARG(ban, word);
	if ((Ban = (ShitList *)find_in_list((List **)&shitlist_list, ban, 0)) && check_channel_match(channel, Ban->channels))
		RETURN_INT(1);
	RETURN_INT(0);
}

BUILT_IN_FUNCTION(function_isignored, word)
{
char *nick;
	GET_STR_ARG(nick, word);
	if (check_is_ignored(nick))
		RETURN_INT(1);
	RETURN_INT(0);
}

BUILT_IN_FUNCTION(function_banonchannel, word)
{
	ChannelList *chan;
	char *channel;
	char *ban;
	GET_STR_ARG(channel, word);
	GET_STR_ARG(ban, word);
	if ((chan = lookup_channel(channel, from_server, 0)))
	{
		if (ban_is_on_channel(ban, chan))
			RETURN_INT(1);
	}
	RETURN_INT(0);
}

BUILT_IN_FUNCTION(function_isop, word)
{
	char *channel;
	char *nick;
	ChannelList *chan;
	NickList *Nick;
	GET_STR_ARG(channel, word);
	GET_STR_ARG(nick, word);
	if ((chan = lookup_channel(channel, from_server, CHAN_NOUNLINK)))
	{
		if ((Nick = find_nicklist_in_channellist(nick, chan, 0)) &&
				(Nick->userlist && (Nick->userlist->flags & ADD_OPS)))
			RETURN_INT(1);
	}
	RETURN_INT(0);
}

BUILT_IN_FUNCTION(function_isvoice, word)
{
	char *channel;
	char *nick;
	ChannelList *chan;
	NickList *Nick;
	GET_STR_ARG(channel, word);
	GET_STR_ARG(nick, word);
	if ((chan = lookup_channel(channel, from_server, CHAN_NOUNLINK)))
	{
		if ((Nick = find_nicklist_in_channellist(nick, chan, 0)) && Nick->voice)
			RETURN_INT(1);
	}
	RETURN_INT(0);
}

BUILT_IN_FUNCTION(function_randomnick, word)
{
	RETURN_STR(random_str(3,9));
}


#ifndef BITCHX_LITE
BUILT_IN_FUNCTION(function_openserver, word)
{
int port = -1;
char *servern = NULL;
int socket_num = -1;
unsigned short this_sucks;
CloneList *new;
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_STR_ARG(servern, word);
	GET_INT_ARG(port, word);
	this_sucks = (unsigned short)port;
	if ((socket_num = connect_by_number(servern, &this_sucks, SERVICE_CLIENT, PROTOCOL_TCP, 1)) < 0)
		RETURN_INT(-1);

	new_open(socket_num);

	new = (CloneList *)new_malloc(sizeof(CloneList));
	new->number = m_sprintf("%d", socket_num);
	new->server = m_strdup(servern);
	new->port = port;
	new->socket_num = socket_num;
	add_to_list((List **)&clones, (List *)new);

	RETURN_INT(socket_num);
}

BUILT_IN_FUNCTION(function_closeserver, word)
{
int socket_num = -1;
CloneList *new;
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_INT_ARG(socket_num, word);
	if (socket_num != -1)
	{
		if ((new = (CloneList *)remove_from_list((List **)&clones, ltoa(socket_num))))
		{
			new_free(&new->server);
			new_free(&new->number);
			new_free((char **)&new);
			new_close(socket_num);
		}
	}	
	RETURN_INT(socket_num);
}

BUILT_IN_FUNCTION(function_readserver, word)
{
int socket_num = -1;
CloneList *new;
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_INT_ARG(socket_num, word);
	if (socket_num != -1 && (new = (CloneList *)find_in_list((List **)&clones, ltoa(socket_num), 0)) )
	{
		char buffer[IRCD_BUFFER_SIZE+1];
		char *str;
		char *s;
		*buffer = 0;
		str = buffer;
		switch(dgets(str, socket_num, 0))
		{
			case -1:
				socket_num = 0;
				break;
			case 0:
				FD_CLR(socket_num, &readables);
				socket_num = -1;
				break;
			default:
				if ((buffer[strlen(buffer)-1] == '\r') || (buffer[strlen(buffer)-1] == '\n'))
					buffer[strlen(buffer)-1] = 0;
				if ((buffer[strlen(buffer)-1] == '\r') || (buffer[strlen(buffer)-1] == '\n'))
					buffer[strlen(buffer)-1] = 0;
				s = m_sprintf("%d %s", strlen(buffer), buffer);
				new->warn = 0;
				return s;
		}
		new->warn = 0;
	}
	RETURN_INT(socket_num);
}

BUILT_IN_FUNCTION(function_writeserver, word)
{
int socket_num = -1;
int len = -1;
#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_INT_ARG(socket_num, word);
	if (socket_num != -1)
	{
		char buffer[IRCD_BUFFER_SIZE + 1];
		strncpy(buffer, word, IRCD_BUFFER_SIZE - 2);
		strmcat(buffer, "\n", IRCD_BUFFER_SIZE - 1);
		len = strlen(buffer);
		send(socket_num, buffer, len, 0);
	}
	RETURN_INT(len);
}
#endif

char *function_cparse(char *word)
{
char *format = NULL;
char *blah;
	GET_STR_ARG(format, word);
	in_cparse++;
	blah = convert_output_format(format, word && *word? "%s":NULL, word&&*word?word:NULL);
	in_cparse--;
	RETURN_STR(blah);
}

BUILT_IN_FUNCTION(function_getreason, word)
{
char *nick = NULL;
	GET_STR_ARG(nick, word);
	RETURN_STR(get_reason(nick, word));
}

BUILT_IN_FUNCTION(function_chmod, words)
{
	char *filearg, *after;
	int fd = -1;
	char *perm_s;
	mode_t perm;

#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	GET_STR_ARG(filearg, words);
	fd = (int) strtoul(filearg, &after, 10);
	GET_STR_ARG(perm_s, words);
	perm = (mode_t) strtol(perm_s, &perm_s, 8);

	if (after != words && *after == 0)
	{
		if (file_valid(fd))
#if defined(__EMX__) || defined(__OPENNT)
			RETURN_INT(0644);
#else
			RETURN_INT(fchmod(fd, perm));
#endif
		else
			RETURN_EMPTY;
	}
	else
	{
		char *expanded;
		int retval;
		expanded = expand_twiddle(filearg);
		retval = chmod(expanded, perm);
		new_free(&expanded);
		RETURN_INT(retval);
	}
}

BUILT_IN_FUNCTION(function_twiddle, words)
{
	if (words && *words)
		return expand_twiddle(new_next_arg(words, &words));

	RETURN_EMPTY;
}


/* 
 * Date: Sun, 29 Sep 1996 19:17:25 -0700
 * Author: Thomas Morgan <tmorgan@pobox.com>
 * Submitted-by: Archon <nuance@twri.tamu.edu>
 *
 * $uniq (string of text)
 * Given a set of words, returns a new set of words with duplicates
 * removed.
 * EX: $uniq(one two one two) returns "one two"
 */
BUILT_IN_FUNCTION(function_uniq, word)
{
        char    **list = NULL, *booya = NULL;
	char 	*p;
        int     listc, listi;

        listc = splitw(word, &list);

        for (listi = 0; listi < listc; listi++)
        {
                if (booya == NULL)
                        m_s3cat(&booya, space, list[listi]);
                else
                {
                        char *rmatch_input =
                                malloc(strlen(list[listi])
                                       + strlen(booya) + 2);

                        strcpy(rmatch_input, list[listi]);
                        strcat(rmatch_input, space);
                        strcat(rmatch_input, booya);

                        if (!strcmp((p = function_rmatch(rmatch_input)),zero))
                                m_s3cat(&booya, space, list[listi]);
			new_free(&p);
                        free(rmatch_input);
                }
        }

        new_free((char **)&list);
	RETURN_MSTR(booya);
}

BUILT_IN_FUNCTION(function_uhost, word)
{
char *nick;
char *answer = NULL;
int i;
register ChannelList *chan;
NickList *n;
	GET_STR_ARG(nick, word);
	for (i = 0; i < number_of_servers; i++)
	{
		for (chan = server_list[i].chan_list; chan; chan = chan->next)
		{
			if ((n = find_nicklist_in_channellist(nick, chan, 0)))
			{
				malloc_strcat(&answer, n->host);
				malloc_strcat(&answer, " ");
			}
		}
	}
	if (!answer)
		RETURN_EMPTY;
	return answer;
}

BUILT_IN_FUNCTION(function_numdiff, word)
{
int value1 = 0, value2 = 0;
	GET_INT_ARG(value1, word);
	GET_INT_ARG(value2, word);
	RETURN_INT(value1-value2);
}

BUILT_IN_FUNCTION(function_winvisible, word)
{
	RETURN_INT(get_visible_by_refnum(word));
}

#ifndef BITCHX_LITE1
BUILT_IN_FUNCTION(function_mircansi, word)
{
extern char *mircansi(char *);
	RETURN_STR(mircansi(word));
}
#endif

BUILT_IN_FUNCTION(function_getenv, word)
{
char *p;
char *q;
	GET_STR_ARG(q, word);
	p = getenv(q);
	if (p && *p)
		RETURN_STR(p);
	RETURN_EMPTY;
}

BUILT_IN_FUNCTION(function_gethost, word)
{
register ChannelList *chan;
register NickList *nick;
char *arg;
	GET_STR_ARG(arg, word);
	if (from_server != -1)
	{
		for (chan = server_list[from_server].chan_list; chan; chan = chan->next)
		{
			for (nick = next_nicklist(chan, NULL); nick; nick = next_nicklist(chan, nick))
			{
				if (!my_stricmp(arg, nick->nick))
					RETURN_STR(nick->host);
			}
		}
	}
	RETURN_EMPTY;
}

BUILT_IN_FUNCTION(function_getvar, words)
{
	char *alias;
	int num = 0;
	if ((alias = (char *)get_cmd_alias(upper(words), &num, NULL)))
		RETURN_STR(alias);
	RETURN_EMPTY;	
}


BUILT_IN_FUNCTION(function_aliasctl, input)
{
	extern char *aliasctl (char *);
	return aliasctl(input);
}

BUILT_IN_FUNCTION(function_status, word)
{
	int window_refnum;
	int status_line;

	GET_INT_ARG(window_refnum, word);
	GET_INT_ARG(status_line, word);
	RETURN_STR(get_status_by_refnum(window_refnum, status_line));
}
extern char *stat_convert_format _((Window *, char *));
BUILT_IN_FUNCTION(function_statsparse, word)
{
	return stat_convert_format(current_window, word);
}

BUILT_IN_FUNCTION(function_absstrlen, word)
{
char *tmp;
	if (!word || !*word)
		RETURN_INT(0);
	tmp = stripansicodes(word);
	RETURN_INT(strlen(tmp));
}

BUILT_IN_FUNCTION(function_findw, input)
{
	char	*word, *this_word;
	int	word_cnt = 0;

	GET_STR_ARG(word, input);

	while (input && *input)
	{
		GET_STR_ARG(this_word, input);
		if (!my_stricmp(this_word, word))
			RETURN_INT(word_cnt);

		word_cnt++;
	}

	RETURN_INT(-1);
}

BUILT_IN_FUNCTION(function_countansi, input)
{
int len = 0;
	if (input && *input)
		len = count_ansi(input, -1);
	RETURN_INT(len);		
}

BUILT_IN_FUNCTION(function_longip, word)
{
	char *blah;
	struct in_addr addr = {0};
	GET_STR_ARG(blah, word);
	if (inet_aton(blah, &addr))
		return m_sprintf("%lu", (unsigned long)htonl(addr.s_addr));
	RETURN_EMPTY;
}

#include  <stdlib.h>
BUILT_IN_FUNCTION(function_rword, word)
{
int num = 0;
long rn;
char *p;
#ifndef RAND_MAX
#define RAND_MAX 2147483647
#endif
	RETURN_IF_EMPTY(word);
	num = word_count(word);
	rn = (int)(((float)num)*rand()/(RAND_MAX+1.0));
	p = extract(word, rn, rn);		
	return p;
}

BUILT_IN_FUNCTION(function_winlen, word)
{
int refnum;
int len = 0;
Window *win = current_window;
	if (word && *word)
	{
		refnum = my_atol(word);
		win = get_window_by_refnum(refnum);
		if (!win)
			RETURN_INT(-1);
	}
	len = win->display_size;
	RETURN_INT(len);
}

BUILT_IN_FUNCTION(function_channel, word)
{
char *chan;
ChannelList     *channel = NULL; /* XXX */
NickList        *tmp = NULL;
char		*nicks = NULL;
	chan = next_arg(word, &word);
	if (!chan) 
		chan = get_current_channel_by_refnum(0);
	RETURN_IF_EMPTY(chan);
	if ((channel = lookup_channel(chan, current_window->server, 0)))
	{
		NickList *list = NULL;
		list = sorted_nicklist(channel);
		for (tmp = list; tmp; tmp = tmp->next)
		{
			if (tmp->ircop)
				malloc_strcat(&nicks, "*");
			if (tmp->chanop) 
				m_3cat(&nicks, "@", tmp->nick);
			else if (tmp->voice)
				m_3cat(&nicks, "+", tmp->nick);
			else
				m_3cat(&nicks, ".", tmp->nick);
			malloc_strcat(&nicks, " ");
		}		
		clear_sorted_nicklist(&list);
	}
	return (nicks ? nicks : m_strdup(empty_string));
	
}

/* 
 * Given a channel, tells you what window its bound to.
 * Given a window, tells you what channel its bound to.
 */
BUILT_IN_FUNCTION(function_winbound, input)
{
	Window *foo;
	char *stuff;

	stuff = new_next_arg(input, &input);
	if (my_atol(stuff) && (foo = get_window_by_refnum((unsigned)my_atol(stuff))))
		RETURN_STR(get_bound_channel(foo));
	else if ((foo = get_window_by_name(stuff)))
		RETURN_STR(get_bound_channel(foo));
	else if ((foo = get_window_bound_channel(stuff)))
		RETURN_STR(get_refnum_by_window(foo));

	RETURN_EMPTY;
}

BUILT_IN_FUNCTION(function_ftime, words)
{
        char	FileBuf[BIG_BUFFER_SIZE+1];
	char	*filename, *fullname;
	struct stat s;

#ifdef PUBLIC_ACCESS
	RETURN_INT(0);
#endif
	*FileBuf = 0;
	if ((filename = new_next_arg(words, &words)))
	{
		if (*filename == '/')
			strcpy(FileBuf, filename);

		else if (*filename == '~') 
		{
			if (!(fullname = expand_twiddle(filename)))
				RETURN_EMPTY;

			strmcpy(FileBuf, fullname, BIG_BUFFER_SIZE);
			new_free(&fullname);
		}
		else 
		{
			getcwd(FileBuf, BIG_BUFFER_SIZE);
			strmcat(FileBuf, "/", BIG_BUFFER_SIZE);
			strmcat(FileBuf, filename, BIG_BUFFER_SIZE);
		}

		if (stat(FileBuf, &s) == -1)
			RETURN_EMPTY;
		else
			RETURN_INT(s.st_mtime);
	}
	RETURN_EMPTY;
}

BUILT_IN_FUNCTION(function_irclib, input)
{
	RETURN_STR(irc_lib);
}

BUILT_IN_FUNCTION(function_country, input)
{
extern char *country(char *);
	RETURN_STR(country(input));
}

BUILT_IN_FUNCTION(function_servernick, input)
{
	char *servdesc;
	int refnum;

	GET_STR_ARG(servdesc, input);
	if ((refnum = parse_server_index(servdesc)) == -1)
		if ((refnum = find_in_server_list(servdesc, 0)) == -1)
			RETURN_EMPTY;

	RETURN_STR(get_server_nickname(refnum));
}
 
BUILT_IN_FUNCTION(function_fparse, input)
{
char *format;
char *f = NULL;
Alias *a = NULL;
int old_window_display = window_display;
	GET_STR_ARG(f, input);
	if (!f || !*f)
		RETURN_EMPTY;
	if ((format = make_fstring_var(f)))
	{
		f = convert_output_format(format, "%s", input?input:empty_string);
		new_free(&format);
		RETURN_STR(f);
	} 
	window_display = 0;
	if ((a = find_var_alias(f)) && a->stuff && input && *input)
		f = convert_output_format(a->stuff, "%s", input?input:empty_string);
	else if (input && *input)
		add_var_alias(f, input);
	else
		delete_var_alias(f);
	window_display = old_window_display;
	RETURN_STR(f);
}

BUILT_IN_FUNCTION(function_isconnected, input)
{
int server = 0;
	GET_INT_ARG(server, input);
	if (is_server_connected(server))
		RETURN_INT(1);
	RETURN_INT(0);
}

BUILT_IN_FUNCTION(function_bmatch, input)
{
char *to = NULL;
char *p, *q;
	GET_STR_ARG(to, input);
	p = q = m_strdup(input);
	while((p = next_arg(p, &p)))
	{
		if (wild_match(to, p))
		{
			char *ret = m_strdup(p);
			new_free(&q);
			return ret;
		}
	}
	new_free(&q);	
	RETURN_EMPTY;
}

BUILT_IN_FUNCTION(function_currchans, input)
{
	int server = -1;
	Window *blah = NULL;
	char *retval = NULL;

	if (input && *input)
		GET_INT_ARG(server, input)
	else
		server = -2;

	if (server == -1)
		server = from_server;

	while (traverse_all_windows(&blah))
 	{
		if (server != -2 && blah->server != server)
			continue;
		if (!blah->current_channel)
			continue;

		m_s3cat(&retval, space, "\"");
		malloc_strcat(&retval, ltoa(blah->server));
		malloc_strcat(&retval, space);
		malloc_strcat(&retval, blah->current_channel);
		malloc_strcat(&retval, "\"");
	}
	RETURN_MSTR(retval);
}

/*
 * Returns 1 if the specified function name is a built in function,
 * Returns 0 if not.
 */
BUILT_IN_FUNCTION(function_fnexist, input)
{
	char *fname;

	GET_STR_ARG(fname, input);
	RETURN_INT(func_exist(fname));
}

BUILT_IN_FUNCTION(function_cexist, input)
{
	char *fname;

	GET_STR_ARG(fname, input);
	RETURN_INT(command_exist(fname));
}

#ifdef HAVE_REGEXP_H
/*
 * These are used as an interface to regex support.  Here's the plan:
 *
 * $regcomp(<pattern>) 
 *	will return an $encode()d string that is suitable for
 * 		assigning to a variable.  The return value of this
 *		function must at some point be passed to $regfree()!
 *
 * $regexec(<compiled> <string>)
 *	Will return "0" or "1" depending on whether or not the given string
 *		was matched by the previously compiled string.  The value
 *		for <compiled> must be a value previously returned by $regex().
 *		Failure to do this will result in undefined chaos.
 *
 * $regerror(<compiled>)
 *	Will return the error string for why the previous $regmatch() or 
 *		$regex() call failed.
 *
 * $regfree(<compiled>)
 *	Will free off any of the data that might be contained in <compiled>
 *		You MUST call $regfree() on any value that was previously
 *		returned by $regex(), or you will leak memory.  This is not
 *		my problem (tm).  It returns the FALSE value.
 */

static int last_error = 0; 		/* XXX */

BUILT_IN_FUNCTION(function_regcomp, input)
{
	regex_t preg;

	last_error = regcomp(&preg, input, REG_EXTENDED | REG_ICASE | REG_NOSUB);
	return encode((char *)&preg, sizeof(regex_t));
}

BUILT_IN_FUNCTION(function_regexec, input)
{
	char *unsaved;
	regex_t *preg;
	int retval;

	GET_STR_ARG(unsaved, input);
	preg = (regex_t *)decode(unsaved);
	retval = regexec(preg, input, 0, NULL, 0);
	new_free((char **)&preg);
	RETURN_INT(retval);		/* DONT PASS FUNC CALL TO RETURN_INT */
}

BUILT_IN_FUNCTION(function_regerror, input)
{
	char *unsaved;
	regex_t *preg;
	char error_buf[1024];
	int errcode;

	GET_INT_ARG(errcode, input);
	GET_STR_ARG(unsaved, input);
	preg = (regex_t *)decode(unsaved);

	if (errcode == -1)
		errcode = last_error;
	regerror(errcode, preg, error_buf, 1023);
	new_free((char **)&preg);
	RETURN_STR(error_buf);
}

BUILT_IN_FUNCTION(function_regfree, input)
{
	char *unsaved;
	regex_t *preg;

	GET_STR_ARG(unsaved, input);
	preg = (regex_t *)decode(unsaved);
	regfree(preg);
	new_free((char **)&preg);
	RETURN_EMPTY;
}
#else
BUILT_IN_FUNCTION(function_regexec, input)  { RETURN_EMPTY; }
BUILT_IN_FUNCTION(function_regcomp, input)  { RETURN_EMPTY; }
BUILT_IN_FUNCTION(function_regerror, input) { RETURN_STR("no regex support"); }
BUILT_IN_FUNCTION(function_regfree, input)  { RETURN_EMPTY; }
#endif

BUILT_IN_FUNCTION(function_count, input)
{
	char 	*str, *str2;
	int 	count = 0;

	GET_STR_ARG(str, input);
	str2 = input;

	for (;;)
	{
		if ((str2 = stristr(str2, str)))
			count++, str2++;
		else
			break;
	}

	RETURN_INT(count);
}

/*
 * Usage: $leftpc(COUNT TEXT)
 * Return the *longest* initial part of TEXT that includes COUNT printable
 * characters.
 */
BUILT_IN_FUNCTION(function_leftpc, word)
{
	u_char  **prepared = NULL;
	int	lines = 1;
	int	count;

	GET_INT_ARG(count, word);
	if (count <= 0)
		RETURN_EMPTY;

	prepared = prepare_display(word, count, &lines, PREPARE_NOWRAP);
	RETURN_STR((char *)prepared[0]);
}
/*
 * $uname()
 * Returns system information.  Expandoes %a, %s, %r, %v, %m, and %n
 * correspond to uname(1)'s -asrvmn switches.  %% expands to a literal
 * '%'.  If no arguments are given, the function returns %s and %r (the
 * same information given in the client's ctcp version reply).
 *
 * Contributed by Matt Carothers (CrackBaby) on Dec 20, 1997
 */
BUILT_IN_FUNCTION(function_uname, input)
{
#ifndef HAVE_UNAME
	RETURN_STR("unknown");
#else
	struct utsname un;
	char 	tmp[BIG_BUFFER_SIZE+1];
	char	*ptr = tmp;
	int 	i;
	int	len;

	if (uname(&un))
		RETURN_STR("unknown");

	if (!*input)
		input = "%s %r";

	*tmp = 0;
	memset(tmp, 0, BIG_BUFFER_SIZE);
	for (i = 0, len = strlen(input); i < len; i++)
	{
		if (ptr - tmp >= BIG_BUFFER_SIZE - 100)
			break;

		if (input[i] == '%') 
		{
		    switch (input[++i]) 
		    {
			case 'm':	strcat(ptr, un.machine);
					break;
			case 'n':	strcat(ptr, un.nodename);
					break;
			case 'r':	strcat(ptr, un.release);
					break;
			case 's':	strcat(ptr, un.sysname);
					break;
			case 'v':	strcat(ptr, un.version);
					break;
			case 'a':	strcat(ptr, un.sysname);
					strcat(ptr, " ");
					strcat(ptr, un.nodename);
					strcat(ptr, " ");
					strcat(ptr, un.release);
					strcat(ptr, " ");
					strcat(ptr, un.version);
					strcat(ptr, " ");
					strcat(ptr, un.machine);
					break;
			case '%':	strcat(ptr, "%");
		    }
		    ptr += strlen(ptr);
		}
		else
		    *ptr++ = input[i];
	}
	*ptr = 0;

	RETURN_STR(tmp);
#endif
}

BUILT_IN_FUNCTION(function_querywin, args)
{
	Window *w = NULL;

	while (traverse_all_windows(&w))
		if (w->query_nick && !my_stricmp(w->query_nick, args))
			RETURN_INT(w->refnum);

	RETURN_INT(-1);
}

BUILT_IN_FUNCTION(function_winrefs, args)
{
	Window *w = NULL;
	char *retval = NULL;

	while (traverse_all_windows(&w))
		m_s3cat(&retval, space, ltoa(w->refnum));

	return retval;		/* DONT MALLOC THIS! */
}


#define DOT (*host ? dot : empty_string)

BUILT_IN_FUNCTION(function_uhc, input)
{
	char 	*nick, *user, *host, *domain;
	int	ip;

	if (!input || !*input)
		RETURN_EMPTY;

	if (figure_out_address(input, &nick, &user, &host, &domain, &ip))
		RETURN_STR(input);
	else if (ip == 0)
		return m_sprintf("%s!%s@%s%s%s", nick, user, host, DOT, domain);
	else
		return m_sprintf("%s!%s@%s%s%s", nick, user, domain, DOT, host);
}

/*
 * Opposite of $uhc().  Breaks down a complete nick!user@host
 * into smaller components, based upon absolute wildcards.
 * Eg, *!user@host becomes user@host.  And *!*@*.host becomes *.host
 * Based on the contribution and further changes by Peter V. Evans 
 * (arc@anet-chi.com)
 */
BUILT_IN_FUNCTION(function_deuhc, input)
{
	char	*buf;
 
	buf = alloca(strlen(input) + 1);
	strcpy(buf, input);
 
	if (!strchr(buf, '!') || !strchr(buf, '@'))
		RETURN_EMPTY;

	if (!strncmp(buf, "*!", 2))
	{
		buf += 2;
		if (!strncmp(buf, "*@", 2))
			buf += 2;
	}

	RETURN_STR(buf);
}


/*
 * $mask(type address)      OR
 * $mask(address type)
 * Returns address with a mask specified by type.
 *
 * $mask(1 nick!khaled@mardam.demon.co.uk)  returns *!*khaled@mardam.demon.co.uk
 * $mask(2 nick!khaled@mardam.demon.co.uk)  returns *!*@mardam.demon.co.uk
 *
 * The available types are:
 *
 * 0: *!user@host.domain
 * 1: *!*user@host.domain
 * 2: *!*@host.domain
 * 3: *!*user@*.domain
 * 4: *!*@*.domain
 * 5: nick!user@host.domain
 * 6: nick!*user@host.domain
 * 7: nick!*@host.domain
 * 8: nick!*user@*.domain
 * 9: nick!*@*.domain
 * 10: *!*@<number-masked-host>.domain		(matt)
 * 11: *!*user@<number-masked-host>.domain	(matt)
 * 12: nick!*@<number-masked-host>.domain	(matt)
 * 13: nick!*user@<number-masked-host>.domain	(matt)
 */
BUILT_IN_FUNCTION(function_mask, args)
{
	char *	nuh;
	char *	nick = NULL;
	char *	user = NULL;
	char *	host = NULL;
	char *	domain = NULL;
	int   	which;
	char	stuff[BIG_BUFFER_SIZE + 1];
	int	ip;
	char	*first_arg;
	char	*ptr;

	first_arg = new_next_arg(args, &args);
	if (is_number(first_arg))
	{
		which = my_atol(first_arg);
		nuh = args;
	}
	else
	{
		nuh = first_arg;
		GET_INT_ARG(which, args);
	}

	if (figure_out_address(nuh, &nick, &user, &host, &domain, &ip))
		RETURN_EMPTY;

	/*
	 * Deal with ~jnelson@acronet.net for exapmle, and all sorts
	 * of other av2.9 breakage.
	 */
	if (strchr("~^-+=", *user))
		*user = '*';

	/* Make sure 'user' isnt too long for a ban... */
	if (strlen(user) > 7)
	{
		user[7] = '*';
		user[8] = 0;
	}

#define USER (user == star ? empty_string : user)
#define MASK1(x, y) snprintf(stuff, BIG_BUFFER_SIZE, x, y); break;
#define MASK2(x, y, z) snprintf(stuff, BIG_BUFFER_SIZE, x, y, z); break;
#define MASK3(x, y, z, a) snprintf(stuff, BIG_BUFFER_SIZE, x, y, z, a); break;
#define MASK4(x, y, z, a, b) snprintf(stuff, BIG_BUFFER_SIZE, x, y, z, a, b); break;
#define MASK5(x, y, z, a, b, c) snprintf(stuff, BIG_BUFFER_SIZE, x, y, z, a, b, c); break;

	if (ip == 0) 
	switch (which)
	{
		case 0:  MASK4("*!%s@%s%s%s",         user, host, DOT, domain)
		case 1:  MASK4("*!*%s@%s%s%s",        USER, host, DOT, domain)
		case 2:  MASK3("*!*@%s%s%s",                host, DOT, domain)
		case 3:  MASK3("*!*%s@*%s%s",         USER,       DOT, domain)
		case 4:  MASK2("*!*@*%s%s",                       DOT, domain)
		case 5:  MASK5("%s!%s@%s%s%s",  nick, user, host, DOT, domain)
		case 6:  MASK5("%s!*%s@%s%s%s", nick, USER, host, DOT, domain)
		case 7:  MASK4("%s!*@%s%s%s",   nick,       host, DOT, domain)
		case 8:  MASK4("%s!*%s@*%s%s",  nick, USER,       DOT, domain)
		case 9:  MASK3("%s!*@*%s%s",    nick,             DOT, domain)
		case 10: mask_digits(&host);
			 MASK3("*!*@%s%s%s",                host, DOT, domain)
		case 11: mask_digits(&host);
			 MASK4("*!*%s@%s%s%s",        USER, host, DOT, domain)
		case 12: mask_digits(&host);
			 MASK4("%s!*@%s%s%s",   nick,       host, DOT, domain)
		case 13: mask_digits(&host);
			 MASK5("%s!*%s@%s%s%s", nick, USER, host, DOT, domain)
	}
	else
	switch (which)
	{
		case 0:  MASK3("*!%s@%s.%s",          user, domain, host)
		case 1:  MASK3("*!*%s@%s.%s",         USER, domain, host)
		case 2:  MASK2("*!*@%s.%s",                 domain, host)
		case 3:  MASK2("*!*%s@%s.*",          USER, domain)
		case 4:  MASK1("*!*@%s.*",                  domain)
		case 5:  MASK4("%s!%s@%s.%s",   nick, user, domain, host)
		case 6:  MASK4("%s!*%s@%s.%s",  nick, USER, domain, host)
		case 7:  MASK3("%s!*@%s.%s",    nick,       domain, host)
		case 8:  MASK3("%s!*%s@%s.*",   nick, USER, domain)
		case 9:  MASK2("%s!*@%s.*",     nick,       domain)
		case 10: MASK1("*!*@%s.*",                  domain)
		case 11: MASK2("*!*%s@%s.*",          USER, domain)
		case 12: MASK2("%s!*@%s.*",     nick,       domain)
		case 13: MASK3("%s!*%s@%s.*",   nick, USER, domain)
	}

	/* Clean up any non-printable chars */
	for (ptr = stuff; *ptr; ptr++)
		if (!isgraph(*ptr))
			*ptr = '?';

	RETURN_STR(stuff);
}

/*
 * $remws(word word word / word word word)
 * Returns the right hand side unchanged, except that any word on the right
 * hand side that is also found on the left hand side will be removed.
 * Space is *not* retained.  So there.
 */
BUILT_IN_FUNCTION(function_remws, word)
{
	char    *left = NULL,
		**lhs = NULL,
		*right = NULL,
		**rhs = NULL, 
		*booya = NULL;
	int	leftc, 	
		lefti,
		rightc,
		righti;
	int	found = 0;

	left = word;
	if (!(right = strchr(word,'/')))
	RETURN_EMPTY;

	*right++ = 0;
	leftc = splitw(left, &lhs);
	rightc = splitw(right, &rhs);

	for (righti = 0; righti < rightc; righti++)
	{
		found = 0;
		for (lefti = 0; lefti < leftc; lefti++)
		{
			if (rhs[righti] && lhs[lefti]  &&
			    !my_stricmp(lhs[lefti], rhs[righti]))
			{
				found = 1;
				break;
			}
		}
		if (!found)
			m_s3cat(&booya, space, rhs[righti]);
		rhs[righti] = NULL;
	}

	new_free((char **)&lhs);
	new_free((char **)&rhs);

	if (!booya)
		RETURN_EMPTY;

	return (booya);				/* DONT USE RETURN_STR HERE! */
}

BUILT_IN_FUNCTION(function_printlen, input)
{
	RETURN_INT(output_with_count(input, 0, 0));
}

BUILT_IN_FUNCTION(function_stripansicodes, input)
{
#if 0
	return strip_ansi(input);
#else
	RETURN_STR(stripansicodes(input));
#endif
}

#if 0
BUILT_IN_FUNCTION(function_igtype, input)
{
	RETURN_STR(get_ignore_types_by_pattern(input));
}

BUILT_IN_FUNCTION(function_rigtype, input)
{
	 return get_ignore_patterns_by_type(input);	/* DONT MALLOC! */
}
#endif

BUILT_IN_FUNCTION(function_getuid, input)
{
	RETURN_INT(getuid());
}

BUILT_IN_FUNCTION(function_getgid, input)
{
	RETURN_INT(getgid());
}

BUILT_IN_FUNCTION(function_getlogin, input)
{
#ifdef HAVE_GETLOGIN
	RETURN_STR(getlogin());
#else
	RETURN_STR(getenv("LOGNAME"));
#endif
}

BUILT_IN_FUNCTION(function_getpgrp, input)
{
	RETURN_INT(getpgrp());
}

BUILT_IN_FUNCTION(function_iscurchan, input)
{
	char 	*chan;
	Window 	*w = NULL;

	GET_STR_ARG(chan, input);
	while (traverse_all_windows(&w))
	{
		/*
		 * Check to see if the channel specified is the current
		 * channel on *any* window for the current server.
		 */
		if (w->current_channel &&
			!my_stricmp(chan, w->current_channel) &&
			w->server == from_server)
				RETURN_INT(1);
	}

	RETURN_INT(0);
}

BUILT_IN_FUNCTION(function_getcset, input)
{
char *var = NULL;
char *channel = NULL;
ChannelList *chan = NULL;
	GET_STR_ARG(var, input);
	if (input && *input)
		channel = next_arg(input, &input);
	else
		channel = get_current_channel_by_refnum(0);
	chan = lookup_channel(channel, current_window->server, 0);
	if (!channel || !chan)
		RETURN_EMPTY;
	return get_cset(var, chan);
}

BUILT_IN_FUNCTION(function_getfsets, input)
{
extern char *get_fset(char *);
char *s = NULL;
char *ret = NULL;
	if (!input || !*input)
		return get_fset(NULL);
	while ((s = next_arg(input, &input)))
		malloc_strcat(&ret, get_fset(s));
	return ret;
}

/*
 * $isnumber(number base)
 * returns the empty value if nothing is passed to it
 * returns 0 if the value passed is not a number
 * returns 1 if the value passed is a number.
 *
 * The "base" number is optional and should be prefixed by the 
 * 	character 'b'.  ala,   $isnumber(0x0F b16) for hexadecimal.
 *	the special base zero (b0) means to 'auto-detect'.  Base must
 *	be between 0 and 36, inclusive.  If not, it defaults to 0.
 */
BUILT_IN_FUNCTION(function_isnumber, input)
{
	int	base = 0;
	char	*endptr;
	char	*barg;
	char	*number = NULL;

	/*
	 * See if the first arg is the base
	 */
	barg = new_next_arg(input, &input);

	/*
	 * If it is, the number is the 2nd arg
	 */
	if (barg && *barg == 'b' && is_number(barg + 1))
	{
		GET_STR_ARG(number, input);
	}
	/*
	 * Otherwise, the first arg is the number,
	 * the 2nd arg probably is the base
	 */
	else
	{
		number = barg;
		barg = new_next_arg(input, &input);
	}

	/*
	 * If the user specified a base, parse it.
	 * Must be between 0 and 36.
	 */
	if (barg && *barg == 'b')
	{
		base = my_atol(barg + 1);
		if (base < 0 || base > 36)
			base = 0;
	}

	/*
	 * Must have specified a number, though.
	 */
	RETURN_IF_EMPTY(number);

	strtol(number, &endptr, base);
	if (*endptr)
		RETURN_INT(0);
	else
		RETURN_INT(1);
}

/*
 * $rest(index string)
 * Returns 'string' starting at the 'index'th character
 * Just like $restw() does for words.
 */
BUILT_IN_FUNCTION(function_rest, input)
{
	int	start = 1;

	if (my_atol(input))
		GET_INT_ARG(start, input);

	if (start <= 0)
		RETURN_STR(input);

	if (start >= strlen(input))
		RETURN_EMPTY;

	RETURN_STR(input + start);
}
