1 /*
2  * hook.c: Does those naughty hook functions.
3  *
4  * Copyright (c) 1990 Michael Sandroff.
5  * Copyright (c) 1991, 1992 Troy Rollo.
6  * Copyright (c) 1992-1996 Matthew Green.
7  * Copyright 1993, 2003 EPIC Software Labs.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notices, the above paragraph (the one permitting redistribution),
17  *    this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The names of the author(s) may not be used to endorse or promote
20  *    products derived from this software without specific prior written
21  *    permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "irc.h"
37 #include "hook.h"
38 #include "ircaux.h"
39 #define __need_ArgList_t__
40 #include "alias.h"
41 #include "window.h"
42 #include "output.h"
43 #include "commands.h"
44 #include "ifcmd.h"
45 #include "stack.h"
46 #include "reg.h"
47 #include "functions.h"
48 
49 /*
50  * The various ON levels: SILENT means the DISPLAY will be OFF and it will
51  * suppress the default action of the event, QUIET means the display will be
52  * OFF but the default action will still take place, NORMAL means you will be
53  * notified when an action takes place and the default action still occurs,
54  * NOISY means you are notified when an action occur plus you see the action
55  * in the display and the default actions still occurs
56  */
57 struct NoiseInfo {
58 	const char *name;
59 	int	display;	/*  0 = suppress display, 1 = don't */
60 	int	alert;		/*  1 = "ON MSG hooked", 0 = shut up */
61 	int	suppress;	/*  0 = don't suppress default action
62 				 *  1 = suppress default action
63 				 * -1 = Let on hook decide */
64 	int	value;
65 	char	identifier;
66 	int	custom;
67 };
68 
69 struct NoiseInfo noise_info_templates[] = {
70 	{ "UNKNOWN", 0, 0, -1, 0, '?', 0 },
71 	{ "SILENT",  0, 0,  1, 1, '^', 0 },
72 	{ "QUIET",   0, 0,  0, 2, '-', 0 },
73 	{ "NORMAL",  0, 1,  0, 3, 0,   0 },
74 	{ "NOISY",   1, 1,  0, 4, '+', 0 },
75 	{ "SYSTEM",  1, 0,  1, 5, '%', 0 },
76 	{ NULL, 	 0, 0,  0, 0,   0, 0 }
77 };
78 
79 #define HF_NORECURSE	0x0001
80 
81 /* Hook: The structure of the entries of the hook functions lists */
82 typedef struct	hook_stru
83 {
84 	struct	hook_stru *next;
85 
86 	int 	type;		/* /on #TYPE sernum nick (arglist) stuff */
87 	int 	sernum;		/* /on #type SERNUM nick (arglist) stuff */
88 	char *	nick;		/* /on #type sernum NICK (arglist) stuff */
89 	ArgList *arglist;	/* /on #type sernum nick (ARGLIST) stuff */
90 	char *	stuff;		/* /on #type sernum nick (arglist) STUFF */
91 
92 	int	not;		/* /on #type sernum ^NICK */
93 	int	noisy;		/* /on #[^-+]TYPE sernum nick (arglist) stuff */
94 	int	flexible;	/* /on #type sernum 'NICK' (arglist) stuff */
95 
96 	int	userial;	/* Unique serial for this hook */
97 	int	skip;		/* hook will be treated like it doesn't exist */
98 	char *	filename;	/* Where it was loaded */
99 }	Hook;
100 
101 /*
102  * Current executing hook, yay!
103  * Silly name, but that can be fixed, can't it? :P
104  */
105 struct Current_hook
106 {
107 	/* Which hook we're "under", if we are under a hook */
108 	struct Current_hook *under;
109 
110 	/* Hook's userial */
111 	int userial;
112 
113 	/* If set to 1, the chain for the current hook should halt */
114 	int halt;
115 
116 	/* The hook's retval */
117 	int retval;
118 
119 	/* The buffer (i.e. the arguments) of the hook */
120 	char *buffer;
121 
122 	/* Set to 1 if buffer has been changed */
123 	int buffer_changed;
124 
125 	/*
126 	 * User-supplied information.
127 	 */
128 	char *user_supplied_info;
129 };
130 
131 /* A list of all the hook functions available */
132 typedef struct Hookables
133 {
134 	const char *name;		/* The name of the hook type */
135 	Hook	*list;			/* The list of events for type */
136 	int	params;			/* Number of parameters expected */
137 	int	mark;			/* Hook type is currently active */
138 	unsigned flags;			/* Anything else needed */
139 	char *	implied;		/* Implied output if unhooked */
140 	int	implied_protect;	/* Do not re-expand implied hook */
141 } Hookables;
142 
143 Hookables hook_function_templates[] =
144 {
145 	{ "ACTION",		NULL,	3,	0,	0,	NULL, 0 },
146 	{ "CHANNEL_CLAIM", 	NULL, 	2,  	0,  	0,  	NULL, 0 },
147 	{ "CHANNEL_LOST", 	NULL, 	2,  	0,  	0,  	NULL, 0 },
148 	{ "CHANNEL_NICK",	NULL,	3,	0,	0,	NULL, 0 },
149 	{ "CHANNEL_SIGNOFF",	NULL,	3,	0,	0,	NULL, 0 },
150 	{ "CHANNEL_SYNC",	NULL,	3,	0,	0,	NULL, 0 },
151 	{ "CONNECT",		NULL,	2,	0,	0,	NULL, 0 },
152 	{ "CTCP",		NULL,	4,	0,	0,	NULL, 0 },
153 	{ "CTCP_REPLY",		NULL,	4,	0,	0,	NULL, 0 },
154 	{ "CTCP_REQUEST",	NULL,	4,	0,	0,	NULL, 0 },
155 	{ "DCC_ACTIVITY",	NULL,	1,	0,	0,	NULL, 0 },
156 	{ "DCC_CHAT",		NULL,	2,	0,	0,	NULL, 0 },
157         { "DCC_CONNECT",        NULL,   2,      0,      0,	NULL, 0 },
158 	{ "DCC_LIST",		NULL,	8,	0,	0,	NULL, 0 },
159         { "DCC_LOST",           NULL,   2,      0,      0,	NULL, 0 },
160 	{ "DCC_OFFER", 		NULL,	2,	0,	0,	NULL, 0 },
161 	{ "DCC_RAW",		NULL,	3,	0,	0,	NULL, 0 },
162         { "DCC_REQUEST",        NULL,   4,      0,      0,	NULL, 0 },
163 	{ "DISCONNECT",		NULL,	1,	0,	0,	NULL, 0 },
164         { "ENCRYPTED_NOTICE",   NULL,   3,      0,      0,	NULL, 0 },
165         { "ENCRYPTED_PRIVMSG",  NULL,   3,      0,      0,	NULL, 0 },
166 	{ "ERROR",		NULL,	1,	0,	0,	NULL, 0 },
167 	{ "EXEC",		NULL,	2,	0,	0,	NULL, 0 },
168 	{ "EXEC_ERRORS",	NULL,	2,	0,	0,	NULL, 0 },
169 	{ "EXEC_EXIT",		NULL,	3,	0,	0,	NULL, 0 },
170 	{ "EXEC_PROMPT",	NULL,	2,	0,	0,	NULL, 0 },
171         { "EXIT",               NULL,   1,      0,      0,	NULL, 0 },
172 	{ "FLOOD",		NULL,	5,	0,	0,	NULL, 0 },
173 	{ "GENERAL_NOTICE",	NULL,	3,	0,	0,	NULL, 0 },
174 	{ "GENERAL_PRIVMSG",	NULL,	3,	0,	0,	NULL, 0 },
175 	{ "HELP",		NULL,	2,	0,	0,	NULL, 0 },
176 	{ "HOOK",		NULL,	1,	0,	0,	NULL, 0 },
177 	{ "IDLE",		NULL,	1,	0,	0,	NULL, 0 },
178 	{ "INPUT",		NULL,	1,	0,	HF_NORECURSE,	NULL, 0 },
179 	{ "INVITE",		NULL,	3,	0,	0,	NULL, 0 },
180 	{ "JOIN",		NULL,	4,	0,	0,	NULL, 0 },
181 	{ "KEYBINDING",		NULL,	3,	0,	0,	NULL, 0 },
182 	{ "KICK",		NULL,	3,	0,	0,	NULL, 0 },
183 	{ "KILL",		NULL,	5,	0,	0,	NULL, 0 },
184 	{ "LIST",		NULL,	3,	0,	0,	NULL, 0 },
185 	{ "MAIL",		NULL,	2,	0,	0,	NULL, 0 },
186 	{ "MODE",		NULL,	3,	0,	0,	NULL, 0 },
187 	{ "MODE_STRIPPED",	NULL,	3,	0,	0,	NULL, 0 },
188 	{ "MSG",		NULL,	2,	0,	0,	NULL, 0 },
189 	{ "MSG_GROUP",		NULL,	3,	0,	0,	NULL, 0 },
190 	{ "NAMES",		NULL,	2,	0,	0,	NULL, 0 },
191 	{ "NEW_NICKNAME",	NULL,	2,	0,	HF_NORECURSE,	NULL, 0 },
192 	{ "NICKNAME",		NULL,	2,	0,	0,	NULL, 0 },
193 	{ "NOTE",		NULL,	3,	0,	0,	NULL, 0 },
194 	{ "NOTICE",		NULL,	2,	0,	0,	NULL, 0 },
195 	{ "NOTIFY_SIGNOFF",	NULL,	1,	0,	0,	NULL, 0 },
196 	{ "NOTIFY_SIGNON",	NULL,	2,	0,	0,	NULL, 0 },
197 	{ "NUMERIC",		NULL,	3,	0,	0,	NULL, 0 },
198 	{ "ODD_SERVER_STUFF",	NULL,	3,	0,	0,	NULL, 0 },
199 	{ "OPERWALL",		NULL,	2,	0,	0,	NULL, 0 },
200 	{ "OPER_NOTICE",	NULL,	2,	0,	0,	NULL, 0 },
201 	{ "PART",		NULL,	2,	0,	0,	NULL, 0 },
202 	{ "PONG",		NULL,	2,	0,	0,	NULL, 0 },
203 	{ "PUBLIC",		NULL,	3,	0,	0,	NULL, 0 },
204 	{ "PUBLIC_MSG",		NULL,	3,	0,	0,	NULL, 0 },
205 	{ "PUBLIC_NOTICE",	NULL,	3,	0,	0,	NULL, 0 },
206 	{ "PUBLIC_OTHER",	NULL,	3,	0,	0,	NULL, 0 },
207 	{ "RAW_IRC",		NULL,	1,	0,	0,	NULL, 0 },
208 	{ "RAW_IRC_BYTES",	NULL,	1,	0,	0,	NULL, 0 },
209 	{ "RECONNECT_REQUIRED",	NULL,	1,	0,	0,	NULL, 0 },
210 	{ "REDIRECT",		NULL,	2,	0,	HF_NORECURSE,	NULL, 0 },
211 	{ "SEND_ACTION",	NULL,	2,	0,	HF_NORECURSE,	NULL, 0 },
212 	{ "SEND_CTCP",		NULL,	3,	0,	HF_NORECURSE,	NULL, 0 },
213 	{ "SEND_DCC_CHAT",	NULL,	2,	0,	HF_NORECURSE,	NULL, 0 },
214 	{ "SEND_EXEC",		NULL,	2,	0,	HF_NORECURSE,	NULL, 0 },
215 	{ "SEND_MSG",		NULL,	2,	0,	HF_NORECURSE,	NULL, 0 },
216 	{ "SEND_NOTICE",	NULL,	2,	0,	HF_NORECURSE,	NULL, 0 },
217 	{ "SEND_PUBLIC",	NULL,	2,	0,	HF_NORECURSE,	NULL, 0 },
218 	{ "SEND_TO_SERVER",	NULL,	3,	0,	HF_NORECURSE,	NULL, 0 },
219 	{ "SERVER_ESTABLISHED",	NULL,	2,	0,	0,	NULL, 0 },
220 	{ "SERVER_LOST",	NULL,	2,	0,	0,	NULL, 0 },
221 	{ "SERVER_NOTICE",	NULL,	1,	0,	0,	NULL, 0 },
222 	{ "SERVER_SSL_EVAL",	NULL,	6,	0,	0,	NULL, 0 },
223 	{ "SERVER_STATE",	NULL,	3,	0,	0,	NULL, 0 },
224 	{ "SERVER_STATUS",	NULL,	3,	0,	0,	NULL, 0 },
225 	{ "SET",		NULL,	2,	0,	0,	NULL, 0 },
226 	{ "SIGNAL",		NULL,	1,	0,	0,	NULL, 0 },
227 	{ "SIGNOFF",		NULL,	1,	0,	0,	NULL, 0 },
228 	{ "SILENCE",		NULL,	2,	0,	0,	NULL, 0 },
229 	{ "SSL_SERVER_CERT",	NULL,	9,	0,	0,	NULL, 0 },
230 	{ "STATUS_UPDATE",	NULL,	3,	0,	0,	NULL, 0 },
231 	{ "SWITCH_CHANNELS",	NULL,	3,	0,	0,	NULL, 0 },
232 	{ "SWITCH_QUERY",	NULL,	3,	0,	0,	NULL, 0 },
233 	{ "SWITCH_WINDOWS",	NULL,	4,	0,	0,	NULL, 0 },
234 	{ "TIMER",		NULL,	1,	0,	0,	NULL, 0 },
235 	{ "TOPIC",		NULL,	2,	0,	0,	NULL, 0 },
236 	{ "UNKNOWN_COMMAND",	NULL,	2,	0,	HF_NORECURSE, 	NULL, 0},
237 	{ "UNKNOWN_SET",	NULL,	2,	0,	HF_NORECURSE,	NULL, 0},
238 	{ "UNLOAD",		NULL,	1,	0,	0,	NULL, 0 },
239 	{ "WALL",		NULL,	2,	0,	0,	NULL, 0 },
240 	{ "WALLOP",		NULL,	3,	0,	0,	NULL, 0 },
241 	{ "WHO",		NULL,	6,	0,	0,	NULL, 0 },
242 	{ "WINDOW",		NULL,	2,	0,	HF_NORECURSE,	NULL, 0 },
243 	{ "WINDOW_COMMAND",	NULL,	1, 	0,	0,	NULL, 0 },
244 	{ "WINDOW_CREATE",	NULL,	1, 	0,	0,	NULL, 0 },
245 	{ "WINDOW_BEFOREKILL",	NULL,	1,	0,	0,	NULL, 0 },
246 	{ "WINDOW_KILL",	NULL,	2,	0,	0,	NULL, 0 },
247 	{ "WINDOW_NOTIFIED",	NULL,	2,	0,	HF_NORECURSE,	NULL, 0 },
248 	{ "WINDOW_SERVER",	NULL,	3,	0,	0,	NULL, 0 },
249 	{ "YELL",		NULL,	1,	0,	0,	NULL, 0 },
250 };
251 
252 static Hookables *hook_functions = NULL;
253 static int	 hook_functions_initialized = 0;
254 
255 static Hook **hooklist = NULL;
256 static int hooklist_size = 0;
257 static int	last_created_hook = -2;
258 static struct Current_hook *current_hook = NULL;
259 /*
260  * If deny_all_hooks is set to 1, no action is taken for any hook.
261  */
262 int deny_all_hooks = 0;
263 
264 static	struct NoiseInfo **	noise_info = NULL;
265 static	int 			noise_level_num = 0;
266 static	int 			default_noise;
267 static	const char *		current_implied_on_hook = NULL;
268 
269 extern char *	    function_cparse	(char *);
270 static void 	    add_to_list 	(Hook **list, Hook *item);
271 static Hook *	    remove_from_list 	(Hook **list, char *item, int sernum);
272 
initialize_hook_functions(void)273 static void	initialize_hook_functions (void)
274 {
275 	int	i, b;
276 	char *  p;
277 
278 	hook_functions = malloc(NUMBER_OF_LISTS * sizeof(Hookables));
279 	p = malloc(4050);
280 
281 	for (i = 0; i < FIRST_NAMED_HOOK; i++, p += 4)
282 	{
283 		snprintf(p, 4, "%03d", i);
284 		hook_functions[i].name = p;
285 		hook_functions[i].list = NULL;
286 		hook_functions[i].params = 1;
287 		hook_functions[i].mark = 0;
288 		hook_functions[i].flags = 0;
289 		hook_functions[i].implied = NULL;
290 		hook_functions[i].implied_protect = 0;
291 	}
292 
293 	for (b = 0, i = FIRST_NAMED_HOOK; i < NUMBER_OF_LISTS; b++, i++)
294 	{
295 		hook_functions[i].name = hook_function_templates[b].name;
296 		hook_functions[i].list = hook_function_templates[b].list;
297 		hook_functions[i].params = hook_function_templates[b].params;
298 		hook_functions[i].mark = hook_function_templates[b].mark;
299 		hook_functions[i].flags = hook_function_templates[b].flags;
300 		hook_functions[i].implied = NULL;
301 		hook_functions[i].implied_protect = 0;
302 	}
303 
304 	if (noise_info == NULL)
305 	{
306 		for (b = 0; noise_info_templates[b].name != NULL; b++)
307 		{
308 			noise_level_num++;
309 			if (noise_level_num == 1)
310 				noise_info = new_malloc(sizeof(struct NoiseInfo *));
311 			else
312 				RESIZE(noise_info, struct NoiseInfo *, noise_level_num);
313 
314 			noise_info[b] = new_malloc(sizeof(struct NoiseInfo));
315 			noise_info[b]->name = noise_info_templates[b].name;
316 			noise_info[b]->display = noise_info_templates[b].display;
317 			noise_info[b]->alert = noise_info_templates[b].alert;
318 			noise_info[b]->suppress = noise_info_templates[b].suppress;
319 			noise_info[b]->value = noise_info_templates[b].value;
320 			noise_info[b]->identifier = noise_info_templates[b].identifier;
321 			noise_info[b]->custom = 0;
322 			if (!strcmp(noise_info[b]->name, "NORMAL"))
323 				default_noise = b;
324 		}
325 	}
326 	hook_functions_initialized = 1;
327 }
328 
329 /*
330  * 	Add a hook to the hooklist
331  *	Returns -1 on error, or size of list if successful.
332  */
inc_hooklist(int size)333 static int	inc_hooklist (int size)
334 {
335 	int newsize, n;
336 	if (size < 1)
337 		return -1;
338 	newsize = hooklist_size + size;
339 	if (hooklist_size == 0)
340 		hooklist = new_malloc(sizeof(Hook) * newsize);
341 	else
342 		RESIZE(hooklist, Hook, newsize);
343 	for (n = hooklist_size; n < newsize; n++)
344 		hooklist[n] = NULL;
345 	hooklist_size = newsize;
346 	return hooklist_size;
347 }
348 
349 /*
350  * Removes n NULL-pointers from the end of hooklist.
351  * Returns -1 on error, and size of list if successful
352  */
dec_hooklist(int n)353 static int	dec_hooklist (int n)
354 {
355 	int size, newsize;
356 	if (n < 1)
357 	{
358 		size = hooklist_size -1;
359 		while (hooklist[size] == NULL)
360 			size--;
361 		n = hooklist_size - size - 1;
362 	}
363 	size = hooklist_size;
364 	newsize = size - n;
365 	for (n = newsize; n < hooklist_size; n++)
366 		if (hooklist[n] != NULL)
367 			return -1;
368 	RESIZE(hooklist, Hook, newsize);
369 	hooklist_size = newsize;
370 	return hooklist_size;
371 }
372 
373 /*
374  * Will return the next empty slot in the hooklist
375  */
next_empty_hookslot(void)376 static int	next_empty_hookslot (void)
377 {
378 	int n;
379 	for (n = 0; n < hooklist_size; n++)
380 		if (hooklist[n] == NULL)
381 			break;
382 	return n;
383 }
384 
385 /*
386  * find_hook: returns the numerical value for a specified hook name
387  */
find_hook(char * name,int * first,int quiet)388 static int 	find_hook (char *name, int *first, int quiet)
389 {
390 	int 	which = INVALID_HOOKNUM, i, cnt;
391 	size_t	len;
392 
393 	if (first)
394 		*first = -1;
395 
396 	if (!name || !(len = strlen(name)))
397 	{
398 		if (!quiet)
399 			say("You must specify an event type!");
400 		return INVALID_HOOKNUM;
401 	}
402 
403 	upper(name);
404 
405 	for (cnt = 0, i = FIRST_NAMED_HOOK; i < NUMBER_OF_LISTS; i++)
406 	{
407 		if (!strncmp(name, hook_functions[i].name, len))
408 		{
409 			if (first && *first == -1)
410 				*first = i;
411 
412 			if (strlen(hook_functions[i].name) == len)
413 			{
414 				cnt = 1;
415 				which = i;
416 				break;
417 			}
418 			else
419 			{
420 				cnt++;
421 				which = i;
422 			}
423 		}
424 		else if (cnt)
425 			break;
426 	}
427 
428 	if (cnt == 0)
429 	{
430 		if (is_number(name))
431 		{
432 			which = atol(name);
433 
434 			if ((which < 0) || (which >= FIRST_NAMED_HOOK))
435 			{
436 				if (!quiet)
437 					say("Numerics must be between 001 and %3d", FIRST_NAMED_HOOK - 1);
438 				return INVALID_HOOKNUM;
439 			}
440 		}
441 		else
442 		{
443 			if (!quiet)
444 				say("No such ON function: %s", name);
445 			return INVALID_HOOKNUM;
446 		}
447 	}
448 	else if (cnt > 1)
449 	{
450 		if (!quiet)
451 			say("Ambiguous ON function: %s", name);
452 		return INVALID_HOOKNUM;
453 	}
454 
455 	return which;
456 }
457 
458 
459 
460 
461 /* * * * * ADDING A HOOK * * * * * */
462 /*
463  * add_hook: Given an index into the hook_functions array, this adds a new
464  * entry to the list as specified by the rest of the parameters.  The new
465  * entry is added in alphabetical order (by nick).
466  */
add_hook(int which,char * nick,ArgList * arglist,char * stuff,int noisy,int not,int sernum,int flexible)467 static int	add_hook (int which, char *nick, ArgList *arglist, char *stuff, int noisy, int not, int sernum, int flexible)
468 {
469 	Hook	*new_h;
470 
471 	if (!(new_h = remove_from_list(&hook_functions[which].list, nick, sernum)))
472 	{
473 		new_h = (Hook *)new_malloc(sizeof(Hook));
474 		new_h->nick = NULL;
475 		new_h->stuff = NULL;
476 		new_h->filename = NULL;
477 
478 		if ((new_h->userial = next_empty_hookslot()) == hooklist_size)
479 			inc_hooklist(3);
480 	}
481 
482 	new_h->type = which;
483 	malloc_strcpy(&new_h->nick, nick);
484 	malloc_strcpy(&new_h->stuff, stuff);
485 	new_h->noisy = noisy;
486 	new_h->not = not;
487 	new_h->sernum = sernum;
488 	new_h->flexible = flexible;
489 	new_h->skip = 0;
490 	new_h->arglist = arglist;
491 	if (current_package())
492 	    malloc_strcpy(&new_h->filename, current_package());
493 	new_h->next = NULL;
494 
495 	upper(new_h->nick);
496 
497 	hooklist[new_h->userial] = new_h;
498 	add_to_list(&hook_functions[which].list, new_h);
499 
500 	last_created_hook = new_h->userial;
501 
502 	return new_h->userial;
503 }
504 
505 
506 
507 
508 /* * * * * * REMOVING A HOOK * * * * * * * */
remove_hook(int which,char * nick,int sernum,int quiet)509 static void remove_hook (int which, char *nick, int sernum, int quiet)
510 {
511 	Hook	*tmp,
512 		*next;
513 	Hook 	*prev = NULL,
514 		*top = NULL;
515 
516 	if (nick)
517 	{
518 		if ((tmp = remove_from_list(&hook_functions[which].list, nick, sernum)))
519 		{
520 			if (!quiet)
521 				say("%c%s%c removed from %s list",
522 					(tmp->flexible?'\'':'"'), nick,
523 					(tmp->flexible?'\'':'"'),
524 					hook_functions[which].name);
525 
526 			new_free(&(tmp->nick));
527 			new_free(&(tmp->stuff));
528 			new_free(&(tmp->filename));
529 			if (tmp->arglist != NULL)
530                     destroy_arglist(&(tmp->arglist));
531 
532 
533 			hooklist[tmp->userial] = NULL;
534 			if (tmp->userial == hooklist_size -1)
535 			{
536 				dec_hooklist(0);
537 			}
538 			tmp->next = NULL;
539 			new_free((char **)&tmp); /* XXX why? */
540 		}
541 		else if (!quiet)
542 			say("\"%s\" is not on the %s list", nick,
543 					hook_functions[which].name);
544 		return;
545 	}
546 
547 	top = hook_functions[which].list;
548 	for (tmp = top; tmp; tmp = next)
549 	{
550 		next = tmp->next;
551 
552 		/*
553 		 * If given a non-zero sernum, then we clean out
554 		 * only those hooks that are at that level.
555 		 */
556 		if (sernum && tmp->sernum != sernum)
557 		{
558 			prev = tmp;
559 			continue;
560 		}
561 
562 		if (prev)
563 			prev->next = tmp->next;
564 		else
565 			top = tmp->next;
566 		tmp->not = 1;
567 		new_free(&(tmp->nick));
568 		new_free(&(tmp->stuff));
569 		new_free(&(tmp->filename));
570 		tmp->next = NULL;
571 
572 		new_free((char **)&tmp);
573 	}
574 	hook_functions[which].list = top;
575 	if (!quiet)
576 	{
577 		if (sernum)
578 			say("The %s <%d> list is empty", hook_functions[which].name, sernum);
579 		else
580 			say("The %s list is empty", hook_functions[which].name);
581 	}
582 }
583 
584 /* Used to bulk-erase all of the currently scheduled ONs */
flush_on_hooks(void)585 void    flush_on_hooks (void)
586 {
587         int x;
588         int old_display = window_display;
589 
590         window_display = 0;
591         for (x = 0; x < NUMBER_OF_LISTS; x++)
592 		remove_hook(x, NULL, 0, 1);
593         window_display = old_display;
594 
595 		new_free(&hooklist);
596 		hooklist_size = 0;
597 		last_created_hook = -2;
598 
599 }
600 
unload_on_hooks(char * filename)601 void	unload_on_hooks (char *filename)
602 {
603 	int		x;
604 	Hook		*list, *next;
605 
606 	int old_display = window_display;
607 	window_display = 0;
608 
609 	for (x = 0; x < NUMBER_OF_LISTS; x++)
610 	{
611 		for (list = hook_functions[x].list; list; list = next)
612 		{
613 		    next = list->next;
614 		    if (list->filename && !strcmp(list->filename, filename))
615 			remove_hook(x, list->nick, list->sernum, 1);
616 		}
617 	}
618 
619 	window_display = old_display;
620 }
621 
622 
623 /* * * * * * SHOWING A HOOK * * * * * * */
624 /* show_hook shows a single hook */
show_hook(Hook * list,const char * name)625 static void 	show_hook (Hook *list, const char *name)
626 {
627 	char *arglist;
628 
629 	if ((arglist = print_arglist(list->arglist)))
630 	{
631 		say ("[%s] On %s from %c%s%c (%s) do %s [%s%s] <%d/#%d>",
632 			empty(list->filename) ? "*" : list->filename,
633 			name,
634 			(list->flexible ? '\'' : '"'),
635 			list->nick,
636 			(list->flexible ? '\'' : '"'),
637 			arglist,
638 			(list->not ? "nothing" : list->stuff),
639 			noise_info[list->noisy]->name,
640 			list->skip ? "/DISABLED" : "",
641 			list->sernum, list->userial);
642 		new_free(&arglist);
643 	}
644 	else
645 		say("[%s] On %s from %c%s%c do %s [%s%s] <%d/#%d>",
646 		    empty(list->filename) ? "*" : list->filename,
647 	    	name,
648   	  	(list->flexible ? '\'' : '"'), list->nick,
649  		   (list->flexible ? '\'' : '"'),
650     		(list->not ? "nothing" : list->stuff),
651 	 	   noise_info[list->noisy]->name,
652 		list->skip ? "/DISABLED" : "",
653 	    	list->sernum, list->userial);
654 }
655 
656 /*
657  * show_list: Displays the contents of the list specified by the index into
658  * the hook_functions array.  This function returns the number of entries in
659  * the list displayed
660  */
show_list(int which)661 static int show_list (int which)
662 {
663 	Hook	*list;
664 	int	cnt = 0;
665 
666 	/* Less garbage when issueing /on without args. (lynx) */
667 	for (list = hook_functions[which].list; list; list = list->next, cnt++)
668 		show_hook(list, hook_functions[which].name);
669 	return (cnt);
670 }
671 
show_all_numerics(int numeric)672 static int show_all_numerics (int numeric)
673 {
674 	int	cnt = 0;
675 	int	tot = 0;
676 
677 	for (cnt = 0; cnt < FIRST_NAMED_HOOK; cnt++)
678 		tot += show_list(cnt);
679 	return tot;
680 }
681 
682 
683 
684 /* * * * * * * * EXECUTING A HOOK * * * * * * */
685 #define NO_ACTION_TAKEN		-1
686 #define SUPPRESS_DEFAULT	 0
687 #define DONT_SUPPRESS_DEFAULT	 1
688 #define RESULT_PENDING		 2
689 static int 	do_hook_internal (int which, char **result, const char *format, va_list args);
690 
691 /*
692  * do_hook: This is what gets called whenever a MSG, INVITES, WALL, (you get
693  * the idea) occurs.  The nick is looked up in the appropriate list. If a
694  * match is found, the stuff field from that entry in the list is treated as
695  * if it were a command. First it gets expanded as though it were an alias
696  * (with the args parameter used as the arguments to the alias).  After it
697  * gets expanded, it gets parsed as a command.  This will return as its value
698  * the value of the noisy field of the found entry, or -1 if not found.
699  */
700 /* huh-huh.. this sucks.. im going to re-write it so that it works */
do_hook(int which,const char * format,...)701 int	do_hook (int which, const char *format, ...)
702 {
703 	char *	result = NULL;
704 	int	retval;
705 	va_list	args;
706 
707 	va_start(args, format);
708 	retval = do_hook_internal(which, &result, format, args);
709 	new_free(&result);
710 	va_end(args);
711 	return retval;
712 }
713 
do_hook_with_result(int which,char ** result,const char * format,...)714 int	do_hook_with_result (int which, char **result, const char *format, ...)
715 {
716 	int	retval;
717 	va_list	args;
718 
719 	va_start(args, format);
720 	retval = do_hook_internal(which, result, format, args);
721 	va_end(args);
722 	return retval;
723 }
724 
do_hook_internal(int which,char ** result,const char * format,va_list args)725 static int 	do_hook_internal (int which, char **result, const char *format, va_list args)
726 {
727 	Hook		*tmp;
728 	const char	*name 		= (char *) 0;
729 	int		retval;
730 	char *		buffer		= NULL;
731 	unsigned	display		= window_display;
732 	char *		stuff_copy;
733 	int		noise, old;
734 	char		quote;
735 	int		serial_number;
736 	struct Current_hook *hook;
737 	Hookables *	h;
738 
739 	*result = NULL;
740 
741 	if (!hook_functions_initialized)
742 		initialize_hook_functions();
743 	h = &hook_functions[which];
744 
745 	/*
746 	 * Press the buffer using the specified format string and args
747 	 * We have to do this first because even if there is no /on, the
748 	 * caller might still want to know what the result of $* is.
749 	 */
750 	if (!format)
751 		panic(1, "do_hook: format is NULL (hook type %d)", which);
752 
753 	malloc_vsprintf(&buffer, format, args);
754 
755 
756 	/*
757 	 * Decide whether to post this event.  Events are suppressed if:
758 	 *   1) $hookctl(DENY_ALL_HOOKS 1) has been turned on
759 	 *   2) There are no /on's and no implied hooks
760 	 *   3) The /on has recursed and that is forbidden.
761 	 */
762 	if (deny_all_hooks ||
763 	    (!h->list && !h->implied) ||
764 	    (h->mark && h->flags & HF_NORECURSE))
765 	{
766 		retval = NO_ACTION_TAKEN;
767 		*result = buffer;
768 		return retval;
769 	}
770 
771 	/*
772 	 * If there are no /on's, but there is an implied hook, skip
773 	 * right to the implied hook.
774 	 */
775 	if (!h->list && h->implied)
776 	{
777 		retval = NO_ACTION_TAKEN;
778 		*result = buffer;
779 		goto implied_hook;
780 	}
781 
782 	/*
783 	 * Set current_hook
784 	 */
785 	hook = new_malloc(sizeof(struct Current_hook));
786 	hook->userial = -1;
787 	hook->halt = 0;
788 	hook->under = current_hook;
789 	hook->retval = DONT_SUPPRESS_DEFAULT;
790 	hook->buffer = buffer;
791 	hook->buffer_changed = 0;
792 	hook->user_supplied_info = NULL;
793 	current_hook = hook;
794 
795 	/*
796 	 * Mark the event as being executed.  This is used to suppress
797  	 * unwanted recursion in some /on's.
798 	 */
799 	if (which >= 0)
800 		h->mark++;
801 
802         serial_number = INT_MIN;
803         for (;!hook->halt;serial_number++)
804 	{
805 	    for (tmp = h->list; !hook->halt && tmp; tmp = tmp->next)
806             {
807 		Hook *besthook = NULL;
808 		ArgList *tmp_arglist;
809 		char *buffer_copy;
810 		int bestmatch = 0;
811 		int currmatch;
812 
813 		if (tmp->sernum < serial_number)
814 		    continue;
815 
816 		if (tmp->sernum > serial_number)
817 		    serial_number = tmp->sernum;
818 
819 		for (;
820 			!hook->halt && tmp && tmp->sernum == serial_number && !tmp->skip;
821 			tmp = tmp->next)
822 		{
823 		    if (tmp->flexible)
824 		    {
825 			/* XXX What about context? */
826 			char *tmpnick;
827 			tmpnick = expand_alias(tmp->nick, hook->buffer);
828 		        currmatch = wild_match(tmpnick, hook->buffer);
829 			new_free(&tmpnick);
830 		    }
831 		    else
832 		        currmatch = wild_match(tmp->nick, hook->buffer);
833 
834 		    if (currmatch > bestmatch)
835 		    {
836 			besthook = tmp;
837 			bestmatch = currmatch;
838 		    }
839 		}
840 
841 		/* If nothing matched, then run the next serial number. */
842 		if (!besthook)
843 			break;
844 
845 		/* Run the hook */
846 		tmp = besthook;
847 
848 		/*
849 		 * If the winning event is a "excepting" event, then move
850 		 * on to the next serial number.
851 		 */
852 		if (tmp->not)
853 			break;
854 
855 		/* Copy off everything important from 'tmp'. */
856 		noise = tmp->noisy;
857 		if (!name)
858 			name = LOCAL_COPY(h->name);
859 		stuff_copy = LOCAL_COPY(tmp->stuff);
860 		quote = tmp->flexible ? '\'' : '"';
861 
862 		hook->userial = tmp->userial;
863 		tmp_arglist = clone_arglist(tmp->arglist);
864 
865 		/*
866 		 * YOU CAN'T TOUCH ``tmp'' AFTER THIS POINT!!!
867 		 */
868 
869 		/*
870 		 * Check to see if this hook is supposed to supress the
871 		 * default action for the event.
872 		 */
873 		if (noise_info[noise]->suppress == 1 && serial_number == 0)
874 			hook->retval = SUPPRESS_DEFAULT;
875 		else if (noise_info[noise]->suppress == -1 && serial_number == 0)
876 			hook->retval = RESULT_PENDING;
877 
878 		/*
879 		 * If this is a NORMAL or NOISY hook, then we tell the user
880 		 * that we're going to execute the hook.
881 		 */
882 		if (noise_info[noise]->alert)
883 			say("%s #%d activated by %c%s%c",
884 				name, hook->userial, quote, hook->buffer, quote);
885 
886 		/*
887 		 * Save some information that may be reset in the
888 		 * execution, turn off the display if the user specified.
889 		 */
890 		if (noise_info[noise]->display == 0)
891 			window_display = 0;
892 		else
893 			window_display = 1;
894 		old = system_exception;
895 
896 		buffer_copy = LOCAL_COPY(hook->buffer);
897 
898 		if (hook->retval == RESULT_PENDING)
899 		{
900 			char *xresult;
901 
902 			xresult = call_user_function(name, stuff_copy,
903 							buffer_copy,
904 							tmp_arglist);
905 
906 			if (xresult && atol(xresult))
907 				hook->retval = SUPPRESS_DEFAULT;
908 			else
909 				hook->retval = DONT_SUPPRESS_DEFAULT;
910 			if (tmp_arglist)
911 				destroy_arglist(&tmp_arglist);
912 			new_free(&xresult);
913 		}
914 		else
915 		{
916 			/*
917 			 * Ok.  Go and run the code.  It is imperitive to note
918 			 * that "tmp" may be deleted by the code executed here,
919 			 * so it is absolutely forbidden to reference "tmp"
920 			 * after this point.
921 			 */
922 			call_user_command(name, stuff_copy,
923 						buffer_copy, tmp_arglist);
924 			if (tmp_arglist)
925 				destroy_arglist(&tmp_arglist);
926 		}
927 
928 		/*
929 		 * Clean up the stuff that may have been mangled by the
930 		 * execution.
931 		 */
932 		system_exception = old;
933 		window_display = display;
934 
935 		/* Move onto the next serial number. */
936 		break;
937 	    }
938 
939 	    /* If 'tmp' is null here, we've processed all of them. */
940 	    if (!tmp)
941 		break;
942 	}
943 
944 	/*
945 	 * Mark the event as not currently being done here.
946 	 */
947 	if (which >= 0)
948 		h->mark--;
949 
950 	/*
951 	 * Reset current_hook to its previous value.
952 	 */
953 	retval = hook->retval;
954 	*result = hook->buffer;
955 	hook->buffer = NULL;
956 	if (hook->user_supplied_info)
957 		new_free(&hook->user_supplied_info);
958 	current_hook = hook->under;
959 	new_free(&hook);
960 
961 	/*
962 	 * And return the user-specified suppression level
963 	 */
964 	if (retval == SUPPRESS_DEFAULT || !h->implied)
965 		return retval;
966 
967     implied_hook:
968 #ifdef IMPLIED_ON_HOOKS
969     do
970     {
971 	char *	func_call = NULL;
972 	char *	func_retval;
973 	char	my_buffer[BIG_BUFFER_SIZE * 10 + 1];
974 	const char *old_current_implied_on_hook = current_implied_on_hook;
975 
976 	if (!h->implied)
977 		break;
978 
979 	if (!format)
980 		panic(1, "do_hook: format is NULL");
981 
982 	strlcpy(my_buffer, *result, sizeof(my_buffer));
983 
984 	if (which >= 0)
985 		h->mark++;
986 
987 	current_implied_on_hook = h->name;
988 	if (h->implied_protect)
989 	{
990 	    malloc_sprintf(&func_call, "\"%s\" %s", h->implied, my_buffer);
991 	    func_retval = function_cparse(func_call);
992 	}
993 	else
994 	{
995 	    malloc_sprintf(&func_call, "cparse(\"%s\" $*)", h->implied);
996 	    func_retval = call_function(func_call, my_buffer);
997 	}
998 	current_implied_on_hook = old_current_implied_on_hook;
999 
1000 	put_echo(func_retval);
1001 
1002 	new_free(&func_call);
1003 	new_free(&func_retval);
1004 	retval = SUPPRESS_DEFAULT;
1005 
1006 	if (which >= 0)
1007 		h->mark--;
1008     }
1009     while (0);
1010 #endif
1011 
1012 	if (!result || !*result)
1013 		panic(1, "do_hook: Didn't set result anywhere.");
1014 	return retval;
1015 }
1016 
1017 /*
1018  * shook: the SHOOK command -- this probably doesnt belong here,
1019  * and shook is probably a stupid name.  It simply asserts a fake
1020  * hook event for a given type.  Fraught with peril!
1021  */
BUILT_IN_COMMAND(shookcmd)1022 BUILT_IN_COMMAND(shookcmd)
1023 {
1024 	int which;
1025 	char *arg = next_arg(args, &args);
1026 
1027 	if ((which = find_hook(arg, NULL, 0)) == INVALID_HOOKNUM)
1028 		return;
1029 	else
1030 		do_hook(which, "%s", args);
1031 }
1032 
1033 
1034 /* * * * * * SCHEDULING AN EVENT * * * * * * * */
1035 /*
1036  * The ON command:
1037  * Format:		/ON [#][+-^]TYPE ['] [SERNUM] NICK ['] [{] STUFF [}]
1038  *
1039  * The "ON" command mainly takes three arguments.  The first argument
1040  * is the "type" of callback that you want to schedule.  This is either
1041  * a three digit number, of it is one of the strings so enumerated at the
1042  * top of this file in hook_list.  The second argument is the "nick" or
1043  * "pattern" that is to be used to match against future events.  If the
1044  * "nick" matches the text that is later passed to do_hook() with the given
1045  * "type", then the commands in "stuff" will be executed.
1046  *
1047  * If "nick" is enclosed in single quotes ('), then it is a "flexible"
1048  * pattern, and will be expanded before it is matched against the text
1049  * in do_hook.  Otherwise, the string so specified is "static", and is
1050  * used as-is in do_hook().
1051  *
1052  * Within each type, there are at least 65,535 different "serial numbers",
1053  * (there actually are MAX_INT of them, but by convention, only 16 bit
1054  * serial numbers are used, from -32,768 to 32,767) which may be used to
1055  * schedule any number of events at the given serial number.
1056  *
1057  * Each time an assertion occurs for a given "type", at most one of the
1058  * scheduled events is executed for each of the distinct serial numbers that
1059  * are in use for that event.  The event to be executed is the one at a
1060  * given serial number that "best" matches the text passed to do_hook().
1061  * While in theory, up to MAX_INT events could be executed for a given single
1062  * assertion, in practice, a hard limit of 2048 events per assertion is
1063  * enforced.
1064  *
1065  * The runtime behavior of the event being scheduled can be modified by
1066  * specifying a character at the beginning of the "type" argument.  If you
1067  * want to schedule an event at a serial number, then the first character
1068  * must be a hash (#).  The argument immediately FOLLOWING the "type"
1069  * argument, and immediately PRECEEDING the "nick" argument must be an
1070  * integer number, and is used for the serial number for this event.
1071  *
1072  * The "verbosity" of the event may also be modified by specifying at most
1073  * one of the following characters:
1074  *	A caret (^) is the SILENT level, and indicates that the event is to
1075  *		be executed with no output (window_display is turned off),
1076  *		and the "default action" (whatever that is) for the event is
1077  *		to be suppressed.  The default action is actually only
1078  *		suppressed if the SILENT level is specified for serial number
1079  *		zero.  This is the most common level used for overriding the
1080  *		output of most /on's.
1081  *	A minus (-) is the QUIET level, and is the same as the SILENT level,
1082  *		except that the default action (whatever that is) is not to
1083  *		be suppressed.
1084  *	No character is the "normal" case, and is the same as the "minus"
1085  *		level, with the addition that the client will inform you that
1086  *		the event was executed.  This is useful for debugging.
1087  *	A plus (+) is the same as the "normal" (no character specified),
1088  *		except that the output is not suppressed (window_display is
1089  *		not changed.)
1090  */
BUILT_IN_COMMAND(oncmd)1091 BUILT_IN_COMMAND(oncmd)
1092 {
1093 	char	*func,
1094 		*nick,
1095 		*serial		= NULL;
1096 	int	noisy;
1097 	int	not		= 0,
1098 		sernum		= 0,
1099 		rem		= 0,
1100 		supp		= 0,
1101 		which		= INVALID_HOOKNUM;
1102 	int	flex		= 0;
1103 	int userial;
1104 	char	type;
1105 	int	first;
1106 	ArgList *arglist = NULL;
1107 	char *str;
1108 	if (!hook_functions_initialized)
1109 		initialize_hook_functions();
1110 
1111 	/*
1112 	 * Get the type of event to be scheduled
1113 	 */
1114 	if ((func = next_arg(args, &args)) != NULL)
1115 	{
1116 		int v;
1117 		/*
1118 		 * Check to see if this has a serial number.
1119 		 */
1120 		if (*func == '#')
1121 		{
1122 			if (!(serial = next_arg(args, &args)))
1123 			{
1124 				say("No serial number specified");
1125 				return;
1126 			}
1127 			sernum = atol(serial);
1128 			func++;
1129 		}
1130 
1131 		/*
1132 		 * Get the verbosity level, if any.
1133 		 */
1134 		noisy = default_noise;
1135 
1136 		for (v = 0; v < noise_level_num; v++)
1137 		{
1138 			if (noise_info[v]->identifier != 0 &&
1139 				noise_info[v]->identifier == *func)
1140 				break;
1141 		}
1142 		if (v != noise_level_num)
1143 		{
1144 			noisy = noise_info[v]->value;
1145 			func++;
1146 		}
1147 
1148 		/*
1149 		 * Check to see if the event type is valid
1150 		 */
1151 		if ((which = find_hook(func, &first, 0)) == INVALID_HOOKNUM)
1152 		{
1153 			/*
1154 			 * Ok.  So either the user specified an invalid type
1155 			 * or they specified an ambiguous type.  Either way,
1156 			 * we're not going to be going anywhere.  So we have
1157 			 * free reign to mangle 'args' at this point.
1158 			 */
1159 
1160 			int len;
1161 
1162 			/*
1163 			 * If first is -1, then it was an unknown type.
1164 			 * An error has already been output, just return here
1165 			 */
1166 			if (first == -1)
1167 				return;
1168 
1169 			/*
1170 			 * Otherwise, its an ambiguous type.  If they were
1171 			 * trying to register the hook, then they've already
1172 			 * gotten the error message, just return;
1173 			 */
1174 			if (new_new_next_arg_count(args, &args, &type, 1))
1175 				return;
1176 
1177 			/*
1178 			 * Ok.  So they probably want a listing.
1179 			 */
1180 			len = strlen(func);
1181 			while (!my_strnicmp(func, hook_functions[first].name, len))
1182 			{
1183 			    if (!show_list(first))
1184 				say("The %s list is empty.",
1185 					hook_functions[first].name);
1186 			    first++;
1187 			}
1188 
1189 			return;
1190 		}
1191 
1192 		/*
1193 		 * If sernum is 0 and serial is "+" or "-" get a serial
1194 		 * number for the event type in question
1195 		 */
1196 		if (sernum == 0 && serial != NULL) {
1197 		    if (!strcmp(serial, "+"))
1198 			sernum = hook_find_free_serial(1, 0, which);
1199 		    else if (!strcmp(serial, "-"))
1200 			sernum = hook_find_free_serial(-1, 0, which);
1201 		}
1202 
1203 		/*
1204 		 * Check to see if this is a removal event or if this
1205 		 * is a negated event.
1206 		 */
1207 		switch (*args)
1208 		{
1209 			case '-':
1210 				rem = 1;
1211 				args++;
1212 				break;
1213 			case '^':
1214 				supp = 1;
1215 				args++;
1216 				break;
1217 			case '!':
1218 				not = 1;
1219 				args++;
1220 				break;
1221 		}
1222 
1223 		/*
1224 		 * Grab the "nick"
1225 		 */
1226 		if ((nick = new_new_next_arg_count(args, &args, &type, 1)))
1227 		{
1228 			char *exp;
1229 
1230 			nick = malloc_strdup(nick);
1231 
1232 			/*
1233 			 * If nick is empty, something is very wrong.
1234 			 */
1235 			if (!*nick)
1236 			{
1237 				say("No expression specified");
1238 				new_free(&nick);
1239 				return;
1240 			}
1241 
1242 			/*
1243 			 * If we're doing a removal, do the deed.
1244 			 */
1245 			if (rem)
1246 			{
1247 				remove_hook(which, nick, sernum, 0);
1248 				new_free(&nick);
1249 				return;
1250 			}
1251 
1252 			/*
1253 			 * Take a note if its flexible or not.
1254 			 */
1255 			if (type == '\'')
1256 				flex = 1;
1257 			else
1258 				flex = 0;
1259 
1260 
1261 			/*
1262 			 * If this is a suppressive event, then we dont want
1263 			 * to take any action for it.
1264 			 */
1265 			if (supp)
1266 				args = endstr(args);
1267 
1268 
1269 			/*
1270 			 * Slurp up any whitespace after the nick
1271 			 */
1272 			while (my_isspace(*args))
1273 				args++;
1274 
1275 
1276 			/* Ripping the alias.c-stuff mercyless */
1277 			if (*args == '(')
1278 			{
1279 				ssize_t span;
1280 				args++;
1281 				if ((span = MatchingBracket(args, '(', ')')) < 0)
1282 				{
1283 					say("Unmatched lparen in HOOK <-> to be fixed");
1284 					new_free(&nick);
1285 					return;
1286 				}
1287 				else
1288 				{
1289 					exp = args + span;
1290 					*exp++ = 0;
1291 					while (*exp && my_isspace(*exp))
1292 						exp++;
1293 					while (*args && my_isspace(*args))
1294 						args++;
1295 					/* Arglist stuff */
1296 					arglist = parse_arglist(args);
1297 					args = exp;
1298 				}
1299 
1300 			}
1301 
1302 			/*
1303 			 * Then slurp up the body ("text")
1304 			 */
1305 			if (*args == '{') /* } */
1306 			{
1307 				if (!(exp = next_expr(&args, '{'))) /* } */
1308 				{
1309 					say("Unmatched brace in ON");
1310 					new_free(&nick);
1311 					destroy_arglist(&arglist);
1312 					return;
1313 				}
1314 			}
1315 			else
1316 				exp = args;
1317 
1318 			/*
1319 			 * Schedule the event
1320 			 */
1321 			userial = add_hook(which, nick, arglist, exp, noisy, not, sernum, flex);
1322 
1323 			/*
1324 			 * Tell the user that we're done.
1325 			 */
1326 			str = print_arglist(arglist);
1327 			if (str)
1328 			{
1329 				if (which < 0)
1330 					say ("On %3.3u from %c%s%c (%s) do %s [%s] <%d/#%d>",
1331 					-which, type, nick, type, str, (not ? "nothing" : exp),
1332 					noise_info[noisy]->name, sernum, userial);
1333 				else
1334 					say ("On %s from %c%s%c (%s) do %s [%s] <%d/#%d>",
1335 						hook_functions[which].name,
1336 						type, nick, type, str,
1337 						(not ? "nothing" : exp),
1338 						noise_info[noisy]->name, sernum, userial);
1339 			}
1340 			else
1341 			{
1342 				if (which < 0)
1343 					say("On %3.3u from %c%s%c do %s [%s] <%d/#%d>",
1344 					    -which, type, nick, type,
1345 					    (not ? "nothing" : exp),
1346 					    noise_info[noisy]->name, sernum, userial);
1347 				else
1348 					say("On %s from %c%s%c do %s [%s] <%d/#%d>",
1349 						hook_functions[which].name,
1350 						type, nick, type,
1351 						(not ? "nothing" : exp),
1352 						noise_info[noisy]->name, sernum, userial);
1353 			}
1354 			/*
1355 			 * Clean up after the nick
1356 			 */
1357 			new_free(&nick);
1358 		}
1359 
1360 		/*
1361 		 * No "nick" argument was specified.  That means the user
1362 		 * either is deleting all of the events of a type, or it
1363 		 * wants to list all the events of a type.
1364 		 */
1365 		else
1366 		{
1367 			/*
1368 			 * if its a removal, do the deed
1369 			 */
1370 			if (rem)
1371 			{
1372 				remove_hook(which, (char *) 0, sernum, 0);
1373 				return;
1374 			}
1375 
1376 			/*
1377 			 * The help files say that an "/on 0" shows all
1378 			 * of the numeric ONs.  Since the ACTION hook is
1379 			 * number 0, we have to check to see if the first
1380 			 * character of "func" is a zero or not.  If it is,
1381 			 * we output all of the numeric functions.
1382 			 */
1383 			if (!strcmp(func, "0"))
1384 			{
1385 				if (!show_all_numerics(0))
1386 				    say("All numeric ON lists are empty.");
1387 			}
1388 			else if (!show_list(which))
1389 				say("The %s list is empty.",
1390 					hook_functions[which].name);
1391 		}
1392 	}
1393 
1394 	/*
1395 	 * No "Type" argument was specified.  That means the user wants to
1396 	 * list all of the ONs currently scheduled.
1397 	 */
1398 	else
1399 	{
1400 		int	total = 0;
1401 
1402 		say("ON listings:");
1403 
1404 		/*
1405 		 * Show the named events
1406 		 */
1407 		for (which = FIRST_NAMED_HOOK; which < NUMBER_OF_LISTS; which++)
1408 			total += show_list(which);
1409 
1410 		/*
1411 		 * Show the numeric events
1412 		 */
1413 		for (which = 0; which < FIRST_NAMED_HOOK; which++)
1414 			total += show_list(which);
1415 
1416 		if (!total)
1417 			say("All ON lists are empty.");
1418 	}
1419 }
1420 
1421 
1422 /* * * * * * * * * * STACKING A HOOK * * * * * * * * */
1423 typedef struct  onstacklist
1424 {
1425 	int     which;
1426 	Hook    *list;
1427 	struct onstacklist *next;
1428 }       OnStack;
1429 
1430 static	OnStack	*	on_stack = NULL;
1431 
do_stack_on(int type,char * args)1432 void	do_stack_on (int type, char *args)
1433 {
1434 	int	which;
1435 	Hook	*list;
1436 
1437 	if (!on_stack && (type == STACK_POP || type == STACK_LIST))
1438 	{
1439 		say("ON stack is empty!");
1440 		return;
1441 	}
1442 	if (!args || !*args)
1443 	{
1444 		say("Missing event type for STACK ON");
1445 		return;
1446 	}
1447 
1448 	if ((which = find_hook(args, NULL, 0)) == INVALID_HOOKNUM)
1449 		return;		/* Error message already outputted */
1450 
1451 	list = hook_functions[which].list;
1452 
1453 
1454 	if (type == STACK_PUSH)
1455 	{
1456 		OnStack	*new_os;
1457 		new_os = (OnStack *) new_malloc(sizeof(OnStack));
1458 		new_os->which = which;
1459 		new_os->list = list;
1460 		new_os->next = on_stack;
1461 		on_stack = new_os;
1462 		hook_functions[which].list = NULL;
1463 		return;
1464 	}
1465 
1466 	else if (type == STACK_POP)
1467 	{
1468 		OnStack	*p, *tmp = (OnStack *) 0;
1469 
1470 		for (p = on_stack; p; tmp = p, p = p->next)
1471 		{
1472 			if (p->which == which)
1473 			{
1474 				if (p == on_stack)
1475 					on_stack = p->next;
1476 				else
1477 					tmp->next = p->next;
1478 				break;
1479 			}
1480 		}
1481 		if (!p)
1482 		{
1483 			say("No %s on the stack", args);
1484 			return;
1485 		}
1486 
1487 		hook_functions[which].list = p->list;
1488 
1489 		new_free((char **)&p);
1490 		return;
1491 	}
1492 
1493 	else if (type == STACK_LIST)
1494 	{
1495 		int	slevel = 0;
1496 		OnStack	*osptr;
1497 
1498 		for (osptr = on_stack; osptr; osptr = osptr->next)
1499 		{
1500 			if (osptr->which == which)
1501 			{
1502 				Hook	*hptr;
1503 
1504 				slevel++;
1505 				say("Level %d stack", slevel);
1506 				for (hptr = osptr->list; hptr; hptr = hptr->next)
1507 					show_hook(hptr, args);
1508 			}
1509 		}
1510 
1511 		if (!slevel)
1512 			say("The STACK ON %s list is empty", args);
1513 		return;
1514 	}
1515 	say("Unknown STACK ON type ??");
1516 }
1517 
1518 
1519 
1520 /* List manips especially for on's. */
add_to_list(Hook ** list,Hook * item)1521 static void 	add_to_list (Hook **list, Hook *item)
1522 {
1523 	Hook *tmp, *last = NULL;
1524 
1525 	for (tmp = *list; tmp; last = tmp, tmp = tmp->next)
1526 	{
1527 		if (tmp->sernum < item->sernum)
1528 			continue;
1529 		else if ((tmp->sernum == item->sernum) && (my_stricmp(tmp->nick, item->nick) < 0))
1530 			continue;
1531 		else
1532 			break;
1533 	}
1534 
1535 	if (last)
1536 	{
1537 		item->next = last->next;
1538 		last->next = item;
1539 	}
1540 	else
1541 	{
1542 		item->next = *list;
1543 		*list = item;
1544 	}
1545 }
1546 
1547 
remove_from_list(Hook ** list,char * item,int sernum)1548 static Hook *remove_from_list (Hook **list, char *item, int sernum)
1549 {
1550 	Hook *tmp, *last = NULL;
1551 
1552 	for (tmp = *list; tmp; last = tmp, tmp = tmp->next)
1553 	{
1554 		if (tmp->sernum == sernum && !my_stricmp(tmp->nick, item))
1555 		{
1556 			if (last)
1557 				last->next = tmp->next;
1558 			else
1559 				*list = tmp->next;
1560 			return tmp;
1561 		}
1562 	}
1563 	return NULL;
1564 }
1565 
1566 
1567 /* this function traverses all the hooks for all the events in the system
1568  * trying to find a free serial number (one that is unused by any hook on
1569  * any event) in the direction given (either -1 or +1 for - and +) starting
1570  * at the given point. */
hook_find_free_serial(int dir,int from,int which)1571 int hook_find_free_serial(int dir, int from, int which) {
1572     int 	ser;
1573     Hook	*hp;
1574     int		wc;
1575 
1576     if (from == 0)
1577 	from = dir;
1578 
1579     /* We iterate through the specified (or all) lists looking for a serial
1580      * number that isn't in use.  If we make it through all of our loops
1581      * without breaking out of them, we have found an unused number */
1582     for (ser = from; (dir > 0 ? ser <= 32767 : ser >= -32767); ser += dir) {
1583 	if (which != INVALID_HOOKNUM) {
1584 	    /* a list was specified */
1585 	    hp = hook_functions[which].list;
1586 
1587 	    while (hp != NULL) {
1588 		if (hp->sernum == ser)
1589 		    break;
1590 		hp = hp->next;
1591 	    }
1592 	    if (hp == NULL)
1593 		break;
1594 	} else {
1595 	    /* no list was specified.  start digging */
1596 	    hp = NULL;
1597 	    for (wc = 0; wc < NUMBER_OF_LISTS; wc++) {
1598 		for (hp = hook_functions[wc].list; hp != NULL; hp = hp->next)
1599 		    if (hp->sernum == ser)
1600 			break;
1601 		if (hp != NULL)
1602 		    break;
1603 	    }
1604 	    if (hp == NULL)
1605 		break; /* found an unused one */
1606 	}
1607     }
1608 
1609     return ser;
1610 }
1611 
1612 /* get_noise_id() returns identifer for noise chr */
get_noise_id(char * chr)1613 static int	get_noise_id (char *chr)
1614 {
1615 	int n;
1616 	n = atol(chr);
1617 	if (n == 0 && chr[0] != '0')
1618 		for (n = 0; n < noise_level_num; n++)
1619 			if (!my_stricmp(chr, noise_info[n]->name))
1620 				break;
1621 	return n;
1622 }
1623 
1624 enum
1625 {
1626 	HOOKCTL_GET_HOOK_NOARG,
1627 
1628 	HOOKCTL_GET_HOOK,
1629 
1630 	HOOKCTL_GET_LIST,
1631 	HOOKCTL_GET_NOISE,
1632 	HOOKCTL_GET_NOISY,
1633 };
1634 enum
1635 {
1636 	HOOKCTL_ADD = 1,
1637 	HOOKCTL_ARGS,
1638 	HOOKCTL_COUNT,
1639 	HOOKCTL_CURRENT_IMPLIED_HOOK,
1640 	HOOKCTL_DEFAULT_NOISE_LEVEL,
1641 	HOOKCTL_DENY_ALL_HOOKS,
1642 	HOOKCTL_EMPTY_SLOTS,
1643 	HOOKCTL_EXECUTING_HOOKS,
1644 	HOOKCTL_FIRST_NAMED_HOOK,
1645 	HOOKCTL_GET,
1646 	HOOKCTL_HALTCHAIN,
1647 	HOOKCTL_HOOKLIST_SIZE,
1648 	HOOKCTL_LAST_CREATED_HOOK,
1649 	HOOKCTL_LIST,
1650 	HOOKCTL_LOOKUP,
1651 	HOOKCTL_MATCH,
1652 	HOOKCTL_MATCHES,
1653 	HOOKCTL_NOISE_LEVELS,
1654 	HOOKCTL_NOISE_LEVEL_NUM,
1655 	HOOKCTL_NUMBER_OF_LISTS,
1656 	HOOKCTL_PACKAGE,
1657 	HOOKCTL_POPULATED_LISTS,
1658 	HOOKCTL_REMOVE,
1659 	HOOKCTL_RETVAL,
1660 	HOOKCTL_SERIAL,
1661 	HOOKCTL_SET,
1662 	HOOKCTL_USERINFO
1663 };
1664 
1665 enum
1666 {
1667 	HOOKCTL_GET_HOOK_ARGUMENT_LIST = 1,
1668 	HOOKCTL_GET_HOOK_FLEXIBLE,
1669 	HOOKCTL_GET_HOOK_NICK,
1670 	HOOKCTL_GET_HOOK_NOT,
1671 	HOOKCTL_GET_HOOK_NOISE,
1672 	HOOKCTL_GET_HOOK_NOISY,
1673 	HOOKCTL_GET_HOOK_PACKAGE,
1674 	HOOKCTL_GET_HOOK_SERIAL,
1675 	HOOKCTL_GET_HOOK_SKIP,
1676 	HOOKCTL_GET_HOOK_STUFF,
1677 	HOOKCTL_GET_HOOK_TYPE,
1678 	HOOKCTL_GET_HOOK_STRING
1679 };
1680 
1681 /*
1682  * $hookctl() arguments:
1683  *   ADD <#!'[NOISETYPE]><list> [[#]<serial>] <nick> [(<argument list>)] <stuff>
1684  *       Argument list not yet implemented for $hookctl()
1685  *   ADD <#!'[NOISETYPE]><list> [[#]<serial>] <nick> <stuff>
1686  *       - Creates a new hook. Returns hook id.
1687  *   COUNT
1688  *       - See COUNT/LIST
1689  *   HALTCHAIN <recursive number>
1690  *       - Will set the haltflag for eventchain. May halt the current chain,
1691  *         or any chain currently being executed.
1692  *         Returns 1 on success, 0 otherwise.
1693  *   DEFAULT_NOISE_LEVEL
1694  *       - returns the 'default noise level'. It is not currently possible
1695  *         to change the current noise level, and probably never will be.
1696  *   DENY_ALL_HOOKS <arguments>
1697  *       - this sets the deny_all_hooks flag, or gets it's value. If set,
1698  *         to anything non negative, all hooks will be "ignored", and the
1699  *         default action of any event will be taken. Similar to a /DUMP ON
1700  *         but doens't actually remove any hooks.
1701  *   EMPTY_SLOTS
1702  *       - will return a list of empty slots in the hook-list.
1703  *   EXECUTING_HOOKS
1704  *       - will return a list of the current executing hooks. This is a
1705  *         'recursive' list, listing the current hook first.
1706  *   FIRST_NAMED_HOOK
1707  *       - returns FIRST_NAMED_HOOK
1708  *   HOOKLIST_SIZE
1709  *       - will returns HOOKLIST_SIZE
1710  *   LAST_CREATED_HOOK
1711  *       - returns the value of LAST_CREATED_HOOK
1712  *   LIST
1713  *   	 - See COUNT/LIST
1714  *   NOISE_LEVELS <pattern>
1715  *       - Returns a list of 'noise-types'. If <pattern> is specified only
1716  *         noise levels matching pattern will be returns.
1717  *   NOISE_LEVEL_NUM
1718  *       - Returns NOISE_LEVEL_NUM
1719  *   NUMBER_OF_LISTS
1720  *       - Returns NUBER_OF_LISTS
1721  *   PACKAGE <package> [<list>]
1722  *       - Returns a list of hooks of the given package. If <list> is
1723  *         specified, it will return only hooks in list <list>
1724  *   RETVAL <recursive number> [<new value>]
1725  *       - If recursve number isn't specified, 0 (the current) is specified.
1726  *         Will either return the value of retval for the given hook, or
1727  *         set it.
1728  *   SERIAL <serial> [<list>]
1729  *       - Works exactly like PACKAGE.
1730  *
1731  *   GET <type> <arg>
1732  *       - See GET/SET
1733  *   LOOKUP <list> <nick> [<serial>]
1734  *       - Returns hook matching given parametres.
1735  *   MATCH <list> <pattern>
1736  *       - Returns a list of matching hooks.
1737  *   REMOVE <hook id>
1738  *       - Removes the hook with the given hook ID. Returns 1 on success,
1739  *         0 otherwise.
1740  *   SET <type> <arg>
1741  *       - See GET/SET
1742  *
1743  *   * GET/SET usage
1744  *   GET gettype <arguments>
1745  *       - will return 'gettype'
1746  *   SET gettype <arguments>
1747  *       - will set 'gettype' or similar, and return 1 on success. Not all
1748  *         'gettypes' may be set, and not all gettypes will silently ignore
1749  *         being set.
1750  *
1751  *   It is very important to remember that GET won't ever SET anything(!!!)
1752  *   Won't, and shouldn't.
1753  *
1754  *   * GET/SET types:
1755  *   HOOK <argument>
1756  *       - More info on this under GET/SET HOOK
1757  *   LIST <arguments>
1758  *       - More info on this under GET/SET LIST
1759  *   NOISE <argument>
1760  *   NOISY <argument>
1761  *       - More info on this under GET/SET NOISE/NOISY
1762  *   MATCHES <argument>
1763  *       - More info on this under GET/SET MATCHES
1764  *
1765  *   * GET/SET HOOK usage:
1766  *       GET HOOK <hook id> <prop> <arg>
1767  *       SET HOOK <hook id> <prop> <arg>
1768  *
1769  *       <prop> may be one of the following:
1770  *		 ARGUMENT_LIST
1771  *		 	 - Returns or sets the argument list, if SET and <arg> is empty,
1772  * 			   it will be set to NULL, and therefore not used.
1773  *       FLEXIBLE
1774  *           - Returns or sets the value of flexible
1775  *       NICK
1776  *           - Sets or gets the hook's nick. The position of the hook will
1777  *             be changed if needed, and it is not possible to change this
1778  *             to a "crashing nick"
1779  *       NOT
1780  *           - Sets or gets the value of NOT.
1781  *       NOISE
1782  *       NOISY
1783  *           - Sets or returns the value of noisy.
1784  *       PACKAGE
1785  *           - Returns or sets the hook's packagename
1786  *       SERIAL
1787  *           - Returns or sets the serial for the hook. The hook's position
1788  *             in the list will be changed if necesarry, and it is not
1789  *             possible to set the serial to a crashing serial.
1790  *       SKIP
1791  *           - Returns or sets the value of skip.
1792  *       STUFF
1793  *           - Returns or sets the value of stuff.
1794  *       TYPE
1795  *           - Returns or sets the type.
1796  *
1797  *   * GET/SET LIST usage:
1798  *       GET LIST <listname> <prop>
1799  *       SET LIST <listname> <prop> - not functional
1800  *
1801  *       <prop> may be one of the following:
1802  *       COUNT
1803  *           - Returns count of hooks
1804  *       FLAGS
1805  *           - Returns flags
1806  *       MARK
1807  *           - Returns mark
1808  *       NAME
1809  *           - Returns name
1810  *       PARAMETERS
1811  *       PARAMS
1812  *           - Returns value of params
1813  *
1814  *
1815  *
1816  *   * GET/SET NOISE/NOISY usage:
1817  *       GET NOISE <noisename> <prop>
1818  *       SET NOISE <noisename> <prop> - not functional
1819  *
1820  *       <prop> may be one of the following:
1821  *           ALERT
1822  *               - returns value of alert.
1823  *           CUSTOM
1824  *               - returns value of custom.
1825  *           DISPLAY
1826  *               - returns value of display.
1827  *           IDENTIFIER
1828  *               - returns value of identifier.
1829  *           NAME
1830  *               - returns name.
1831  *           SUPPRESS
1832  *               - returns value of suppress.
1833  *           VALUE
1834  *               - returns value of value. d'oh!
1835  *
1836  *   * GET/SET MATCHES:
1837  *       - This function is not ready yet, and will currently RETURN_NULL.
1838  *
1839  *   * COUNT/LIST usage:
1840  *   	COUNT / LIST work doing the same, the only difference is that
1841  *   	COUNT will return the count of lists/hooks, while list will return
1842  *   	a list
1843  *
1844  * 	    The following options are permitted:
1845  *
1846  *	 		LISTS <pattern>
1847  *          - Will either return all lists available, or only the
1848  *            matching ones.
1849  *	 		POPULATED_LISTS <pattern>
1850  *	 	    - Works _just_ like LISTS, but will only return "populated"
1851  *	 	      lists
1852  *   		HOOKS <pattern>
1853  *   	 	- Will either return all the hooks on the system, or all
1854  *   	 	  the hooks in the matching lists
1855 
1856  */
1857 
hookctl(char * input)1858 char *hookctl (char *input)
1859 {
1860 	/* ALL the variables are due for a clean up */
1861 	int go = 0;
1862 	int hooknum = INVALID_HOOKNUM;
1863 	int action;
1864 	int prop;
1865 	int userial;
1866 	int tmp_int, tmp_int2;
1867 	int set;
1868 	int ser;
1869 	int is_serial = 0;
1870 	int set_noisy = default_noise;
1871 	int set_serial = 0;
1872 	int set_not = 0;
1873 	int serial = 0;
1874 	int set_flex = 0;
1875 	int bestmatch = 0;
1876 	int currmatch;
1877 	int sernum;
1878 	int halt = 0;
1879 	int id;
1880 	size_t retlen;
1881 	char *nam;
1882 	char *str;
1883 	char *hookname;
1884 	char *name;
1885 	char *ret = NULL;
1886 	char *tmp;
1887 	char *nick;
1888 	char *buffer;
1889 
1890 	struct Current_hook *curhook;
1891 
1892 	Hookables *hooks = NULL;
1893 	Hook *hook = NULL;
1894 	Hook *tmp_hook;
1895 
1896 	ArgList *tmp_arglist = NULL;
1897 
1898 	if (!hook_functions_initialized)
1899 		initialize_hook_functions();
1900 
1901 	if (!input || !*input)
1902 		RETURN_EMPTY;
1903 
1904 	GET_FUNC_ARG(str, input);
1905 	go = vmy_strnicmp (strlen(str), str,
1906 		"ADD",
1907 		"ARGS",
1908 		"COUNT",
1909 		"CURRENT_IMPLIED_HOOK",
1910 		"DEFAULT_NOISE_LEVEL",
1911 		"DENY_ALL_HOOKS",
1912 		"EMPTY_SLOTS",
1913 		"EXECUTING_HOOKS",
1914 		"FIRST_NAMED_HOOK",
1915 		"GET",
1916 		"HALTCHAIN",
1917 		"HOOKLIST_SIZE",
1918 		"LAST_CREATED_HOOK",
1919 		"LIST",
1920 		"LOOKUP",
1921 		"MATCH",
1922 		"MATCHES",
1923 		"NOISE_LEVELS",
1924 		"NOISE_LEVEL_NUM",
1925 		"NUMBER_OF_LISTS",
1926 		"POPULATED_LISTS",
1927 		"PACKAGE",
1928 		"REMOVE",
1929 		"RETVAL",
1930 		"SERIAL",
1931 		"SET",
1932 		"USERINFO",
1933 		NULL);
1934 
1935 	switch (go)
1936 	{
1937 
1938 	/* go-switch */
1939 	case 0:
1940 		RETURN_EMPTY;
1941 		break;
1942 
1943 	/* go-switch */
1944 	case HOOKCTL_LOOKUP:
1945 		ser = 0;
1946 		if (!input || !*input)
1947 			RETURN_EMPTY;
1948 		GET_FUNC_ARG(hookname, input);
1949 		if ((hooknum = find_hook(hookname, NULL, 1)) == INVALID_HOOKNUM)
1950 			RETURN_EMPTY;
1951 		if (!input || !*input)
1952 			RETURN_EMPTY;
1953 		GET_FUNC_ARG(nick, input);
1954 		if (input && *input)
1955 			GET_INT_ARG(ser, input);
1956 		for (
1957 			hook = hook_functions[hooknum].list;
1958 			hook != NULL;
1959 			hook = hook->next)
1960 		{
1961 			if (hook->sernum != ser || my_stricmp(nick, hook->nick))
1962 				continue;
1963 			RETURN_INT(hook->userial);
1964 		}
1965 		RETURN_INT(-1);
1966 		break;
1967 
1968 	/* go-switch */
1969 	case HOOKCTL_DEFAULT_NOISE_LEVEL:
1970 		RETURN_STR(noise_info[default_noise]->name);
1971 		break;
1972 
1973 	/* go-switch */
1974 	case HOOKCTL_FIRST_NAMED_HOOK:
1975 		RETURN_INT(FIRST_NAMED_HOOK);
1976 		break;
1977 
1978 	/* go-switch */
1979 	case HOOKCTL_HOOKLIST_SIZE:
1980 		RETURN_INT(hooklist_size);
1981 		break;
1982 
1983 	/* go-switch */
1984 	case HOOKCTL_LAST_CREATED_HOOK:
1985 		RETURN_INT(last_created_hook);
1986 		break;
1987 
1988 	/* go-switch */
1989 	case HOOKCTL_NOISE_LEVEL_NUM:
1990 		RETURN_INT(noise_level_num);
1991 		break;
1992 
1993 	/* go-switch */
1994 	case HOOKCTL_NUMBER_OF_LISTS:
1995 		RETURN_INT(NUMBER_OF_LISTS);
1996 		break;
1997 
1998 	case HOOKCTL_CURRENT_IMPLIED_HOOK:
1999 		RETURN_STR(current_implied_on_hook);
2000 		break;
2001 
2002 	/* go-switch */
2003 	case HOOKCTL_RETVAL:
2004 		if (!current_hook)
2005 			RETURN_EMPTY;
2006 		if (!input || !*input)
2007 			RETURN_INT(current_hook->retval);
2008 		else
2009 		{
2010 			GET_FUNC_ARG(str, input);
2011 			if (!isdigit(str[0]) && str[0] != '-')
2012 				tmp_int = -2 + vmy_strnicmp(strlen(str), str,
2013 					"NO_ACTION_TAKEN",	"SUPPRESS_DEFAULT",
2014 					"DONT_SUPPRESS_DEFAULT", "RESULT_PENDING",
2015 					NULL
2016 				);
2017 			else
2018 				GET_INT_ARG(tmp_int, str);
2019 
2020 			if (tmp_int < -1 || tmp_int > 2)
2021 				RETURN_INT(0);
2022 			current_hook->retval = tmp_int;
2023 			RETURN_INT(1);
2024 		}
2025 		break;
2026 
2027 	/* go-switch */
2028 	case HOOKCTL_DENY_ALL_HOOKS:
2029 		if (!input || !*input)
2030 			RETURN_INT(deny_all_hooks);
2031 		else
2032 		{
2033 			GET_INT_ARG(tmp_int, input);
2034 			deny_all_hooks = tmp_int ? 1 : 0;
2035 			RETURN_INT(deny_all_hooks);
2036 		}
2037 		break;
2038 
2039 	/* go-switch */
2040 	case HOOKCTL_EMPTY_SLOTS:
2041 		for (tmp_int = 0; tmp_int < hooklist_size; tmp_int++)
2042 			if (hooklist[tmp_int] == NULL)
2043 				malloc_strcat_wordlist_c(&ret, space, ltoa(tmp_int), &retlen);
2044 
2045 		RETURN_MSTR(ret);
2046 		break;
2047 
2048 	/* go-switch */
2049 	case HOOKCTL_COUNT:
2050 	case HOOKCTL_LIST:
2051 		/* Shamelessly using prop here as well. Save the variables! */
2052 		if (!input || !*input)
2053 			prop = 1;
2054 		else
2055 		{
2056 			GET_FUNC_ARG(str,input);
2057 			prop = vmy_strnicmp(strlen(str), str, "LISTS", "POPULATED_LISTS", "HOOKS", NULL);
2058 		}
2059 		if (input && *input)
2060 		{
2061 			GET_FUNC_ARG(str, input);
2062 		}
2063 		else
2064 		{
2065 			str = NULL;
2066 		}
2067 		if (prop == 0)
2068 			RETURN_EMPTY;
2069 		tmp_int2 = 0;
2070 		/* Walk through the entire list, starting with the named hooks */
2071 		for (tmp_int = 0; tmp_int < NUMBER_OF_LISTS; tmp_int++)
2072 		{
2073 			if (((prop != 1 && hook_functions[tmp_int].list == NULL)
2074 				|| (str && !wild_match(str, hook_functions[tmp_int].name))
2075 			)
2076 			)
2077 				continue;
2078 			if (prop != 3)
2079 			{
2080 				if (go == HOOKCTL_COUNT)
2081 					tmp_int2++;
2082 				else
2083 					malloc_strcat_wordlist_c(&ret, space, hook_functions[tmp_int].name,
2084 						&retlen);
2085 				continue;
2086 			}
2087 			for (tmp_hook = hook_functions[tmp_int].list;
2088 				tmp_hook != NULL;
2089 				tmp_hook = tmp_hook->next)
2090 			{
2091 				if (go == HOOKCTL_COUNT)
2092 					tmp_int2++;
2093 				else
2094 					malloc_strcat_wordlist_c(&ret, space, ltoa(tmp_hook->userial),
2095 						&retlen);
2096 			}
2097 		}
2098 		if (go == HOOKCTL_COUNT)
2099 			RETURN_INT(tmp_int2);
2100 		else
2101 			RETURN_MSTR(ret);
2102 		break;
2103 
2104 	/* go-switch */
2105 	case HOOKCTL_SERIAL:
2106 	case HOOKCTL_PACKAGE:
2107 		if (go == HOOKCTL_SERIAL)
2108 			is_serial = 1;
2109 		if (!input || !*input)
2110 			RETURN_EMPTY;
2111 		if (is_serial)
2112 		{
2113 			GET_INT_ARG(serial, input);
2114 		}
2115 		else
2116 		{
2117 			GET_FUNC_ARG(str, input);
2118 		}
2119 		if (input && *input)
2120 		{
2121 			GET_FUNC_ARG(hookname, input);
2122 			if ((hooknum = find_hook (hookname, NULL, 1)) == INVALID_HOOKNUM)
2123 				RETURN_EMPTY;
2124 		}
2125 		if (hooknum == INVALID_HOOKNUM)
2126 		{
2127 			for (tmp_int = 0; tmp_int < hooklist_size; tmp_int++)
2128 			{
2129 				if (hooklist[tmp_int] == NULL)
2130 					continue;
2131 				if ((is_serial && hooklist[tmp_int]->sernum == serial) ||
2132 					(!is_serial && hooklist[tmp_int]->filename && !my_stricmp(hooklist[tmp_int]->filename, str)))
2133 				{
2134 					malloc_strcat_wordlist_c(&ret, space, ltoa(tmp_int), &retlen);
2135 				}
2136 			}
2137 		}
2138 		else
2139 		{
2140 			if (hook_functions[hooknum].list == NULL)
2141 				RETURN_EMPTY;
2142 			for (
2143 				hook = hook_functions[hooknum].list;
2144 				hook != NULL;
2145 				hook = hook->next)
2146 				if ((is_serial && hook->sernum == serial) ||
2147 					(!is_serial && hook->filename && !my_stricmp(hook->filename, str))
2148 				)
2149 				{
2150 					malloc_strcat_wordlist_c(&ret, space, ltoa(hook->userial), &retlen);
2151 				}
2152 		}
2153 		RETURN_MSTR(ret);
2154 		break;
2155 
2156 	/* go-switch */
2157 	case HOOKCTL_NOISE_LEVELS:
2158 		if (!input || !*input)
2159 			nam = NULL;
2160 		else
2161 		{
2162 			GET_FUNC_ARG(nam, input);
2163 		}
2164 		for (tmp_int = 0; tmp_int < noise_level_num; tmp_int++)
2165 		{
2166 			if (!nam || wild_match(nam, noise_info[tmp_int]->name))
2167 				malloc_strcat_wordlist_c(&ret, space, noise_info[tmp_int]->name, &retlen);
2168 		}
2169 		RETURN_MSTR(ret);
2170 		break;
2171 
2172 	/* go-switch */
2173 	case HOOKCTL_EXECUTING_HOOKS:
2174 		if (current_hook == NULL)
2175 			RETURN_EMPTY;
2176 		for (
2177 			curhook = current_hook;
2178 			curhook != NULL; curhook = curhook->under)
2179 			malloc_strcat_wordlist_c(&ret, space, ltoa(curhook->userial), &retlen);
2180 		RETURN_MSTR(ret);
2181 		break;
2182 
2183 	/* go-switch */
2184 	case HOOKCTL_MATCHES:
2185 		/*
2186 		 * serial, package, stuff, nick, list
2187 		 */
2188 		RETURN_EMPTY;
2189 
2190 	/* go-switch */
2191 	case HOOKCTL_GET:
2192 	case HOOKCTL_SET:
2193 		set = (go == HOOKCTL_SET);
2194 
2195 		if (!input || !*input)
2196 			RETURN_EMPTY;
2197 
2198 		if (atoi(input) == 0 && input[0] != '0')
2199 		{
2200 			GET_FUNC_ARG(str, input);
2201 			action = vmy_strnicmp(strlen(str), str,
2202 				"HOOK",
2203 				"LIST",
2204 				"NOISE",
2205 				"NOISY",
2206 				NULL
2207 			);
2208 
2209 			if (action == 0)
2210 				RETURN_EMPTY;
2211 		}
2212 		else
2213 			action = HOOKCTL_GET_HOOK_NOARG;
2214 
2215 		switch (action)
2216 		{
2217 		/* action-switch */
2218 		case HOOKCTL_GET_HOOK:
2219 		case HOOKCTL_GET_HOOK_NOARG:
2220 
2221 			GET_INT_ARG(userial, input);
2222 			if (userial == -1 && current_hook)
2223 				userial = current_hook->userial;
2224 			if (userial < 0
2225 				|| hooklist_size <= userial
2226 				|| hooklist[userial] == NULL)
2227 				RETURN_EMPTY;
2228 			hook = hooklist[userial];
2229 			if (!set && (!input || !*input))
2230 				RETURN_STR(hook->stuff);
2231 			GET_FUNC_ARG(str, input);
2232 
2233 			prop = vmy_strnicmp(strlen(str), str,
2234 				"ARGUMENT_LIST",
2235 				"FLEXIBLE", 	"NICK", 	"NOT",		"NOISE",
2236 				"NOISY", 		"PACKAGE",	"SERIAL", 	"SKIP",
2237 				"STUFF",		"TYPE", 	"STRING",	NULL);
2238 			if (prop == 0)
2239 				RETURN_EMPTY;
2240 
2241 			if (!input || !*input)
2242 				str = LOCAL_COPY(empty_string);
2243 			else
2244 				str = input;
2245 
2246 			switch (prop)
2247 			{
2248 			/* prop-switch */
2249 			case HOOKCTL_GET_HOOK_ARGUMENT_LIST:
2250 				if (!set)
2251 				{
2252 					if (!hook->arglist)
2253 						RETURN_EMPTY;
2254 					str = print_arglist(hook->arglist);
2255 					if (!str)
2256 						RETURN_EMPTY;
2257 					return str;
2258 				}
2259 
2260 				if (hook->arglist)
2261 					destroy_arglist(&(hook->arglist));
2262 
2263 				if (!str || !*str)
2264 				{
2265 					hook->arglist = NULL;
2266 					RETURN_EMPTY;
2267 				}
2268 
2269 				hook->arglist = parse_arglist(str);
2270 				str = print_arglist(hook->arglist);
2271 				if (!str)
2272 				{
2273 					destroy_arglist(&(hook->arglist));
2274 					RETURN_EMPTY;
2275 				}
2276 				return str;
2277 
2278 				break;
2279 
2280 			case HOOKCTL_GET_HOOK_NICK:
2281 				if (!set)
2282 					RETURN_STR(hook->nick);
2283 				str = malloc_strdup(upper(str));
2284 				for (
2285 					tmp_hook = hook_functions[hook->type].list;
2286 					tmp_hook != NULL;
2287 					tmp_hook = tmp_hook->next
2288 				)
2289 				{
2290 					if (!strcmp(tmp_hook->nick, str)
2291 						&& tmp_hook->sernum == hook->sernum)
2292 					{
2293 						new_free (&str);
2294 						RETURN_INT(0);
2295 					}
2296 				}
2297 				remove_from_list(
2298 					&hook_functions[hook->type].list,
2299 					hook->nick,
2300 					hook->sernum
2301 				);
2302 				new_free(&hook->nick);
2303 				hook->nick = str;
2304 				add_to_list(
2305 					&hook_functions[hook->type].list,
2306 					hook
2307 				);
2308 				RETURN_INT(1);
2309 				break;
2310 
2311 			/* prop-switch */
2312 			case HOOKCTL_GET_HOOK_STUFF:
2313 				if (!set)
2314 					RETURN_STR(hook->stuff);
2315 				new_free (&(hook->stuff));
2316 				hook->stuff = malloc_strdup(str);
2317 				RETURN_INT(1);
2318 				break;
2319 
2320 			/* prop-switch */
2321 			case HOOKCTL_GET_HOOK_NOT:
2322 				if (!set)
2323 					RETURN_INT(hook->not);
2324 				hook->not = atol(str) ? 1 : 0;
2325 				RETURN_INT(1);
2326 				break;
2327 
2328 			case HOOKCTL_GET_HOOK_SKIP:
2329 				if (!set)
2330 					RETURN_INT(hook->skip);
2331 				hook->skip = atol(str) ? 1 : 0;
2332 				RETURN_INT(1);
2333 				break;
2334 
2335 			/* prop-switch */
2336 			case HOOKCTL_GET_HOOK_NOISE:
2337 			case HOOKCTL_GET_HOOK_NOISY:
2338 				if (!set)
2339 					RETURN_STR(noise_info[hook->noisy]->name);
2340 				if ((tmp_int = get_noise_id(input)) >= noise_level_num
2341 					|| tmp_int < 0
2342 				)
2343 					RETURN_INT(0);
2344 				hook->noisy = tmp_int;
2345 				RETURN_INT(1);
2346 				break;
2347 
2348 			/* prop-switch */
2349 			case HOOKCTL_GET_HOOK_SERIAL:
2350 				if (!set)
2351 					RETURN_INT(hook->sernum);
2352 				tmp_int = atol(str);
2353 				for (
2354 					tmp_hook = hook_functions[hook->type].list;
2355 					tmp_hook != NULL;
2356 					tmp_hook = tmp_hook->next
2357 				)
2358 				{
2359 					if (!strcmp(tmp_hook->nick, hook->nick)
2360 						&& tmp_hook->sernum == tmp_int
2361 					)
2362 						RETURN_INT(0);
2363 				}
2364 				remove_from_list(
2365 					&hook_functions[hook->type].list,
2366 					hook->nick,
2367 					hook->sernum
2368 				);
2369 
2370 				hook->sernum = tmp_int;
2371 				add_to_list(
2372 					&hook_functions[hook->type].list,
2373 					hook
2374 				);
2375 				RETURN_INT(1);
2376 				break;
2377 
2378 			/* prop-switch */
2379 			case HOOKCTL_GET_HOOK_FLEXIBLE:
2380 				if (!set)
2381 					RETURN_INT(hook->flexible);
2382 				hook->flexible = atol(str) ? 1 : 0;
2383 				RETURN_INT(1);
2384 				break;
2385 
2386 			/* prop-switch */
2387 			case HOOKCTL_GET_HOOK_PACKAGE:
2388 				if (!set)
2389 					RETURN_STR(hook->filename);
2390 				new_free (&(hook->filename));
2391 				hook->filename = malloc_strdup(str);
2392 				RETURN_INT(1);
2393 				break;
2394 
2395 			/* prop-switch */
2396 			case HOOKCTL_GET_HOOK_TYPE:
2397 				if (!set)
2398 					RETURN_STR(hook_functions[hook->type].name);
2399 				break;
2400 
2401 			case HOOKCTL_GET_HOOK_STRING:
2402 			{
2403 				char *retval = NULL;
2404 				size_t	clue = 0;
2405 				char	blah[10];
2406 
2407 				/* Just to start off */
2408 				retval = new_malloc(128);
2409 				*retval = 0;
2410 
2411 				/* ON <SERIAL-INDICATOR><NOISE><TYPE> <SERIAL-NUMBER>
2412 						<QUOTE><PATTERN><QUOTE> {<STUFF>} */
2413 				malloc_strcat_c(&retval, "ON ", &clue);
2414 
2415 				if (hook->sernum)
2416 					malloc_strcat_c(&retval, "#", &clue);
2417 				if (noise_info[hook->noisy]->identifier)
2418 				{
2419 					blah[0] = noise_info[hook->noisy]->identifier;
2420 					blah[1] = 0;
2421 					malloc_strcat_c(&retval, blah, &clue);
2422 				}
2423 				malloc_strcat_c(&retval, hook_functions[hook->type].name, &clue);
2424 
2425 				if (hook->sernum)
2426 				{
2427 					snprintf(blah, sizeof blah, " %d", hook->sernum);
2428 					malloc_strcat_c(&retval, blah, &clue);
2429 				}
2430 
2431 				malloc_strcat_c(&retval, space, &clue);
2432 				if (hook->not)
2433 				{
2434 					blah[0] = '^';
2435 					blah[1] = 0;
2436 					malloc_strcat_c(&retval, blah, &clue);
2437 				}
2438 
2439 				if (hook->flexible)
2440 				{
2441 					blah[0] = '\'';
2442 					blah[1] = 0;
2443 				}
2444 				else
2445 				{
2446 					blah[0] = '"';
2447 					blah[1] = 0;
2448 				}
2449 				malloc_strcat_c(&retval, blah, &clue);
2450 				malloc_strcat_c(&retval, hook->nick, &clue);
2451 				malloc_strcat_c(&retval, blah, &clue);
2452 				malloc_strcat_c(&retval, space, &clue);
2453 
2454 				if (hook->arglist)
2455 				{
2456 					char *arglist = print_arglist(hook->arglist);
2457 
2458 					blah[0] = '(';
2459 					blah[1] = 0;
2460 					malloc_strcat_c(&retval, blah, &clue);
2461 					malloc_strcat_c(&retval, arglist, &clue);
2462 					blah[0] = ')';
2463 					malloc_strcat_c(&retval, blah, &clue);
2464 				    malloc_strcat_c(&retval, space, &clue);
2465 					new_free(&arglist);
2466 				}
2467 
2468 				if (!hook->not)
2469 				{
2470 					blah[0] = '{';
2471 					blah[1] = 0;
2472 					malloc_strcat_c(&retval, blah, &clue);
2473 					malloc_strcat_c(&retval, hook->stuff, &clue);
2474 					blah[0] = '}';
2475 					malloc_strcat_c(&retval, blah, &clue);
2476 				}
2477 				RETURN_MSTR(retval);
2478 			}
2479 		/* end prop-switch */
2480 		}
2481 		RETURN_EMPTY;
2482 		break;
2483 
2484 		/* action switch */
2485 		case HOOKCTL_GET_NOISE:
2486 		case HOOKCTL_GET_NOISY:
2487 			if (!input || !*input)
2488 				RETURN_EMPTY;
2489 			if (isdigit(input[0]))
2490 			{
2491 				GET_INT_ARG(id, input);
2492 				if (noise_level_num <= id)
2493 					RETURN_EMPTY;
2494 			}
2495 			else
2496 			{
2497 				GET_FUNC_ARG(name, input);
2498 				for (tmp_int = 0; tmp_int < noise_level_num; tmp_int++)
2499 				{
2500 					if (!my_stricmp(name, noise_info[tmp_int]->name))
2501 						break;
2502 				}
2503 				if (noise_level_num == tmp_int)
2504 					RETURN_EMPTY;
2505 				id = tmp_int;
2506 			}
2507 
2508 			if (!input || !*input)
2509 				RETURN_EMPTY;
2510 
2511 			GET_FUNC_ARG(str, input);
2512 			switch (vmy_strnicmp(strlen(str), str,
2513 				"ALERT", 	"CUSTOM", 	"DISPLAY", 	"IDENTIFIER",
2514 				"NAME", 	"SUPPRESS", "VALUE", 	NULL)
2515 			)
2516 			{
2517 				case 1: 	RETURN_INT(noise_info[id]->alert ? 1 : 0);
2518 				case 2: 	RETURN_INT(noise_info[id]->custom ? 1 : 0);
2519 				case 3: 	RETURN_INT(noise_info[id]->display ? 1 : 0);
2520 				case 4: 	RETURN_INT(noise_info[id]->identifier);
2521 				case 5: 	RETURN_STR(noise_info[id]->name);
2522 				case 6:		RETURN_INT(noise_info[id]->suppress ? 1 : 0);
2523 				case 7:		RETURN_INT(noise_info[id]->value);
2524 				default: 	RETURN_EMPTY;
2525 			}
2526 			break;
2527 
2528 		/* action-switch */
2529 		case HOOKCTL_GET_LIST:
2530 
2531 			GET_FUNC_ARG(hookname, input);
2532 			if (atoi(hookname) == -1)
2533 			{
2534 				if (current_hook == NULL)
2535 					RETURN_EMPTY;
2536 				hooknum = hooklist[current_hook->userial]->type;
2537 			}
2538 			else
2539 			{
2540 				if ((hooknum = find_hook (hookname, NULL, 1)) == INVALID_HOOKNUM)
2541 				{
2542 					RETURN_EMPTY;
2543 				}
2544 			}
2545 			hooks = &hook_functions[hooknum];
2546 			if (!input || !*input)
2547 				prop = 2;
2548 			else
2549 			{
2550 				GET_FUNC_ARG(str, input);
2551 				prop = vmy_strnicmp(strlen(str), str,
2552 					"FLAGS",		"MARK", 		"NAME",
2553 					"PARAMETRES", 	"PARAMETERS",	"PARAMS",
2554 					"IMPLIED",		NULL);
2555 			}
2556 
2557 			switch (prop)
2558 			{
2559 				case 1:		RETURN_INT(hooks->flags);
2560 				case 2:		RETURN_INT(hooks->mark);
2561 				case 3:		RETURN_STR(hooks->name);
2562 				case 4:
2563 				case 5:
2564 				case 6:		RETURN_INT(hooks->params);
2565 #ifdef IMPLIED_ON_HOOKS
2566 				case 7:		if (set) {
2567 								if (*input == '{')
2568 								{
2569 									ssize_t span;
2570 									span = MatchingBracket(input+1, '{', '}');
2571 									if (span >= 0) {
2572 										input[span + 1] = 0;
2573 										input++;
2574 									}
2575 									hooks->implied_protect = 1;
2576 								}
2577 								if (empty(input))
2578 									new_free(&hooks->implied);
2579 								else
2580 									malloc_strcpy(&hooks->implied, input);
2581 								RETURN_INT(1);
2582 							} else
2583 								RETURN_STR(hooks->implied);
2584 #endif
2585 			}
2586 			RETURN_EMPTY;
2587 			break;
2588 
2589 		/* end action-switch */
2590 		}
2591 		RETURN_EMPTY;
2592 		break;
2593 
2594 	/* go-switch */
2595 	case HOOKCTL_ADD:
2596 		if (!input || !*input)
2597 			RETURN_INT(-1);
2598 		GET_FUNC_ARG(hookname, input);
2599 		while (*hookname != '\0')
2600 		{
2601 			switch (*hookname)
2602 			{
2603 			case '#':
2604 				set_serial = 1;
2605 				hookname++;
2606 				continue;
2607 
2608 			case '!':
2609 				set_not = 1;
2610 				hookname++;
2611 				continue;
2612 
2613 			case '\'':
2614 				set_flex = 1;
2615 				hookname++;
2616 				continue;
2617 
2618 			default:
2619 				for (tmp_int = 0; tmp_int < noise_level_num; tmp_int++)
2620 				{
2621 					if (noise_info[tmp_int]->identifier != 0 &&
2622 						noise_info[tmp_int]->identifier == *hookname)
2623 						break;
2624 				}
2625 				if (tmp_int == noise_level_num)
2626 					break;
2627 				set_noisy = noise_info[tmp_int]->value;
2628 				hookname++;
2629 				continue;
2630 			}
2631 			break;
2632 		}
2633 		if ((hooknum = find_hook (hookname, NULL, 1)) == INVALID_HOOKNUM)
2634 			RETURN_EMPTY;
2635 		if (set_serial)
2636 		{
2637 			if (!input || !*input)
2638 				RETURN_INT(-1);
2639 			if (input[0] == '#')
2640 				input++;
2641 			GET_FUNC_ARG(tmp, input);
2642 			if (!strcmp(tmp, "-"))
2643 			{
2644 				serial = hook_find_free_serial(-1, 0, hooknum);
2645 			}
2646 			else if (!strcmp(tmp, "+"))
2647 			{
2648 				serial = hook_find_free_serial(1, 0, hooknum);
2649 			}
2650 			else
2651 			{
2652 				serial = atoi (tmp);
2653 			}
2654 		}
2655 		if (!input || !*input)
2656 			RETURN_INT(-1);
2657 		GET_FUNC_ARG(nick, input);
2658 		nick = malloc_strdup(nick);
2659 		tmp_int = add_hook (hooknum, nick, tmp_arglist, input, set_noisy, set_not, serial, set_flex);
2660 		new_free (&nick);
2661 		RETURN_INT(tmp_int);
2662 		break;
2663 
2664 	/* go-switch */
2665 	case HOOKCTL_REMOVE:
2666 		if (!input || !*input)
2667 			RETURN_INT(-1);
2668 		GET_INT_ARG(id, input);
2669 		if (id == -1 && current_hook != NULL)
2670 			id = current_hook -> userial;
2671 		if (hooklist_size <= id || id < 0 || hooklist[id] == NULL)
2672 			RETURN_INT(-1);
2673 		hook = hooklist[id];
2674 		remove_hook(hook->type, hook->nick, hook->sernum, 1);
2675 		RETURN_INT(1);
2676 		break;
2677 
2678 	/* go-switch */
2679 	case HOOKCTL_HALTCHAIN:
2680 		if (input && *input)
2681 		{
2682 			GET_INT_ARG(halt, input);
2683 		}
2684 		curhook = current_hook;
2685 		for (tmp_int = 0; curhook && tmp_int < halt; tmp_int++)
2686 			curhook = curhook->under;
2687 		if (!curhook)
2688 			RETURN_INT(0);
2689 		curhook->halt = 1;
2690 		RETURN_INT(1);
2691 		break;
2692 
2693 	/* go-switch */
2694 	case HOOKCTL_ARGS:
2695 		if (input && *input)
2696 		{
2697 			GET_INT_ARG(halt, input);
2698 		}
2699 		curhook = current_hook;
2700 		for (tmp_int = 0; curhook && tmp_int < halt; tmp_int++)
2701 			curhook = curhook->under;
2702 		if (!curhook)
2703 			RETURN_INT(0);
2704 		malloc_strcpy(&curhook->buffer, input);
2705 		curhook->buffer_changed = 1;
2706 		RETURN_INT(1);
2707 		break;
2708 
2709 	/* go-switch */
2710 	case HOOKCTL_USERINFO:
2711 		if (input && *input)
2712 		{
2713 			GET_INT_ARG(halt, input);
2714 		};
2715 		curhook = current_hook;
2716 		for (tmp_int = 0; curhook && tmp_int < halt; tmp_int++)
2717 			curhook = curhook->under;
2718 		if (!curhook)
2719 			RETURN_INT(0);
2720 		if (!input || !*input)
2721 		{
2722 			if (curhook->user_supplied_info == NULL)
2723 				RETURN_EMPTY;
2724 			else
2725 				RETURN_STR(curhook->user_supplied_info);
2726 		}
2727 		malloc_strcpy(&curhook->user_supplied_info, input);
2728 		RETURN_INT(1);
2729 		break;
2730 	/* go-switch */
2731 	case HOOKCTL_MATCH:
2732 		if (!input || !*input)
2733 			RETURN_EMPTY;
2734 		GET_FUNC_ARG(hookname, input);
2735 		if ((hooknum = find_hook (hookname, NULL, 1)) == INVALID_HOOKNUM)
2736 			RETURN_EMPTY;
2737 		if (!hook_functions[hooknum].list
2738 			|| (hook_functions[hooknum].mark
2739 				&& hook_functions[hooknum].flags & HF_NORECURSE))
2740 			RETURN_EMPTY;
2741 		buffer = malloc_strdup(!input || !*input ? "" : input);
2742 		for (sernum = INT_MIN;sernum < INT_MAX;sernum++)
2743 		{
2744 			for (hook = hook_functions[hooknum].list; hook; hook = hook->next)
2745 			{
2746 				Hook *besthook = NULL;
2747 				if (hook->sernum < sernum)
2748 					continue;
2749 
2750 				if (hook->sernum > sernum)
2751 					sernum = hook->sernum;
2752 
2753 				for (; hook && hook->sernum == sernum; hook = hook->next)
2754 				{
2755 					if (hook->flexible)
2756 					{
2757 						char *tmpnick;
2758 						tmpnick = expand_alias(hook->nick, empty_string);
2759 						currmatch = wild_match(tmpnick, buffer);
2760 						new_free(&tmpnick);
2761 					}
2762 					else
2763 						currmatch = wild_match(hook->nick, buffer);
2764 
2765 					if (currmatch > bestmatch)
2766 					{
2767 						besthook = hook;
2768 						bestmatch = currmatch;
2769 					}
2770 				}
2771 
2772 				if (!besthook || besthook->not)
2773 					break;
2774 
2775 				malloc_strcat_wordlist_c(&ret, space, ltoa(besthook->userial), &retlen);
2776 				break;
2777 			}
2778 			if (!hook)
2779 				break;
2780 		}
2781 		new_free (&buffer);
2782 		RETURN_MSTR(ret);
2783 		break;
2784 
2785 	/* go-switch */
2786 	default:
2787 		yell ("In function hookctl: unknown go: %d", go);
2788 		RETURN_EMPTY;
2789 	/* The function should never come to this point */
2790 
2791 	/* end go-switch */
2792 	}
2793 	RETURN_EMPTY;
2794 }
2795 
2796 #if 0
2797 void    help_topics_on (FILE *f)
2798 {
2799         int     x;
2800 
2801         for (x = 0; x < NUMBER_OF_LISTS; x++)
2802 		if (!isdigit(*hook_functions[x].name))
2803 			fprintf(f, "on %s\n", hook_functions[x].name);
2804 }
2805 #endif
2806 
2807