1859 lines
42 KiB
C
1859 lines
42 KiB
C
/*
|
|
* hook.c: Does those naughty hook functions.
|
|
*
|
|
* Written By Michael Sandrof
|
|
* Rewritten by Jeremy Nelson and then rewritten to use
|
|
* hash'd lists by Colten Edwards
|
|
* Copyright(c) 1997
|
|
*/
|
|
|
|
#define __hook_c
|
|
#include "irc.h"
|
|
static char cvsrevision[] = "$Id$";
|
|
CVS_REVISION(hook_c)
|
|
#include "struct.h"
|
|
|
|
#include "hook.h"
|
|
#include "vars.h"
|
|
#include "ircaux.h"
|
|
#include "if.h"
|
|
#include "alias.h"
|
|
#include "list.h"
|
|
#include "window.h"
|
|
#include "server.h"
|
|
#include "output.h"
|
|
#include "commands.h"
|
|
#include "parse.h"
|
|
#include "misc.h"
|
|
#include "stack.h"
|
|
#include "tcl_bx.h"
|
|
#define MAIN_SOURCE
|
|
#include "modval.h"
|
|
|
|
#include <stdarg.h>
|
|
|
|
/*
|
|
* The various ON levels: SILENT means the DISPLAY will be OFF and it will
|
|
* suppress the default action of the event, QUIET means the display will be
|
|
* OFF but the default action will still take place, NORMAL means you will be
|
|
* notified when an action takes place and the default action still occurs,
|
|
* NOISY means you are notified when an action occur plus you see the action
|
|
* in the display and the default actions still occurs
|
|
*/
|
|
static char *noise_level[] = { "UNKNOWN", "SILENT", "QUIET", "NORMAL", "NOISY" };
|
|
|
|
#define HF_NORECURSE 0x0001
|
|
#define HF_DEBUG 0x0010
|
|
|
|
|
|
/*
|
|
* NumericList: a special list type to dynamically handle numeric hook
|
|
* requests
|
|
*/
|
|
typedef struct numericlist_stru
|
|
{
|
|
struct numericlist_stru *next;
|
|
int numeric;
|
|
char name[4];
|
|
Hook *list;
|
|
char *filename;
|
|
} NumericList;
|
|
|
|
NumericList *numeric_list = NULL;
|
|
|
|
#ifdef WANT_DLL
|
|
NumericList *dll_numeric_list = NULL;
|
|
#endif
|
|
|
|
/* hook_functions: the list of all hook functions available */
|
|
#define HOOK_COUNT (sizeof hook_functions / sizeof *hook_functions)
|
|
HookFunc hook_functions[] =
|
|
{
|
|
{ "ACTION", NULL, 3, 0, 0 },
|
|
{ "BANS", NULL, 4, 0, 0 },
|
|
{ "BANS_FOOTER", NULL, 2, 0, 0 },
|
|
{ "BANS_HEADER", NULL, 4, 0, 0 },
|
|
|
|
{ "CDCC_NOTE", NULL, 3, 0, 0 },
|
|
{ "CDCC_PACK", NULL, 7, 0, 0 },
|
|
{ "CDCC_POSTPACK", NULL, 8, 0, 0 },
|
|
{ "CDCC_PREPACK", NULL, 14, 0, 0 },
|
|
{ "CDCC_QUEUE", NULL, 5, 0, 0 },
|
|
{ "CDCC_QUEUEH", NULL, 3, 0, 0 },
|
|
{ "CDCC_SEND_NICK", NULL, 3, 0, 0 },
|
|
{ "CDCC_STATS", NULL, 1, 0, 0 },
|
|
|
|
{ "CHANOP", NULL, 1, 0, 0 },
|
|
{ "CHANNEL_NICK", NULL, 3, 0, 0 },
|
|
{ "CHANNEL_SIGNOFF", NULL, 3, 0, 0 },
|
|
{ "CHANNEL_STATS", NULL, 32, 0, 0 },
|
|
{ "CHANNEL_SWITCH", NULL, 1, 0, 0 },
|
|
{ "CHANNEL_SYNCH", NULL, 2, 0, 0 },
|
|
{ "CLONE", NULL, 2, 0, 0 },
|
|
{ "CONNECT", NULL, 1, 0, 0 },
|
|
{ "CTCP", NULL, 4, 0, 0 },
|
|
{ "CTCP_REPLY", NULL, 3, 0, 0 },
|
|
{ "DCC_CHAT", NULL, 2, 0, 0 },
|
|
{ "DCC_CONNECT", NULL, 2, 0, 0 },
|
|
{ "DCC_ERROR", NULL, 6, 0, 0 },
|
|
{ "DCC_HEADER", NULL, 7, 0, 0 },
|
|
{ "DCC_LOST", NULL, 2, 0, 0 },
|
|
{ "DCC_OFFER", NULL, 1, 0, 0 },
|
|
{ "DCC_POST", NULL, 7, 0, 0 },
|
|
{ "DCC_RAW", NULL, 3, 0, 0 },
|
|
{ "DCC_REQUEST", NULL, 4, 0, 0 },
|
|
{ "DCC_STAT", NULL, 7, 0, 0 },
|
|
{ "DCC_STATF", NULL, 7, 0, 0 },
|
|
{ "DCC_STATF1", NULL, 5, 0, 0 },
|
|
{ "DCC_TRANSFER_STAT", NULL, 13, 0, 0 },
|
|
{ "DCC_UPDATE", NULL, 1, 0, 0 },
|
|
{ "DEBUG", NULL, 1, 0, 0 },
|
|
{ "DESYNC_MESSAGE", NULL, 2, 0, 0 },
|
|
{ "DISCONNECT", NULL, 1, 0, 0 },
|
|
{ "EBANS", NULL, 4, 0, 0 },
|
|
{ "EBANS_FOOTER", NULL, 2, 0, 0 },
|
|
{ "EBANS_HEADER", NULL, 4, 0, 0 },
|
|
{ "ENCRYPTED_NOTICE", NULL, 3, 0, 0 },
|
|
{ "ENCRYPTED_PRIVMSG", NULL, 3, 0, 0 },
|
|
{ "EXEC", NULL, 2, 0, 0 },
|
|
{ "EXEC_ERRORS", NULL, 2, 0, 0 },
|
|
{ "EXEC_EXIT", NULL, 2, 0, 0 },
|
|
{ "EXEC_PROMPT", NULL, 2, 0, 0 },
|
|
{ "EXIT", NULL, 1, 0, 0 },
|
|
{ "FLOOD", NULL, 4, 0, 0 },
|
|
{ "FTP", NULL, 1, 0, 0 },
|
|
{ "HELP", NULL, 2, 0, 0 },
|
|
{ "HELPSUBJECT", NULL, 2, 0, 0 },
|
|
{ "HELPTOPIC", NULL, 1, 0, 0 },
|
|
{ "HOOK", NULL, 1, 0, 0 },
|
|
{ "IDLE", NULL, 1, 0, 0 },
|
|
{ "INPUT", NULL, 1, 0, 0 },
|
|
{ "INVITE", NULL, 2, 0, 0 },
|
|
{ "JOIN", NULL, 3, 0, 0 },
|
|
{ "JOIN_ME", NULL, 1, 0, 0 },
|
|
{ "KICK", NULL, 3, 0, 0 },
|
|
{ "LEAVE", NULL, 2, 0, 0 },
|
|
{ "LEAVE_ME", NULL, 1, 0, 0 },
|
|
{ "LIST", NULL, 3, 0, 0 },
|
|
{ "LLOOK_ADDED", NULL, 2, 0, 0 },
|
|
{ "LLOOK_JOIN", NULL, 2, 0, 0 },
|
|
{ "LLOOK_SPLIT", NULL, 2, 0, 0 },
|
|
{ "MAIL", NULL, 2, 0, 0 },
|
|
{ "MODE", NULL, 3, 0, 0 },
|
|
{ "MODE_STRIPPED", NULL, 3, 0, 0 },
|
|
{ "MODULE", NULL, 1, 0, 0 },
|
|
{ "MSG", NULL, 2, 0, 0 },
|
|
{ "MSG_GROUP", NULL, 3, 0, 0 },
|
|
{ "MSGLOG", NULL, 4, 0, 0 },
|
|
{ "NAMES", NULL, 2, 0, 0 },
|
|
{ "NETSPLIT", NULL, 1, 0, 0 },
|
|
{ "NICK_COMP", NULL, 1, 0, 0 },
|
|
{ "NICKNAME", NULL, 2, 0, 0 },
|
|
{ "NOTE", NULL, 3, 0, 0 },
|
|
{ "NOTICE", NULL, 2, 0, 0 },
|
|
{ "NOTICE_GROUP", NULL, 3, 0, 0 },
|
|
{ "NOTIFY", NULL, 2, 0, 0 },
|
|
{ "NOTIFY_HEADER", NULL, 2, 0, 0 },
|
|
{ "NOTIFY_SIGNOFF", NULL, 1, 0, 0 },
|
|
{ "NOTIFY_SIGNON", NULL, 1, 0, 0 },
|
|
{ "NSLOOKUP", NULL, 3, 0, 0 },
|
|
{ "ODD_SERVER_STUFF", NULL, 3, 0, 0 },
|
|
{ "PASTE", NULL, 2, 0, 0 },
|
|
{ "PUBLIC", NULL, 3, 0, 0 },
|
|
{ "PUBLIC_AR", NULL, 3, 0, 0 },
|
|
{ "PUBLIC_MSG", NULL, 3, 0, 0 },
|
|
{ "PUBLIC_NOTICE", NULL, 3, 0, 0 },
|
|
{ "PUBLIC_OTHER", NULL, 3, 0, 0 },
|
|
{ "PUBLIC_OTHER_AR", NULL, 3, 0, 0 },
|
|
|
|
{ "RAW_IRC", NULL, 1, 0, 0 },
|
|
{ "REDIRECT", NULL, 2, 0, 0 },
|
|
{ "REPLY_AR", NULL, 1, 0, 0 },
|
|
{ "SAVEFILE", NULL, 2, 0, 0 },
|
|
{ "SAVEFILEPOST", NULL, 2, 0, 0 },
|
|
{ "SAVEFILEPRE", NULL, 2, 0, 0 },
|
|
{ "SEND_ACTION", NULL, 2, 0, HF_NORECURSE },
|
|
{ "SEND_CTCP", NULL, 3, 0, HF_NORECURSE },
|
|
{ "SEND_DCC_CHAT", NULL, 2, 0, HF_NORECURSE },
|
|
{ "SEND_MSG", NULL, 2, 0, HF_NORECURSE },
|
|
{ "SEND_NOTICE", NULL, 2, 0, HF_NORECURSE },
|
|
{ "SEND_PUBLIC", NULL, 2, 0, HF_NORECURSE },
|
|
{ "SEND_TO_SERVER", NULL, 3, 0, 0 },
|
|
{ "SERVER_NOTICE_FAKES",NULL, 3, 0, 0 },
|
|
{ "SERVER_NOTICE_FAKES_MYCHANNEL",NULL,3,0, 0 },
|
|
{ "SERVER_NOTICE_FOREIGN_KILL", NULL, 4, 0, 0 },
|
|
{ "SERVER_NOTICE_KILL", NULL, 4, 0, 0 },
|
|
{ "SERVER_NOTICE", NULL, 1, 0, 0 },
|
|
{ "SERVER_NOTICE_LOCAL_KILL",NULL,4, 0, 0 },
|
|
{ "SERVER_NOTICE_SERVER_KILL",NULL, 4, 0, 0 },
|
|
{ "SET", NULL, 2, 0, 0 },
|
|
{ "SHITLIST", NULL, 6, 0, 0 },
|
|
{ "SHITLIST_FOOTER", NULL, 1, 0, 0 },
|
|
{ "SHITLIST_HEADER", NULL, 6, 0, 0 },
|
|
{ "SHOWIDLE_FOOTER", NULL, 1, 0, 0 },
|
|
{ "SHOWIDLE_HEADER", NULL, 2, 0, 0 },
|
|
{ "SHOWIDLE", NULL, 4, 0, 0 },
|
|
{ "SIGNOFF", NULL, 1, 0, 0 },
|
|
{ "SILENCE", NULL, 2, 0, 0 },
|
|
{ "SOCKET", NULL, 3, 0, 0 },
|
|
{ "SOCKET_NOTIFY", NULL, 3, 0, 0 },
|
|
{ "STAT", NULL, 5, 0, 0 },
|
|
{ "STAT_FOOTER", NULL, 1, 0, 0 },
|
|
{ "STAT_HEADER", NULL, 5, 0, 0 },
|
|
{ "STATUS_UPDATE", NULL, 2, 0, 0 },
|
|
{ "SWITCH_CHANNELS", NULL, 3, 0, 0 },
|
|
{ "TIMER", NULL, 1, 0, 0 },
|
|
{ "TIMER_HOUR", NULL, 1, 0, 0 },
|
|
{ "TOPIC", NULL, 2, 0, 0 },
|
|
{ "URLGRAB", NULL, 1, 0, 0 },
|
|
{ "USAGE", NULL, 2, 0, 0 },
|
|
{ "USERLIST", NULL, 4, 0, 0 },
|
|
{ "USERLIST_FOOTER", NULL, 1, 0, 0 },
|
|
{ "USERLIST_HEADER", NULL, 5, 0, 0 },
|
|
{ "USERS", NULL, 5, 0, 0 },
|
|
{ "USERS_FOOTER", NULL, 1, 0, 0 },
|
|
{ "USERS_HEADER", NULL, 5, 0, 0 },
|
|
{ "USERS_IP", NULL, 3, 0, 0 },
|
|
{ "USERS_SERVER", NULL, 2, 0, 0 },
|
|
{ "USERS_SERVER_HEADER",NULL, 2, 0, 0 },
|
|
{ "WALL", NULL, 2, 0, 0 },
|
|
{ "WALLOP", NULL, 3, 0, 0 },
|
|
{ "WATCH", NULL, 3, 0, 0 },
|
|
{ "WHO", NULL, 6, 0, 0 },
|
|
{ "WHOLEFT", NULL, 6, 0, 0 },
|
|
{ "WHOLEFT_FOOTER", NULL, 1, 0, 0 },
|
|
{ "WHOLEFT_HEADER", NULL, 6, 0, 0 },
|
|
{ "WIDELIST", NULL, 1, 0, 0 },
|
|
{ "WINDOW", NULL, 2, 0, HF_NORECURSE },
|
|
{ "WINDOW_CREATE", NULL, 1, 0, 0 },
|
|
{ "WINDOW_FOCUS", NULL, 4, 0, 0 },
|
|
{ "WINDOW_KILL", NULL, 1, 0, 0 },
|
|
{ "WINDOW_SWAP", NULL, 1, 0, 0 },
|
|
{ "YELL", NULL, 1, 0, 0 }
|
|
};
|
|
|
|
void hook_add_to_list (Hook **list, Hook *item);
|
|
static Hook * hook_remove_from_list (Hook **list, char *item, int sernum);
|
|
static void add_numeric_list (NumericList *item);
|
|
static NumericList *find_numeric_list (int numeric);
|
|
static NumericList *remove_numeric_list (int numeric);
|
|
|
|
char *current_package(void) { return empty_string; }
|
|
|
|
/*
|
|
* name of the current hook that's running. a hook within a hook, might
|
|
* screw this up though.
|
|
*/
|
|
char hook_name[BIG_BUFFER_SIZE+1] = {0};
|
|
extern int last_function_call_level;
|
|
|
|
|
|
/*
|
|
* This converts a user-specified string of unknown composition and
|
|
* returns a string that contains at minimum "params" number of words
|
|
* in it. For any words that are forcibly added on, the last word will
|
|
* be a * (so it can match any number of words), and any previous words
|
|
* will be a % (so it forces the correct number of words to be caught.)
|
|
*/
|
|
static char * fill_it_out (char *str, int params)
|
|
{
|
|
char buffer[BIG_BUFFER_SIZE];
|
|
char *arg,
|
|
*ptr;
|
|
int i = 0;
|
|
|
|
ptr = LOCAL_COPY(str);
|
|
*buffer = 0;
|
|
|
|
while ((arg = next_arg(ptr, &ptr)) != NULL)
|
|
{
|
|
if (*buffer)
|
|
strlcat(buffer, space, sizeof buffer);
|
|
strlcat(buffer, arg, sizeof buffer);
|
|
if (++i == params)
|
|
break;
|
|
}
|
|
|
|
for (; i < params; i++)
|
|
strlcat(buffer, (i < params-1) ? " %" : " *", sizeof buffer);
|
|
|
|
if (*ptr)
|
|
{
|
|
strlcat(buffer, space, sizeof buffer);
|
|
strlcat(buffer, ptr, sizeof buffer);
|
|
}
|
|
return m_strdup(buffer);
|
|
}
|
|
|
|
|
|
#define INVALID_HOOKNUM -1001
|
|
|
|
/*
|
|
* find_hook: returns the numerical value for a specified hook name
|
|
*/
|
|
static int find_hook (char *name, int *first)
|
|
{
|
|
int which = INVALID_HOOKNUM, i, len, cnt;
|
|
|
|
if (first)
|
|
*first = -1;
|
|
|
|
if (!name || !(len = strlen(name)))
|
|
{
|
|
say("You must specify an event type!");
|
|
return INVALID_HOOKNUM;
|
|
}
|
|
|
|
upper(name);
|
|
|
|
for (cnt = 0, i = 0; i < NUMBER_OF_LISTS; i++)
|
|
{
|
|
if (!strncmp(name, hook_functions[i].name, len))
|
|
{
|
|
if (first && *first == -1)
|
|
*first = i;
|
|
|
|
if (strlen(hook_functions[i].name) == len)
|
|
{
|
|
cnt = 1;
|
|
which = i;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
cnt++;
|
|
which = i;
|
|
}
|
|
}
|
|
else if (cnt)
|
|
break;
|
|
}
|
|
|
|
if (cnt == 0)
|
|
{
|
|
if (is_number(name))
|
|
{
|
|
which = atol(name);
|
|
|
|
if ((which < 0) || (which > 999))
|
|
{
|
|
say("Numerics must be between 001 and 999");
|
|
return INVALID_HOOKNUM;
|
|
}
|
|
which = -which;
|
|
}
|
|
else
|
|
{
|
|
say("No such ON function: %s", name);
|
|
return INVALID_HOOKNUM;
|
|
}
|
|
}
|
|
else if (cnt > 1)
|
|
{
|
|
say("Ambiguous ON function: %s", name);
|
|
return INVALID_HOOKNUM;
|
|
}
|
|
|
|
return which;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* * * * * ADDING A HOOK * * * * * */
|
|
/*
|
|
* This adds a numeric hook to the numeric hook list. The composition of that
|
|
* list is a linked list of "numeric holders" which simply indicate the
|
|
* numeric of that list, and a pointer to the actual list itself. The actual
|
|
* list itself is just a linked list of events sorted by serial number, then
|
|
* by "nick".
|
|
*/
|
|
static void add_numeric_hook (int numeric, char *nick, char *stuff, Noise noisy, int not, int sernum, int flexible)
|
|
{
|
|
NumericList *entry;
|
|
Hook *new;
|
|
|
|
if (!(entry = find_numeric_list(numeric)))
|
|
{
|
|
entry = (NumericList *) new_malloc(sizeof(NumericList));
|
|
entry->numeric = numeric;
|
|
sprintf(entry->name, "%3.3d", numeric);
|
|
entry->next = NULL;
|
|
entry->list = NULL;
|
|
add_numeric_list(entry);
|
|
}
|
|
|
|
if (!(new = hook_remove_from_list(&entry->list, nick, sernum)))
|
|
{
|
|
new = (Hook *)new_malloc(sizeof(Hook));
|
|
new->nick = NULL;
|
|
new->stuff = NULL;
|
|
}
|
|
|
|
malloc_strcpy(&new->nick, nick);
|
|
malloc_strcpy(&new->stuff, stuff);
|
|
new->noisy = noisy;
|
|
new->not = not;
|
|
new->sernum = sernum;
|
|
new->flexible = flexible;
|
|
new->global = loading_global;
|
|
malloc_strcpy(&new->filename, current_package());
|
|
new->next = NULL;
|
|
|
|
upper(new->nick);
|
|
hook_add_to_list(&entry->list, new);
|
|
}
|
|
|
|
#ifdef WANT_DLL
|
|
static void add_numeric_dll_hook (int numeric, Noise noise, int serial, char *nick, char *package, int (*func)(int, char *, char **))
|
|
{
|
|
NumericList *entry;
|
|
Hook *new;
|
|
|
|
if (!(entry = find_numeric_list(numeric)))
|
|
{
|
|
entry = (NumericList *) new_malloc(sizeof(NumericList));
|
|
entry->numeric = numeric;
|
|
sprintf(entry->name, "%3.3d", numeric);
|
|
entry->next = NULL;
|
|
entry->list = NULL;
|
|
add_numeric_list(entry);
|
|
}
|
|
new = (Hook *)new_malloc(sizeof(Hook));
|
|
malloc_strcpy(&new->nick, nick);
|
|
malloc_strcpy(&new->filename, package);
|
|
new->num_func = func;
|
|
new->noisy = noise;
|
|
new->sernum = serial;
|
|
upper(new->nick);
|
|
hook_add_to_list(&entry->list, new);
|
|
}
|
|
|
|
void add_dll_hook(int which, Noise noise, char *nick, char *package, int (*func1)(int, char *, char **), int (*func2)(char *, char *, char **))
|
|
{
|
|
Hook *new;
|
|
static int serial = 0;
|
|
serial++;
|
|
nick = fill_it_out(nick, which < 0 ? 1 : hook_functions[which].params);
|
|
if (which < 0)
|
|
{
|
|
add_numeric_dll_hook(which, noise, serial, nick, package, func1);
|
|
return;
|
|
}
|
|
new = (Hook *)new_malloc(sizeof(Hook));
|
|
malloc_strcpy(&new->nick, nick);
|
|
malloc_strcpy(&new->filename, package);
|
|
new->num_func = func1;
|
|
new->hook_func = func2;
|
|
new->noisy = noise;
|
|
new->sernum = serial;
|
|
upper(new->nick);
|
|
hook_add_to_list(&hook_functions[which].list, new);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* add_hook: Given an index into the hook_functions array, this adds a new
|
|
* entry to the list as specified by the rest of the parameters. The new
|
|
* entry is added in alphabetical order (by nick).
|
|
*/
|
|
static void add_hook (int which, char *nick, char *stuff, Noise noisy, int not, int sernum, int flexible)
|
|
{
|
|
Hook *new;
|
|
|
|
if (which < 0)
|
|
{
|
|
add_numeric_hook(-which, nick, stuff, noisy,
|
|
not, sernum, flexible);
|
|
return;
|
|
}
|
|
|
|
if (!(new = hook_remove_from_list(&hook_functions[which].list, nick, sernum)))
|
|
{
|
|
new = (Hook *)new_malloc(sizeof(Hook));
|
|
new->nick = NULL;
|
|
new->stuff = NULL;
|
|
}
|
|
|
|
malloc_strcpy(&new->nick, nick);
|
|
malloc_strcpy(&new->stuff, stuff);
|
|
new->noisy = noisy;
|
|
new->not = not;
|
|
new->sernum = sernum;
|
|
new->flexible = flexible;
|
|
new->global = loading_global;
|
|
malloc_strcpy(&new->filename, current_package());
|
|
new->next = NULL;
|
|
|
|
upper(new->nick);
|
|
new->debug = (hook_functions[which].flags & HF_DEBUG) ? HF_DEBUG : 0;
|
|
hook_add_to_list(&hook_functions[which].list, new);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* * * * * * REMOVING A HOOK * * * * * * * */
|
|
static void remove_numeric_hook (int numeric, char *nick, int sernum, int quiet)
|
|
{
|
|
NumericList *hook;
|
|
Hook *tmp,
|
|
*next;
|
|
|
|
if ((hook = find_numeric_list(numeric)))
|
|
{
|
|
if (nick)
|
|
{
|
|
if ((tmp = hook_remove_from_list(&hook->list, nick, sernum)))
|
|
{
|
|
if (!quiet)
|
|
{
|
|
say("%c%s%c removed from %d list",
|
|
(tmp->flexible?'\'':'"'), nick,
|
|
(tmp->flexible?'\'':'"'), numeric);
|
|
}
|
|
new_free(&(tmp->nick));
|
|
new_free(&tmp->filename);
|
|
new_free(&(tmp->stuff));
|
|
new_free((char **)&tmp);
|
|
|
|
if (!hook->list)
|
|
{
|
|
if ((hook = remove_numeric_list(numeric)))
|
|
new_free((char **)&hook);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
remove_numeric_list(numeric);
|
|
for (tmp = hook->list; tmp; tmp = next)
|
|
{
|
|
next = tmp->next;
|
|
tmp->not = 1;
|
|
new_free(&(tmp->nick));
|
|
new_free(&(tmp->stuff));
|
|
new_free(&tmp->filename);
|
|
new_free((char **)&tmp);
|
|
}
|
|
hook->list = NULL;
|
|
new_free((char **)&hook);
|
|
if (!quiet)
|
|
say("The %d list is empty", numeric);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (quiet)
|
|
return;
|
|
|
|
if (nick)
|
|
say("\"%s\" is not on the %d list", nick, numeric);
|
|
else
|
|
say("The %d list is empty", numeric);
|
|
}
|
|
|
|
|
|
static void remove_hook (int which, char *nick, int sernum, int quiet)
|
|
{
|
|
Hook *tmp,
|
|
*next;
|
|
|
|
if (which < 0)
|
|
{
|
|
remove_numeric_hook(-which, nick, sernum, quiet);
|
|
return;
|
|
}
|
|
if (nick)
|
|
{
|
|
if ((tmp = hook_remove_from_list(&hook_functions[which].list, nick, sernum)))
|
|
{
|
|
if (!quiet)
|
|
say("%c%s%c removed from %s list",
|
|
(tmp->flexible?'\'':'"'), nick,
|
|
(tmp->flexible?'\'':'"'),
|
|
hook_functions[which].name);
|
|
|
|
new_free(&(tmp->nick));
|
|
new_free(&(tmp->stuff));
|
|
new_free(&tmp->filename);
|
|
new_free((char **)&tmp); /* XXX why? */
|
|
}
|
|
else if (!quiet)
|
|
say("\"%s\" is not on the %s list", nick,
|
|
hook_functions[which].name);
|
|
}
|
|
else
|
|
{
|
|
Hook *prev = NULL;
|
|
Hook *top = NULL;
|
|
|
|
for (tmp = hook_functions[which].list; tmp; prev=tmp, tmp=next)
|
|
{
|
|
next = tmp->next;
|
|
|
|
/*
|
|
* If given a non-zero sernum, then we clean out
|
|
* only those hooks that are at that level.
|
|
*/
|
|
if (sernum && tmp->sernum != sernum)
|
|
{
|
|
if (!top)
|
|
top = tmp;
|
|
continue;
|
|
}
|
|
|
|
if (prev)
|
|
prev->next = tmp->next;
|
|
tmp->not = 1;
|
|
new_free(&(tmp->nick));
|
|
new_free(&(tmp->stuff));
|
|
new_free(&tmp->filename);
|
|
new_free((char **)&tmp);
|
|
}
|
|
hook_functions[which].list = top;
|
|
if (!quiet)
|
|
{
|
|
if (sernum)
|
|
say("The %s <%d> list is empty", hook_functions[which].name, sernum);
|
|
else
|
|
say("The %s list is empty", hook_functions[which].name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Used to bulk-erase all of the currently scheduled ONs */
|
|
void flush_on_hooks (void)
|
|
{
|
|
int x;
|
|
int old_display = window_display;
|
|
|
|
window_display = 0;
|
|
for (x = 1; x < 999; x++)
|
|
remove_numeric_hook(x, NULL, x, 1);
|
|
for (x = 0; x < NUMBER_OF_LISTS; x++)
|
|
remove_hook(x, NULL, 0, 1);
|
|
window_display = old_display;
|
|
}
|
|
|
|
void unload_on_hooks (char *filename)
|
|
{
|
|
int x;
|
|
NumericList *tmp;
|
|
Hook *list, *next;
|
|
|
|
int old_display = window_display;
|
|
window_display = 0;
|
|
|
|
for (x = 1; x < 999; x++)
|
|
{
|
|
if ((tmp = find_numeric_list(x)))
|
|
{
|
|
for (list = tmp->list; list; list = next)
|
|
{
|
|
next = list->next;
|
|
if (!strcmp(list->filename, filename))
|
|
remove_numeric_hook(x, list->nick, list->sernum, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (x = 0; x < NUMBER_OF_LISTS; x++)
|
|
{
|
|
for (list = hook_functions[x].list; list; list = next)
|
|
{
|
|
next = list->next;
|
|
if (!strcmp(list->filename, filename))
|
|
remove_hook(x, list->nick, list->sernum, 1);
|
|
}
|
|
}
|
|
|
|
window_display = old_display;
|
|
}
|
|
|
|
void debug_hook(char *name, int x)
|
|
{
|
|
int cnt = 0, i;
|
|
|
|
for (cnt = 0, i = 0; i < NUMBER_OF_LISTS; i++)
|
|
{
|
|
if (!strcmp(name, hook_functions[i].name))
|
|
{
|
|
Hook *tmp;
|
|
if (x)
|
|
hook_functions[i].flags |= HF_DEBUG;
|
|
else
|
|
hook_functions[i].flags &= ~(HF_DEBUG);
|
|
for (tmp = hook_functions[i].list; tmp; tmp = tmp->next)
|
|
{
|
|
if (x)
|
|
tmp->debug |= HF_DEBUG;
|
|
else
|
|
tmp->debug &= ~(HF_DEBUG);
|
|
cnt++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cnt == 0)
|
|
{
|
|
int which;
|
|
if (is_number(name))
|
|
{
|
|
NumericList *tmp;
|
|
which = atol(name);
|
|
|
|
if ((which < 0) || (which > 999))
|
|
return;
|
|
|
|
if ((tmp = find_numeric_list(which)))
|
|
{
|
|
Hook *list;
|
|
for (list = tmp->list; list; list = list->next, cnt++)
|
|
{
|
|
if (x)
|
|
list->debug |= HF_DEBUG;
|
|
else
|
|
list->debug &= ~(HF_DEBUG);
|
|
cnt++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* * * * * * SHOWING A HOOK * * * * * * */
|
|
/* show_hook shows a single hook */
|
|
static void show_hook (Hook *list, char *name)
|
|
{
|
|
char *hooks = fget_string_var(FORMAT_HOOK_FSET);
|
|
if (hooks)
|
|
put_it("%s",
|
|
convert_output_format(hooks,
|
|
"%s %s %c %s %c %s %s %d",
|
|
list->filename[0] ? list->filename : "*",
|
|
name,
|
|
(list->flexible?'\'':'"'), list->nick,
|
|
(list->flexible?'\'':'"'),
|
|
(list->not ? "nothing" : list->stuff ? list->stuff:"DLL"),
|
|
noise_level[list->noisy],
|
|
list->sernum));
|
|
else
|
|
say("[%s] On %s from %c%s%c do %s [%s] <%d>",
|
|
list->filename[0] ? list->filename : "*",
|
|
name,
|
|
(list->flexible?'\'':'"'), list->nick,
|
|
(list->flexible?'\'':'"'),
|
|
(list->not ? "nothing" : list->stuff ? list->stuff:"DLL"),
|
|
noise_level[list->noisy],
|
|
list->sernum);
|
|
}
|
|
|
|
/*
|
|
* show_numeric_list: If numeric is 0, then all numeric lists are displayed.
|
|
* If numeric is non-zero, then that particular list is displayed. The total
|
|
* number of entries displayed is returned
|
|
*/
|
|
static int show_numeric_list (int numeric)
|
|
{
|
|
NumericList *tmp;
|
|
Hook *list;
|
|
char buf[4];
|
|
int cnt = 0;
|
|
|
|
if (numeric)
|
|
{
|
|
sprintf(buf, "%3.3d", numeric);
|
|
if ((tmp = find_numeric_list(numeric)))
|
|
{
|
|
for (list = tmp->list; list; list = list->next, cnt++)
|
|
show_hook(list, tmp->name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (tmp = numeric_list; tmp; tmp = tmp->next)
|
|
{
|
|
for (list = tmp->list; list; list = list->next, cnt++)
|
|
show_hook(list, tmp->name);
|
|
}
|
|
}
|
|
return (cnt);
|
|
}
|
|
|
|
/*
|
|
* show_list: Displays the contents of the list specified by the index into
|
|
* the hook_functions array. This function returns the number of entries in
|
|
* the list displayed
|
|
*/
|
|
static int show_list (int which)
|
|
{
|
|
Hook *list;
|
|
int cnt = 0;
|
|
|
|
/* Less garbage when issuing /on without args. (lynx) */
|
|
for (list = hook_functions[which].list; list; list = list->next, cnt++)
|
|
show_hook(list, hook_functions[which].name);
|
|
return (cnt);
|
|
}
|
|
|
|
|
|
|
|
/* * * * * * * * EXECUTING A HOOK * * * * * * */
|
|
#define NO_ACTION_TAKEN -1
|
|
#define SUPPRESS_DEFAULT 0
|
|
#define DONT_SUPPRESS_DEFAULT 1
|
|
#define RESULT_PENDING 2
|
|
#define RESULT_NEXT 3
|
|
|
|
/*
|
|
* do_hook: This is what gets called whenever a MSG, INVITES, WALL, (you get
|
|
* the idea) occurs. The nick is looked up in the appropriate list. If a
|
|
* match is found, the stuff field from that entry in the list is treated as
|
|
* if it were a command. First it gets expanded as though it were an alias
|
|
* (with the args parameter used as the arguments to the alias). After it
|
|
* gets expanded, it gets parsed as a command. This will return as its value
|
|
* the value of the noisy field of the found entry, or -1 if not found.
|
|
*/
|
|
|
|
int BX_do_hook (int which, char *format, ...)
|
|
{
|
|
Hook *tmp = NULL;
|
|
Hook *next = NULL;
|
|
Hook **list;
|
|
char buffer[BIG_BUFFER_SIZE * 10 + 1];
|
|
char *name = NULL;
|
|
int retval = DONT_SUPPRESS_DEFAULT;
|
|
unsigned display = window_display;
|
|
int i;
|
|
Hook *hook_array[2048] = { 0 };
|
|
int hook_num = 0;
|
|
char *result = NULL;
|
|
int old_debug_count = debug_count;
|
|
|
|
/*
|
|
* Figure out where the hooks are for the event type were asserting
|
|
*/
|
|
/* Numeric list */
|
|
if (which < 0)
|
|
{
|
|
NumericList *hook;
|
|
|
|
if ((hook = find_numeric_list(-which)))
|
|
{
|
|
name = hook->name;
|
|
list = &hook->list;
|
|
}
|
|
else
|
|
list = NULL;
|
|
}
|
|
|
|
/* Named list */
|
|
else if (which < HOOK_COUNT)
|
|
{
|
|
/*
|
|
* If we're already executing the type, and we're
|
|
* specifically not supposed to allow recursion, then
|
|
* don't allow recursion. ;-)
|
|
*/
|
|
if (hook_functions[which].mark &&
|
|
(hook_functions[which].flags & HF_NORECURSE))
|
|
list = NULL;
|
|
else
|
|
{
|
|
list = &(hook_functions[which].list);
|
|
name = hook_functions[which].name;
|
|
strncpy(hook_name, hook_functions[which].name, BIG_BUFFER_SIZE);
|
|
}
|
|
}
|
|
else /* invalid hook */
|
|
{
|
|
list = NULL;
|
|
}
|
|
|
|
/*
|
|
* Press the buffer using the specified format string and args
|
|
* We do this here so that we don't waste time doing the vsnprintf
|
|
* if we're not going to do any matching. So for types where the
|
|
* user has no hooks, its a cheapie call.
|
|
*/
|
|
if (format)
|
|
{
|
|
va_list args;
|
|
va_start (args, format);
|
|
vsnprintf(buffer, BIG_BUFFER_SIZE * 5, format, args);
|
|
va_end(args);
|
|
}
|
|
else
|
|
ircpanic("do_hook: format is NULL");
|
|
|
|
#ifdef WANT_TCL
|
|
if (tcl_interp)
|
|
check_on_hook(which, format?buffer:NULL);
|
|
#endif
|
|
|
|
/*
|
|
* No hooks to look at? No problem. Drop out.
|
|
*/
|
|
if (!list)
|
|
{
|
|
*hook_name = 0;
|
|
return NO_ACTION_TAKEN;
|
|
}
|
|
|
|
/*
|
|
* Mark the event as being executed. This is used to suppress
|
|
* unwanted recursion in some /on's.
|
|
*/
|
|
if (which >= 0)
|
|
hook_functions[which].mark++;
|
|
|
|
|
|
/* not attached, so don't "fix" it */
|
|
{
|
|
int currser = 0,
|
|
oldser = INT_MIN,
|
|
currmatch = 0,
|
|
oldmatch = 0;
|
|
Hook * bestmatch = NULL;
|
|
|
|
/*
|
|
* Walk the list of hooks for this event
|
|
*/
|
|
for (tmp = *list; tmp; tmp = tmp->next)
|
|
{
|
|
char * tmpnick = NULL;
|
|
int sa;
|
|
|
|
/*
|
|
* save the current serial number
|
|
*/
|
|
currser = tmp->sernum;
|
|
|
|
/*
|
|
* Is this a different serial number than the
|
|
* last hook? If it is, then we save the previous
|
|
* serial number's best hook to the hook_array.
|
|
*/
|
|
if (currser != oldser)
|
|
{
|
|
oldser = currser;
|
|
oldmatch = 0;
|
|
if (bestmatch)
|
|
hook_array[hook_num++] = bestmatch;
|
|
bestmatch = NULL;
|
|
}
|
|
|
|
/*
|
|
* If this is a flexible hook, expand the nick stuff
|
|
*/
|
|
if (tmp->flexible)
|
|
tmpnick = expand_alias(tmp->nick, empty_string,
|
|
&sa, NULL);
|
|
else
|
|
tmpnick = tmp->nick;
|
|
|
|
|
|
/*
|
|
* Check to see if the pattern matches the text
|
|
*/
|
|
currmatch = wild_match(tmpnick, buffer);
|
|
|
|
/*
|
|
* If it is the "best match" so far, then we mark
|
|
* its "value" and save a pointer to it.
|
|
*/
|
|
if (currmatch > oldmatch)
|
|
{
|
|
oldmatch = currmatch;
|
|
bestmatch = tmp;
|
|
}
|
|
|
|
/*
|
|
* Clean up after flexible hooks
|
|
*/
|
|
if (tmp->flexible)
|
|
new_free(&tmpnick);
|
|
}
|
|
|
|
/*
|
|
* OK. We've walked the list. If the last hook had a best
|
|
* match, use that one too. =)
|
|
*/
|
|
if (bestmatch)
|
|
hook_array[hook_num++] = bestmatch;
|
|
}
|
|
|
|
|
|
/*
|
|
* Now we walk the list of collected hook events that are to be run
|
|
*/
|
|
for (i = 0; i < hook_num; i++)
|
|
{
|
|
const char * saved_who_from;
|
|
unsigned long saved_who_level;
|
|
char * name_copy;
|
|
char * stuff_copy;
|
|
int old_alias_debug = alias_debug;
|
|
char *result1 = NULL;
|
|
|
|
/*
|
|
* This should never happen.
|
|
*/
|
|
if (!(tmp = hook_array[i]))
|
|
ircpanic("hook_array[%d] is null", i);
|
|
|
|
/*
|
|
* Check to see if this hook is supposed to suppress the
|
|
* default action for the event.
|
|
*/
|
|
|
|
hook_next:
|
|
if (tmp->noisy == SILENT && tmp->sernum == 0)
|
|
retval = SUPPRESS_DEFAULT;
|
|
else if (tmp->noisy == UNKNOWN && tmp->sernum == 0)
|
|
retval = RESULT_PENDING;
|
|
/*
|
|
* If this is a negated event, or there isn't anything to be
|
|
* executed, then we don't bother. Just go on to the next one
|
|
*/
|
|
if (tmp->not || !tmp->stuff || !*tmp->stuff)
|
|
{
|
|
if (!tmp->stuff)
|
|
{
|
|
if (tmp->hook_func)
|
|
(tmp->hook_func)(name, buffer, NULL);
|
|
else if (tmp->num_func)
|
|
(tmp->num_func)(which, buffer, NULL);
|
|
}
|
|
continue;
|
|
}
|
|
/*
|
|
* If this is a NORMAL or NOISY hook, then we tell the user
|
|
* that we're going to execute the hook.
|
|
*/
|
|
if (tmp->noisy > QUIET)
|
|
say("%s activated by %c%s%c",
|
|
name, tmp->flexible ? '\'' : '"',
|
|
buffer, tmp->flexible ? '\'' : '"');
|
|
|
|
if ((tmp->debug & HF_DEBUG) && (internal_debug & DEBUG_HOOK) && !in_debug_yell)
|
|
{
|
|
debugyell("ON %s activated [%s]", name, buffer);
|
|
alias_debug++;
|
|
}
|
|
/*
|
|
* Save some information that may be reset in the
|
|
* execution, turn off the display if the user specified.
|
|
*/
|
|
save_display_target(&saved_who_from, &saved_who_level);
|
|
if (tmp->noisy < NOISY)
|
|
window_display = 0;
|
|
|
|
name_copy = LOCAL_COPY(name);
|
|
stuff_copy = LOCAL_COPY(tmp->stuff);
|
|
|
|
if (tmp->noisy == UNKNOWN)
|
|
result = parse_line_with_return(name_copy, stuff_copy, buffer, 0, 0);
|
|
else
|
|
{
|
|
/*
|
|
* OK. Go and run the code. It is imperative to note
|
|
* that "tmp" may be deleted by the code executed here,
|
|
* so it is absolutely forbidden to reference "tmp" after
|
|
* this point.
|
|
*/
|
|
next = tmp->next;
|
|
will_catch_return_exceptions++;
|
|
result1 = parse_line_with_return(name_copy, stuff_copy, buffer, 0, 0);
|
|
will_catch_return_exceptions--;
|
|
return_exception = 0;
|
|
}
|
|
if (retval == RESULT_PENDING)
|
|
{
|
|
if (result && atol(result))
|
|
retval = SUPPRESS_DEFAULT;
|
|
else
|
|
retval = DONT_SUPPRESS_DEFAULT;
|
|
}
|
|
new_free(&result);
|
|
/*
|
|
* Clean up the stuff that may have been mangled by the
|
|
* execution.
|
|
*/
|
|
restore_display_target(saved_who_from, saved_who_level);
|
|
if (result1 && *result1 && !my_stricmp(result1, "next"))
|
|
{
|
|
new_free(&result1);
|
|
{
|
|
if ((tmp = next))
|
|
goto hook_next;
|
|
else
|
|
retval = RESULT_NEXT;
|
|
}
|
|
}
|
|
new_free(&result1);
|
|
alias_debug = old_alias_debug;
|
|
window_display = display;
|
|
}
|
|
|
|
/*
|
|
* Mark the event as not currently being done here.
|
|
*/
|
|
if (which >= 0)
|
|
hook_functions[which].mark--;
|
|
|
|
if (oper_command && *buffer)
|
|
memset(buffer, 0, strlen(buffer)-1);
|
|
/*
|
|
* And return the user-specified suppression level
|
|
*/
|
|
if (old_debug_count == 1)
|
|
debug_count = 1;
|
|
*hook_name = 0;
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* shook: the SHOOK command -- this probably doesn't belong here,
|
|
* and shook is probably a stupid name. It simply asserts a fake
|
|
* hook event for a given type. Fraught with peril!
|
|
*/
|
|
BUILT_IN_COMMAND(shookcmd)
|
|
{
|
|
int which;
|
|
char *arg = next_arg(args, &args);
|
|
|
|
if ((which = find_hook(arg, NULL)) == INVALID_HOOKNUM)
|
|
return;
|
|
else
|
|
do_hook(which, "%s", args);
|
|
}
|
|
|
|
|
|
/* * * * * * SCHEDULING AN EVENT * * * * * * * */
|
|
/*
|
|
* The ON command:
|
|
* Format: /ON [#][+-^]TYPE ['] [SERNUM] NICK ['] [{] STUFF [}]
|
|
*
|
|
* The "ON" command mainly takes three arguments. The first argument
|
|
* is the "type" of callback that you want to schedule. This is either
|
|
* a three digit number, of it is one of the strings so enumerated at the
|
|
* top of this file in hook_list. The second argument is the "nick" or
|
|
* "pattern" that is to be used to match against future events. If the
|
|
* "nick" matches the text that is later passed to do_hook() with the given
|
|
* "type", then the commands in "stuff" will be executed.
|
|
*
|
|
* If "nick" is enclosed in single quotes ('), then it is a "flexible"
|
|
* pattern, and will be expanded before it is matched against the text
|
|
* in do_hook. Otherwise, the string so specified is "static", and is
|
|
* used as-is in do_hook().
|
|
*
|
|
* Within each type, there are at least 65,535 different "serial numbers",
|
|
* (there actually are MAX_INT of them, but by convention, only 16 bit
|
|
* serial numbers are used, from -32,768 to 32,767) which may be used to
|
|
* schedule any number of events at the given serial number.
|
|
*
|
|
* Each time an assertion occurs for a given "type", at most one of the
|
|
* scheduled events is executed for each of the distinct serial numbers that
|
|
* are in use for that event. The event to be executed is the one at a
|
|
* given serial number that "best" matches the text passed to do_hook().
|
|
* While in theory, up to MAX_INT events could be executed for a given single
|
|
* assertion, in practice, a hard limit of 2048 events per assertion is
|
|
* enforced.
|
|
*
|
|
* The runtime behavior of the event being scheduled can be modified by
|
|
* specifying a character at the beginning of the "type" argument. If you
|
|
* want to schedule an event at a serial number, then the first character
|
|
* must be a hash (#). The argument immediately FOLLOWING the "type"
|
|
* argument, and immediately PRECEDING the "nick" argument must be an
|
|
* integer number, and is used for the serial number for this event.
|
|
*
|
|
* The "verbosity" of the event may also be modified by specifying at most
|
|
* one of the following characters:
|
|
* A caret (^) is the SILENT level, and indicates that the event is to
|
|
* be executed with no output (window_display is turned off),
|
|
* and the "default action" (whatever that is) for the event is
|
|
* to be suppressed. The default action is actually only
|
|
* suppressed if the SILENT level is specified for serial number
|
|
* zero. This is the most common level used for overriding the
|
|
* output of most /on's.
|
|
* A minus (-) is the QUIET level, and is the same as the SILENT level,
|
|
* except that the default action (whatever that is) is not to
|
|
* be suppressed.
|
|
* No character is the "normal" case, and is the same as the "minus"
|
|
* level, with the addition that the client will inform you that
|
|
* the event was executed. This is useful for debugging.
|
|
* A plus (+) is the same as the "normal" (no character specified),
|
|
* except that the output is not suppressed (window_display is
|
|
* not changed.)
|
|
*/
|
|
BUILT_IN_COMMAND(oncmd)
|
|
{
|
|
char *func,
|
|
*nick,
|
|
*serial;
|
|
Noise noisy = NORMAL;
|
|
int not = 0,
|
|
sernum = 0,
|
|
remove = 0,
|
|
which = INVALID_HOOKNUM;
|
|
int flex = 0;
|
|
char type;
|
|
int first;
|
|
|
|
/*
|
|
* Get the type of event to be scheduled
|
|
*/
|
|
if ((func = next_arg(args, &args)) != NULL)
|
|
{
|
|
/*
|
|
* Check to see if this has a serial number.
|
|
*/
|
|
if (*func == '#')
|
|
{
|
|
if (!(serial = next_arg(args, &args)))
|
|
{
|
|
say("No serial number specified");
|
|
return;
|
|
}
|
|
sernum = atol(serial);
|
|
func++;
|
|
}
|
|
|
|
/*
|
|
* Get the verbosity level, if any.
|
|
*/
|
|
switch (*func)
|
|
{
|
|
case '?':
|
|
noisy = UNKNOWN;
|
|
func++;
|
|
break;
|
|
case '-':
|
|
noisy = QUIET;
|
|
func++;
|
|
break;
|
|
case '^':
|
|
noisy = SILENT;
|
|
func++;
|
|
break;
|
|
case '+':
|
|
noisy = NOISY;
|
|
func++;
|
|
break;
|
|
default:
|
|
noisy = NORMAL;
|
|
break;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check to see if the event type is valid
|
|
*/
|
|
if ((which = find_hook(func, &first)) == INVALID_HOOKNUM)
|
|
{
|
|
/*
|
|
* OK. So either the user specified an invalid type
|
|
* or they specified an ambiguous type. Either way,
|
|
* we're not going to be going anywhere. So we have
|
|
* free reign to mangle 'args' at this point.
|
|
*/
|
|
|
|
int len;
|
|
|
|
/*
|
|
* If first is -1, then it was an unknown type.
|
|
* An error has already been output, just return here
|
|
*/
|
|
if (first == -1)
|
|
return;
|
|
|
|
/*
|
|
* Otherwise, its an ambiguous type. If they were
|
|
* trying to register the hook, then theyve already
|
|
* gotten the error message, just return;
|
|
*/
|
|
if (new_new_next_arg(args, &args, &type))
|
|
return;
|
|
|
|
/*
|
|
* OK. So they probably want a listing.
|
|
*/
|
|
say("ON listings:");
|
|
len = strlen(func);
|
|
while (!my_strnicmp(func, hook_functions[first].name, len))
|
|
{
|
|
show_list(first);
|
|
first++;
|
|
if (!hook_functions[first].name)
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Check to see if this is a removal event or if this
|
|
* is a negated event.
|
|
*/
|
|
switch (*args)
|
|
{
|
|
case '-':
|
|
remove = 1;
|
|
args++;
|
|
break;
|
|
case '^':
|
|
not = 1;
|
|
args++;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Grab the "nick"
|
|
*/
|
|
if ((nick = new_new_next_arg(args, &args, &type)))
|
|
{
|
|
char *exp;
|
|
|
|
/*
|
|
* Pad it to the appropriate number of args
|
|
*/
|
|
if (which < 0)
|
|
nick = fill_it_out(nick, 1);
|
|
else
|
|
nick = fill_it_out(nick, hook_functions[which].params);
|
|
|
|
/*
|
|
* If nick is empty, something is very wrong.
|
|
*/
|
|
if (!*nick)
|
|
{
|
|
say("No expression specified");
|
|
new_free(&nick);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If we're doing a removal, do the deed.
|
|
*/
|
|
if (remove)
|
|
{
|
|
remove_hook(which, nick, sernum, 0);
|
|
new_free(&nick);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Take a note if its flexible or not.
|
|
*/
|
|
if (type == '\'')
|
|
flex = 1;
|
|
else
|
|
flex = 0;
|
|
|
|
|
|
/*
|
|
* If this is a negative event, then we don't want
|
|
* to take any action for it.
|
|
*/
|
|
if (not)
|
|
args = empty_string;
|
|
|
|
|
|
/*
|
|
* Slurp up any whitespace after the nick
|
|
*/
|
|
while (my_isspace(*args))
|
|
args++;
|
|
|
|
/*
|
|
* Then slurp up the body ("text")
|
|
*/
|
|
if (*args == '{') /* } */
|
|
{
|
|
if (!(exp = next_expr(&args, '{'))) /* } */
|
|
{
|
|
say("Unmatched brace in ON");
|
|
new_free(&nick);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
exp = args;
|
|
|
|
/*
|
|
* Schedule the event
|
|
*/
|
|
add_hook(which, nick, exp, noisy, not, sernum, flex);
|
|
|
|
/*
|
|
* Tell the user that we're done.
|
|
*/
|
|
if (which < 0)
|
|
say("On %3.3u from %c%s%c do %s [%s] <%d>",
|
|
-which, type, nick, type,
|
|
(not ? "nothing" : exp),
|
|
noise_level[noisy], sernum);
|
|
else
|
|
say("On %s from %c%s%c do %s [%s] <%d>",
|
|
hook_functions[which].name,
|
|
type, nick, type,
|
|
(not ? "nothing" : exp),
|
|
noise_level[noisy], sernum);
|
|
|
|
/*
|
|
* Clean up after the nick
|
|
*/
|
|
new_free(&nick);
|
|
}
|
|
|
|
/*
|
|
* No "nick" argument was specified. That means the user
|
|
* either is deleting all of the events of a type, or it
|
|
* wants to list all the events of a type.
|
|
*/
|
|
else
|
|
{
|
|
/*
|
|
* if its a removal, do the deed
|
|
*/
|
|
if (remove)
|
|
{
|
|
remove_hook(which, NULL, sernum, 0);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The help files say that an "/on 0" shows all
|
|
* of the numeric ONs. Since the ACTION hook is
|
|
* number 0, we have to check to see if the first
|
|
* character of "func" is a zero or not. If it is,
|
|
* we output all of the numeric functions.
|
|
*/
|
|
if (*func == '0')
|
|
{
|
|
if (!show_numeric_list(0))
|
|
say("All numeric ON lists are empty.");
|
|
}
|
|
else if (which < 0)
|
|
{
|
|
if (!show_numeric_list(-which))
|
|
say("The %3.3u list is empty.", -which);
|
|
}
|
|
else if (!show_list(which))
|
|
say("The %s list is empty.",
|
|
hook_functions[which].name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* No "Type" argument was specified. That means the user wants to
|
|
* list all of the ONs currently scheduled.
|
|
*/
|
|
else
|
|
{
|
|
int total = 0;
|
|
|
|
say("ON listings:");
|
|
|
|
/*
|
|
* Show the named events
|
|
*/
|
|
for (which = 0; which < NUMBER_OF_LISTS; which++)
|
|
total += show_list(which);
|
|
|
|
/*
|
|
* Show the numeric events
|
|
*/
|
|
total += show_numeric_list(0);
|
|
|
|
if (!total)
|
|
say("All ON lists are empty.");
|
|
}
|
|
}
|
|
|
|
|
|
/* * * * * * * * * * SAVING A HOOK * * * * * * * * * * */
|
|
static void write_hook (FILE *fp, Hook *hook, char *name)
|
|
{
|
|
char *stuff = NULL;
|
|
char flexi = '"';
|
|
|
|
if (hook->flexible)
|
|
flexi = '\'';
|
|
|
|
switch (hook->noisy)
|
|
{
|
|
case SILENT:
|
|
stuff = "^";
|
|
break;
|
|
case QUIET:
|
|
stuff = "-";
|
|
break;
|
|
case NORMAL:
|
|
stuff = empty_string;
|
|
break;
|
|
case NOISY:
|
|
stuff = "+";
|
|
break;
|
|
case UNKNOWN:
|
|
stuff = "?";
|
|
break;
|
|
}
|
|
|
|
if (hook->sernum)
|
|
fprintf(fp, "ON #%s%s %d", stuff, name, hook->sernum);
|
|
else
|
|
fprintf(fp, "ON %s%s", stuff, name);
|
|
|
|
fprintf(fp, " %c%s%c {%s}\n", flexi, hook->nick, flexi, hook->stuff);
|
|
}
|
|
|
|
/*
|
|
* save_hooks: for use by the SAVE command to write the hooks to a file so it
|
|
* can be interpreted by the LOAD command
|
|
*/
|
|
void save_hooks (FILE *fp, int do_all)
|
|
{
|
|
Hook *list;
|
|
NumericList *numeric;
|
|
int which;
|
|
|
|
for (which = 0; which < NUMBER_OF_LISTS; which++)
|
|
{
|
|
for (list = hook_functions[which].list; list; list = list->next)
|
|
if (!list->global || do_all)
|
|
write_hook(fp,list, hook_functions[which].name);
|
|
}
|
|
for (numeric = numeric_list; numeric; numeric = numeric->next)
|
|
{
|
|
for (list = numeric->list; list; list = list->next)
|
|
if (!list->global)
|
|
write_hook(fp, list, numeric->name);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* * * * * * * * * * STACKING A HOOK * * * * * * * * */
|
|
typedef struct onstacklist
|
|
{
|
|
int which;
|
|
Hook *list;
|
|
struct onstacklist *next;
|
|
} OnStack;
|
|
|
|
static OnStack * on_stack = NULL;
|
|
|
|
void do_stack_on (int type, char *args)
|
|
{
|
|
int which;
|
|
Hook *list;
|
|
NumericList *nhook = NULL, *nptr;
|
|
|
|
if (!on_stack && (type == STACK_POP || type == STACK_LIST))
|
|
{
|
|
say("ON stack is empty!");
|
|
return;
|
|
}
|
|
if (!args || !*args)
|
|
{
|
|
say("Missing event type for STACK ON");
|
|
return;
|
|
}
|
|
|
|
if ((which = find_hook(args, NULL)) == INVALID_HOOKNUM)
|
|
return; /* Error message already outputted */
|
|
|
|
if (which < 0)
|
|
{
|
|
if ((nhook = find_numeric_list(-which)))
|
|
list = nhook->list;
|
|
else
|
|
list = NULL;
|
|
}
|
|
else
|
|
list = hook_functions[which].list;
|
|
|
|
|
|
if (type == STACK_PUSH)
|
|
{
|
|
OnStack *new;
|
|
new = (OnStack *) new_malloc(sizeof(OnStack));
|
|
new->which = which;
|
|
new->list = list;
|
|
new->next = on_stack;
|
|
on_stack = new;
|
|
|
|
if (which < 0)
|
|
{
|
|
if (nhook && numeric_list)
|
|
remove_numeric_list(-which);
|
|
}
|
|
else
|
|
hook_functions[which].list = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
else if (type == STACK_POP)
|
|
{
|
|
OnStack *p, *tmp = (OnStack *) 0;
|
|
|
|
for (p = on_stack; p; tmp = p, p = p->next)
|
|
{
|
|
if (p->which == which)
|
|
{
|
|
if (p == on_stack)
|
|
on_stack = p->next;
|
|
else
|
|
tmp->next = p->next;
|
|
break;
|
|
}
|
|
}
|
|
if (!p)
|
|
{
|
|
say("No %s on the stack", args);
|
|
return;
|
|
}
|
|
|
|
if ((which < 0 && nhook) ||
|
|
(which >= 0 && hook_functions[which].list))
|
|
remove_hook(which, NULL, 0, 1); /* free hooks */
|
|
|
|
if (which < 0)
|
|
{
|
|
/* look -- do we have any hooks already for this numeric? */
|
|
if (!(nptr = find_numeric_list(-which)))
|
|
{
|
|
if (p->list) /* not just a placeholder? */
|
|
{
|
|
/* No. make a new list and put the stack on it */
|
|
nptr = (NumericList *) new_malloc(sizeof(NumericList));
|
|
nptr->list = p->list;
|
|
nptr->next = NULL;
|
|
nptr->numeric = -which;
|
|
sprintf(nptr->name, "%3.3d", -which);
|
|
add_numeric_list(nptr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
remove_hook(which, NULL, 0, 1);
|
|
nptr->list = p->list;
|
|
}
|
|
}
|
|
else
|
|
hook_functions[which].list = p->list;
|
|
|
|
new_free((char **)&p);
|
|
return;
|
|
}
|
|
|
|
else if (type == STACK_LIST)
|
|
{
|
|
int slevel = 0;
|
|
OnStack *osptr;
|
|
|
|
for (osptr = on_stack; osptr; osptr = osptr->next)
|
|
{
|
|
if (osptr->which == which)
|
|
{
|
|
Hook *hptr;
|
|
|
|
slevel++;
|
|
say("Level %d stack", slevel);
|
|
for (hptr = osptr->list; hptr; hptr = hptr->next)
|
|
show_hook(hptr, args);
|
|
}
|
|
}
|
|
|
|
if (!slevel)
|
|
say("The STACK ON %s list is empty", args);
|
|
return;
|
|
}
|
|
say("Unknown STACK ON type ??");
|
|
}
|
|
|
|
|
|
|
|
/* List manips especially for on's. */
|
|
void hook_add_to_list (Hook **list, Hook *item)
|
|
{
|
|
Hook *tmp, *last = NULL;
|
|
|
|
for (tmp = *list; tmp; last = tmp, tmp = tmp->next)
|
|
{
|
|
if (tmp->sernum < item->sernum)
|
|
continue;
|
|
else if ((tmp->sernum == item->sernum) && my_stricmp(tmp->nick, item->nick) < 0)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (last)
|
|
{
|
|
item->next = last->next;
|
|
last->next = item;
|
|
}
|
|
else
|
|
{
|
|
item->next = *list;
|
|
*list = item;
|
|
}
|
|
}
|
|
|
|
|
|
static Hook *hook_remove_from_list (Hook **list, char *item, int sernum)
|
|
{
|
|
Hook *tmp, *last = NULL;
|
|
|
|
for (tmp = *list; tmp; last = tmp, tmp = tmp->next)
|
|
{
|
|
if ((tmp->sernum == sernum) && !my_stricmp(tmp->nick, item))
|
|
{
|
|
if (last)
|
|
last->next = tmp->next;
|
|
else
|
|
*list = tmp->next;
|
|
return tmp;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void add_numeric_list (NumericList *item)
|
|
{
|
|
NumericList *tmp, *last = NULL;
|
|
|
|
for (tmp = numeric_list; tmp; last = tmp, tmp = tmp->next)
|
|
{
|
|
if (tmp->numeric > item->numeric)
|
|
break;
|
|
}
|
|
|
|
if (last)
|
|
{
|
|
last->next = item;
|
|
item->next = tmp;
|
|
}
|
|
else
|
|
{
|
|
item->next = numeric_list;
|
|
numeric_list = item;
|
|
}
|
|
}
|
|
|
|
static NumericList *find_numeric_list (int numeric)
|
|
{
|
|
NumericList *tmp;
|
|
|
|
for (tmp = numeric_list; tmp; tmp = tmp->next)
|
|
{
|
|
if (tmp->numeric == numeric)
|
|
return tmp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static NumericList *remove_numeric_list (int numeric)
|
|
{
|
|
NumericList *tmp, *last = NULL;
|
|
|
|
for (tmp = numeric_list; tmp; last = tmp, tmp = tmp->next)
|
|
{
|
|
if (tmp->numeric == numeric)
|
|
break;
|
|
}
|
|
|
|
if (tmp)
|
|
{
|
|
if (last)
|
|
last->next = tmp->next;
|
|
else
|
|
numeric_list = numeric_list->next;
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
#ifdef WANT_DLL
|
|
#if 0
|
|
static NumericList *remove_numeric_dll_list(int numeric)
|
|
{
|
|
NumericList *tmp, *last = NULL;
|
|
|
|
for (tmp = numeric_list; tmp; last = tmp, tmp = tmp->next)
|
|
{
|
|
if (tmp->numeric == numeric)
|
|
break;
|
|
}
|
|
|
|
if (last)
|
|
last->next = tmp->next;
|
|
else
|
|
numeric_list = numeric_list->next;
|
|
|
|
return tmp;
|
|
}
|
|
|
|
static Hook *remove_dll_hook_list(Hook **entry)
|
|
{
|
|
Hook *tmp, *last = NULL;
|
|
|
|
for (tmp = *list; tmp; last = tmp, tmp = tmp->next)
|
|
{
|
|
if (last)
|
|
last->next = tmp->next;
|
|
else
|
|
*list = tmp->next;
|
|
return tmp;
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
int remove_dll_hook(char *package)
|
|
{
|
|
NumericList *tmp;
|
|
Hook *list, *next = NULL;
|
|
int x, ret = 0;
|
|
for (x = 1; x < 999; x++)
|
|
{
|
|
if ((tmp = find_numeric_list(x)))
|
|
{
|
|
for (list = tmp->list; list; list = next)
|
|
{
|
|
next = list->next;
|
|
if (!my_stricmp(list->filename, package))
|
|
{
|
|
remove_numeric_hook(x, list->nick, list->sernum, 1);
|
|
ret++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (x = 0; x < NUMBER_OF_LISTS; x++)
|
|
{
|
|
for (list = hook_functions[x].list; list; list = next)
|
|
{
|
|
next = list->next;
|
|
if (!my_stricmp(list->filename, package))
|
|
{
|
|
remove_hook(x, list->nick, list->sernum, 1);
|
|
ret++;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#endif
|