1 /*
2  * alias.c -- Handles the whole kit and caboodle for aliases.
3  *
4  * Copyright (c) 1990 Michael Sandroff.
5  * Copyright (c) 1991, 1992 Troy Rollo.
6  * Copyright (c) 1992-1996 Matthew Green.
7  * Copyright 1997, 2014 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 #define __need_cs_alist_hash__
37 #include "irc.h"
38 #define __need_ArgList_t__
39 #include "alias.h"
40 #include "alist.h"
41 #include "array.h"
42 #include "commands.h"
43 #include "files.h"
44 #include "hook.h"
45 #include "input.h"
46 #include "ircaux.h"
47 #include "output.h"
48 #include "screen.h"
49 #include "stack.h"
50 #include "status.h"
51 #include "vars.h"
52 #include "window.h"
53 #include "keys.h"
54 #include "functions.h"
55 #include "words.h"
56 #include "reg.h"
57 #include "vars.h"
58 #include "timer.h"
59 
60 #define LEFT_BRACE '{'
61 #define RIGHT_BRACE '}'
62 #define LEFT_BRACKET '['
63 #define RIGHT_BRACKET ']'
64 #define LEFT_PAREN '('
65 #define RIGHT_PAREN ')'
66 #define DOUBLE_QUOTE '"'
67 
68 /**************************** INTERMEDIATE INTERFACE *********************/
69 /* Just doing some changes for hooks */
70 /*
71 enum ARG_TYPES {
72 	WORD,
73 	UWORD,
74 	DWORD,
75 	QWORD
76 };
77 */
78 /* Ugh. XXX Bag on the side */
79 /*
80 struct ArgListT {
81 	char *	vars[32];
82 	char *	defaults[32];
83 	int	words[32];
84 	enum	ARG_TYPES types[32];
85 	int	void_flag;
86 	int	dot_flag;
87 };
88 typedef struct ArgListT ArgList;
89 ArgList	*parse_arglist (char *arglist);
90 void	destroy_arglist (ArgList **);
91 */
92 
93 /*
94  * This is the description of an alias entry
95  * This is an ``array_item'' structure
96  */
97 typedef	struct	SymbolStru
98 {
99 	char	*name;			/* name of alias */
100 	u_32int_t hash;			/* Hash of the name */
101 
102 	char *	user_variable;
103 	int	user_variable_stub;
104 	char *	user_variable_package;
105 
106 	char *	user_command;
107 	int	user_command_stub;
108 	char *	user_command_package;
109 	ArgList *arglist;		/* List of arguments to alias */
110 
111         void    (*builtin_command) (const char *, char *, const char *);
112 	char *	(*builtin_function) (char *);
113 	char *	(*builtin_expando) (void);
114 	IrcVariable *	builtin_variable;
115 
116 struct SymbolStru *	saved;		/* For stacks */
117 	int	saved_hint;
118 }	Symbol;
119 
120 #define SAVED_VAR		 1
121 #define SAVED_CMD		 2
122 #define SAVED_BUILTIN_CMD	 4
123 #define SAVED_BUILTIN_FUNCTION	 8
124 #define SAVED_BUILTIN_EXPANDO	16
125 #define SAVED_BUILTIN_VAR	32
126 
127 const char *symbol_types[] = {
128 	"ASSIGN",		"ALIAS",		"BUILTIN_COMMAND",
129 	"BUILTIN_FUNCTION",	"BUILTIN_EXPANDO",	"BUILTIN_VARIABLE",
130 	NULL
131 };
132 
133 /*
134  * This is the description for a list of aliases
135  * This is an ``array_set'' structure
136  */
137 #define ALIAS_CACHE_SIZE 4
138 
139 typedef struct	SymbolSetStru
140 {
141 	Symbol **	list;
142 	int		max;
143 	int		max_alloc;
144 	alist_func 	func;
145 	hash_type	hash;
146 }	SymbolSet;
147 
148 static SymbolSet globals = 	{ NULL, 0, 0, strncmp, HASH_INSENSITIVE };
149 
150 static	Symbol *lookup_symbol (const char *name);
151 static	Symbol *find_local_alias   (const char *name, SymbolSet **list);
152 
153 /*
154  * This is the ``stack frame''.  Each frame has a ``name'' which is
155  * the name of the alias or on of the frame, or is NULL if the frame
156  * is not an ``enclosing'' frame.  Each frame also has a ``current command''
157  * that is being executed, which is used to help us when the client crashes.
158  * Each stack also contains a list of local variables.
159  */
160 typedef struct RuntimeStackStru
161 {
162 	const char *name;	/* Name of the stack */
163 	char 	*current;	/* Current cmd being executed */
164 	SymbolSet alias;	/* Local variables */
165 	int	locked;		/* Are we locked in a wait? */
166 	int	parent;		/* Our parent stack frame */
167 }	RuntimeStack;
168 
169 /*
170  * This is the master stack frame.  Its size is saved in ``max_wind''
171  * and the current frame being used is stored in ``wind_index''.
172  */
173 static 	RuntimeStack *call_stack = NULL;
174 	int 	max_wind = -1;
175 	int 	wind_index = -1;
176 
177 
178 /*
179  * This is where we keep track of where the last pending function call.
180  * This is used when you assign to the FUNCTION_RETURN value.  Since it
181  * is neccesary to be able to access FUNCTION_RETURN in contexts where other
182  * local variables would not be visible, we do this as a quasi-neccesary
183  * hack.  When you reference FUNCTION_RETURN, it goes right to this stack.
184  */
185 	int	last_function_call_level = -1;
186 
187 /*
188  * The following actions are supported:  add, delete, find, list
189  * On the following types of data:	 var_alias, cmd_alias, local_alias
190  * Except you cannot list or delete local_aliases.
191  *
192  * To fetch a variable, use ``get_variable''
193  * To fetch an alias, use ``get_cmd_alias''
194  * To fetch an ambiguous alias, use ``glob_cmd_alias''
195  * To recurse an array structure, use ``get_subarray_elements''
196  */
197 
198 /*
199  * These are general purpose interface functions.
200  * However, the static ones should **always** be static!  If you are tempted
201  * to use them outside of alias.c, please rethink what youre trying to do.
202  * Using these functions violates the encapsulation of the interface.
203  * Specifically, if you create a global variable and then want to delete it,
204  * try using a local variable so it is reaped automatically.
205  */
206 extern	void    add_var_alias      (Char *name, Char *stuff, int noisy);
207 extern  void    add_local_alias    (Char *name, Char *stuff, int noisy);
208 extern  void    add_cmd_alias      (Char *name, ArgList *arglist, Char *stuff);
209 extern  void    add_var_stub_alias (Char *name, Char *stuff);
210 extern  void    add_cmd_stub_alias (Char *name, Char *stuff);
211 extern	void	add_builtin_cmd_alias  (Char *, void (*)(Char *, char *, Char *));
212 extern	void	add_builtin_func_alias (Char *, char *(*)(char *));
213 extern	void	add_builtin_expando    (Char *, char *(*)(void));
214 
215 static	void	delete_var_alias   (Char *name, int noisy);
216 static	void	delete_cmd_alias   (Char *name, int noisy);
217 /*	void	delete_local_alias (Char *name); 		*/
218 
219 static	void	unload_cmd_alias   (Char *fn);
220 static	void	unload_var_alias   (Char *fn);
221 static	void	list_cmd_alias     (Char *name);
222 static	void	list_var_alias     (Char *name);
223 static	void	list_local_alias   (Char *name);
224 static	void 	destroy_cmd_aliases    (SymbolSet *);
225 static	void 	destroy_var_aliases    (SymbolSet *);
226 static	void 	destroy_builtin_commands    (SymbolSet *);
227 static	void 	destroy_builtin_functions    (SymbolSet *);
228 static	void 	destroy_builtin_variables    (SymbolSet *);
229 static	void 	destroy_builtin_expandos    (SymbolSet *);
230 
231 extern	char *  get_variable       (Char *name);
232 extern	char ** glob_cmd_alias          (Char *name, int *howmany, int maxret, int start, int rev);
233 extern	char ** glob_assign_alias	(Char *name, int *howmany, int maxret, int start, int rev);
234 extern	const char *  get_cmd_alias     (Char *name, void **args,
235 					 void (**func) (const char *, char *,
236 					                const char *));
237 extern	char ** get_subarray_elements   (Char *root, int *howmany, int type);
238 
239 
240 static	char *	get_variable_with_args (Char *str, Char *args);
241 
flush_all_symbols(void)242 void	flush_all_symbols (void)
243 {
244 	int	i;
245 	Symbol *s;
246 
247 	for (i = 0; i < globals.max; i++)
248 	{
249 		s = globals.list[i];
250 		new_free(&s->name);
251 		new_free(&s->user_variable);
252 		new_free(&s->user_variable_package);
253 		new_free(&s->user_command);
254 		new_free(&s->user_command_package);
255 		destroy_arglist(&s->arglist);
256 		s->builtin_command = NULL;
257 		s->builtin_function = NULL;
258 		s->builtin_expando = NULL;
259 		if (s->builtin_variable)
260 		{
261 			if (s->builtin_variable->type == STR_VAR)
262 				new_free(&s->builtin_variable->data->string);
263 			new_free(&s->builtin_variable->data);
264 			new_free(&s->builtin_variable->script);
265 			new_free(&s->builtin_variable);
266 		}
267 		new_free(&globals.list[i]);
268 	}
269 	new_free(&globals.list);
270 }
271 
272 
273 /************************** HIGH LEVEL INTERFACE ***********************/
274 
275 /*
276  * User front end to the ALIAS command
277  * Syntax to debug alias:  ALIAS /S
278  * Syntax to add alias:    ALIAS name [{] irc command(s) [}]
279  * Syntax to delete alias: ALIAS -name
280  */
BUILT_IN_COMMAND(aliascmd)281 BUILT_IN_COMMAND(aliascmd)
282 {
283 	char *name;
284 	char *real_name;
285 	char *ptr;
286 
287 	/*
288 	 * If no name present, list all aliases
289 	 */
290 	if (!(name = next_arg(args, &args)))
291 	{
292 		list_cmd_alias(NULL);
293 		return;
294 	}
295 
296 	/*
297 	 * Alias can take an /s arg, which shows some data we've collected
298 	 */
299 	if (!my_strnicmp(name, "/S", 2))
300 	{
301 		say("Sorry, this EPIC does not have caches");
302 		return;
303 	}
304 
305 	/*
306 	 * Canonicalize the alias name
307 	 */
308 	real_name = remove_brackets(name, NULL);
309 
310 	/*
311 	 * Find the argument body
312 	 */
313 	while (my_isspace(*args))
314 		args++;
315 
316 	/*
317 	 * If there is no argument body, we probably want to delete alias
318 	 */
319 	if (!args || !*args)
320 	{
321 		/*
322 		 * If the alias name starts with a hyphen, then we are
323 		 * going to delete it.
324 		 */
325 		if (real_name[0] == '-')
326 		{
327 			if (real_name[1])
328 				delete_cmd_alias(real_name + 1, 1);
329 			else
330 				say("You must specify an alias to be removed.");
331 		}
332 
333 		/*
334 		 * Otherwise, the user wants us to list that alias
335 		 */
336 		else
337 			list_cmd_alias(real_name);
338 	}
339 
340 	/*
341 	 * If there is an argument body, then we have to register it
342 	 */
343 	else
344 	{
345 		ArgList *arglist = NULL;
346 
347 		/*
348 		 * Aliases may contain a parameter list, which is parsed
349 		 * at registration time.
350 		 */
351 		if (*args == LEFT_PAREN)
352 		{
353 		    ssize_t	span;
354 
355 		    args++;
356 		    if ((span = MatchingBracket(args, '(', ')')) < 0)
357 			say("Unmatched lparen in ALIAS %s", real_name);
358 		    else
359 		    {
360 			ptr = args + span;
361 			*ptr++ = 0;
362 			while (*ptr && my_isspace(*ptr))
363 				ptr++;
364 			if (!*ptr)
365 				say("Missing alias body in ALIAS %s",
366 					real_name);
367 
368 			while (*args && my_isspace(*args))
369 				args++;
370 			arglist = parse_arglist(args);
371 			args = ptr;
372 		    }
373 		}
374 
375 		/*
376 		 * Aliases' bodies can be surrounded by a set of braces,
377 		 * which are stripped off.
378 		 */
379 		if (*args == LEFT_BRACE)
380 		{
381 		    ssize_t	span;
382 
383 		    args++;
384 		    if ((span = MatchingBracket(args, '{', '}')) < 0)
385 			say("Unmatched brace in ALIAS %s", real_name);
386 		    else
387 		    {
388 			ptr = args + span;
389 			*ptr++ = 0;
390 			while (*ptr && my_isspace(*ptr))
391 				ptr++;
392 
393 			if (*ptr)
394 				say("Junk [%s] after closing brace in ALIAS %s",
395 					ptr, real_name);
396 
397 			while (*args && my_isspace(*args))
398 				args++;
399 		    }
400 		}
401 
402 		/*
403 		 * Register the alias
404 		 */
405 		add_cmd_alias(real_name, arglist, args);
406 	}
407 
408 	new_free(&real_name);
409 	return;
410 }
411 
412 /*
413  * User front end to the ASSIGN command
414  * Syntax to add variable:    ASSIGN name text
415  * Syntax to delete variable: ASSIGN -name
416  */
BUILT_IN_COMMAND(assigncmd)417 BUILT_IN_COMMAND(assigncmd)
418 {
419 	char *real_name;
420 	char *name;
421 
422 	/*
423 	 * If there are no arguments, list all the global variables
424 	 */
425 	if (!(name = next_arg(args, &args)))
426 	{
427 		list_var_alias(NULL);
428 		return;
429 	}
430 
431 	/*
432 	 * Canonicalize the variable name
433 	 */
434 	real_name = remove_brackets(name, NULL);
435 
436 	/*
437 	 * Find the stuff to assign to the variable
438 	 */
439 	while (my_isspace(*args))
440 		args++;
441 
442 	/*
443 	 * If there is no body, then the user probably wants to delete
444 	 * the variable
445 	 */
446 	if (!args || !*args)
447 	{
448 		/*
449 	 	 * If the variable name starts with a hyphen, then we remove
450 		 * the variable
451 		 */
452 		if (real_name[0] == '-')
453 		{
454 			if (real_name[1])
455 				delete_var_alias(real_name + 1, 1);
456 			else
457 				say("You must specify an alias to be removed.");
458 		}
459 
460 		/*
461 		 * Otherwise, the user wants us to list the variable
462 		 */
463 		else
464 			list_var_alias(real_name);
465 	}
466 
467 	/*
468 	 * Register the variable
469 	 */
470 	else
471 		add_var_alias(real_name, args, 1);
472 
473 	new_free(&real_name);
474 	return;
475 }
476 
477 /*
478  * User front end to the STUB command
479  * Syntax to stub an alias to a file:	STUB ALIAS name[,name] filename(s)
480  * Syntax to stub a variable to a file:	STUB ASSIGN name[,name] filename(s)
481  */
BUILT_IN_COMMAND(stubcmd)482 BUILT_IN_COMMAND(stubcmd)
483 {
484 	int 	type;
485 	char 	*cmd;
486 	char 	*name;
487 const 	char 	*usage = "Usage: STUB (alias|assign) <name> <file> [<file> ...]";
488 
489 	/*
490 	 * The first argument is the type of stub to make
491 	 * (alias or assign)
492 	 */
493 	if (!(cmd = upper(next_arg(args, &args))))
494 	{
495 		my_error("Missing stub type");
496 		say("%s", usage);
497 		return;
498 	}
499 
500 	if (!strncmp(cmd, "ALIAS", strlen(cmd)))
501 		type = COMMAND_ALIAS;
502 	else if (!strncmp(cmd, "ASSIGN", strlen(cmd)))
503 		type = VAR_ALIAS;
504 	else
505 	{
506 		my_error("[%s] is an Unrecognized stub type", cmd);
507 		say("%s", usage);
508 		return;
509 	}
510 
511 	/*
512 	 * The next argument is the name of the item to be stubbed.
513 	 * This is not optional.
514 	 */
515 	if (!(name = next_arg(args, &args)))
516 	{
517 		my_error("Missing alias name");
518 		say("%s", usage);
519 		return;
520 	}
521 
522 	/*
523 	 * Find the filename argument
524 	 */
525 	while (my_isspace(*args))
526 		args++;
527 
528 	/*
529 	 * The rest of the argument(s) are the files to load when the
530 	 * item is referenced.  This is not optional.
531 	 */
532 	if (!args || !*args)
533 	{
534 		my_error("Missing file name");
535 		say("%s", usage);
536 		return;
537 	}
538 
539 	/*
540 	 * Now we iterate over the item names we were given.  For each
541 	 * item name, seperated from the next by a comma, stub that item
542 	 * to the given filename(s) specified as the arguments.
543 	 */
544 	while (name && *name)
545 	{
546 		char 	*next_name;
547 		char	*real_name;
548 
549 		if ((next_name = strchr(name, ',')))
550 			*next_name++ = 0;
551 
552 		real_name = remove_brackets(name, NULL);
553 		if (type == COMMAND_ALIAS)
554 			add_cmd_stub_alias(real_name, args);
555 		else
556 			add_var_stub_alias(real_name, args);
557 
558 		new_free(&real_name);
559 		name = next_name;
560 	}
561 }
562 
BUILT_IN_COMMAND(localcmd)563 BUILT_IN_COMMAND(localcmd)
564 {
565 	char *name;
566 
567 	if (!(name = next_arg(args, &args)))
568 	{
569 		list_local_alias(NULL);
570 		return;
571 	}
572 
573 	while (args && *args && my_isspace(*args))
574 		args++;
575 
576 	if (!args)
577 		args = LOCAL_COPY(empty_string);
578 
579 	if (!my_strnicmp(name, "-dump", 2))	/* Illegal name anyways */
580 	{
581 		destroy_var_aliases(&call_stack[wind_index].alias);
582 		return;
583 	}
584 
585 	while (name && *name)
586 	{
587 		char 	*next_name;
588 		char	*real_name;
589 
590 		if ((next_name = strchr(name, ',')))
591 			*next_name++ = 0;
592 
593 		real_name = remove_brackets(name, NULL);
594 		add_local_alias(real_name, args, 1);
595 		new_free(&real_name);
596 		name = next_name;
597 	}
598 }
599 
BUILT_IN_COMMAND(dumpcmd)600 BUILT_IN_COMMAND(dumpcmd)
601 {
602 	const char 	*blah = empty_string;
603 	int 	all = 0;
604 	int 	dumped = 0;
605 
606 	upper(args);
607 
608 	if (!args || !*args || !strncmp(args, "ALL", 3))
609 		all = 1;
610 
611 	while (all || (blah = next_arg(args, &args)))
612 	{
613 		dumped = 0;
614 
615 		if (all || !strncmp(blah, "VAR", strlen(blah)))
616 		{
617 			say("Dumping your global variables");
618 			destroy_var_aliases(&globals);
619 			dumped++;
620 		}
621 		if (all || !strncmp(blah, "ALIAS", strlen(blah)))
622 		{
623 			say("Dumping your global aliases");
624 			destroy_cmd_aliases(&globals);
625 			dumped++;
626 		}
627 		if (all || !strncmp(blah, "ON", strlen(blah)))
628 		{
629 			say("Dumping your ONs");
630 			flush_on_hooks();
631 			dumped++;
632 		}
633 
634 		if (!dumped)
635 			say("Dump what? ('%s' is unrecognized)", blah);
636 
637 		if (all)
638 			break;
639 	}
640 }
641 
BUILT_IN_COMMAND(unloadcmd)642 BUILT_IN_COMMAND(unloadcmd)
643 {
644 	char *filename;
645 
646 	if (!(filename = next_arg(args, &args)))
647 		my_error("You must supply a filename for /UNLOAD.");
648 	else
649 	{
650 		do_hook(UNLOAD_LIST, "%s", filename);
651 		say("Removing aliases from %s ...", filename);
652 		unload_cmd_alias(filename);
653 		say("Removing assigns from %s ...", filename);
654 		unload_var_alias(filename);
655 		say("Removing hooks from %s ...", filename);
656 		unload_on_hooks(filename);
657 		say("Removing keybinds from %s ...", filename);
658 		unload_bindings(filename);
659 		say("Removing timers from %s ...", filename);
660 		unload_timers(filename);
661 		say("Done.");
662 	}
663 }
664 
665 
666 /*
667  * Argument lists look like this:
668  *
669  * LIST   := LPAREN TERM [COMMA TERM] RPAREN)
670  * LPAREN := '('
671  * RPAREN := ')'
672  * COMMA  := ','
673  * TERM   := LVAL QUAL | "..." | "void"
674  * LVAL   := <An alias name>
675  * QUAL   := NUM "words"
676  *
677  * In English:
678  *   An argument list is a comma seperated list of variable descriptors (TERM)
679  *   enclosed in a parenthesis set.  Each variable descriptor may contain any
680  *   valid alias name followed by an action qualifier, or may be the "..."
681  *   literal string, or may be the "void" literal string.  If a variable
682  *   descriptor is an alias name, then that positional argument will be removed
683  *   from the argument list and assigned to that variable.  If the qualifier
684  *   specifies how many words are to be taken, then that many are taken.
685  *   If the variable descriptor is the literal string "...", then all argument
686  *   list parsing stops there and all remaining alias arguments are placed
687  *   into the $* special variable.  If the variable descriptor is the literal
688  *   string "void", then the balance of the remaining alias arguments are
689  *   discarded and $* will expand to the false value.  If neither "..." nor
690  *   "void" are provided in the argument list, then that last variable in the
691  *   list will recieve all of the remaining arguments left at its position.
692  *
693  * Examples:
694  *
695  *   # This example puts $0 in $channel, $1 in $mag, and $2- in $nicklist.
696  *   /alias opalot (channel, mag, nicklist) {
697  *	fe ($nicklist) xx yy zz {
698  *	    mode $channel ${mag}ooo $xx $yy $zz
699  *	}
700  *   }
701  *
702  *   # This example puts $0 in $channel, $1 in $mag, and the new $* is old $2-
703  *   /alias opalot (channel, mag, ...) {
704  *	fe ($*) xx yy zz {
705  *	    mode $channel ${mag}ooo $xx $yy $zz
706  *	}
707  *   }
708  *
709  *   # This example throws away any arguments passed to this alias
710  *   /alias booya (void) { echo Booya! }
711  */
parse_arglist(char * arglist)712 ArgList	*parse_arglist (char *arglist)
713 {
714 	char *	this_term;
715 	char *	next_term;
716 	char *	varname;
717 	char *	modifier, *value;
718 	int	arg_count = 0;
719 	ArgList *args = new_malloc(sizeof(ArgList));
720 
721 	if (arglist == NULL)
722 		panic(1, "parse_arglist: arglist is NULL and it shouldn't be.");
723 
724 	args->void_flag = args->dot_flag = 0;
725 	for (this_term = arglist; *this_term; this_term = next_term)
726 	{
727 		while (isspace(*this_term))
728 			this_term++;
729 		next_in_comma_list(this_term, &next_term);
730 		if (!(varname = next_arg(this_term, &this_term)))
731 			continue;
732 
733 		args->types[arg_count] = WORD;
734 		if (!my_stricmp(varname, "void")) {
735 			args->void_flag = 1;
736 			break;
737 		} else if (!my_stricmp(varname, "...")) {
738 			args->dot_flag = 1;
739 			break;
740 		} else {
741 			args->vars[arg_count] = malloc_strdup(varname);
742 			args->defaults[arg_count] = NULL;
743 			args->words[arg_count] = 1;
744 
745 			while ((modifier = next_arg(this_term, &this_term)))
746 			{
747 				if (!(value = new_next_arg(this_term, &this_term)))
748 						break;
749 				if (!my_stricmp(modifier, "default"))
750 				{
751 					args->defaults[arg_count] = malloc_strdup(value);
752 				}
753 				else if (!my_stricmp(modifier, "words"))
754 				{
755 					args->types[arg_count] = WORD;
756 					args->words[arg_count] = atol(value);
757 				}
758 				else if (!my_stricmp(modifier, "uwords"))
759 				{
760 					args->types[arg_count] = UWORD;
761 					args->words[arg_count] = atol(value);
762 				}
763 				else if (!my_stricmp(modifier, "qwords"))
764 				{
765 					args->types[arg_count] = QWORD;
766 					args->words[arg_count] = atol(value);
767 				}
768 				else if (!my_stricmp(modifier, "dwords"))
769 				{
770 					args->types[arg_count] = DWORD;
771 					args->words[arg_count] = atol(value);
772 				}
773 				else
774 				{
775 					yell("Bad arglist parameter modifier %s -- did you forget to put a comma before this variable names?", modifier);
776 				}
777 			}
778 			arg_count++;
779 		}
780 	}
781 	args->vars[arg_count] = NULL;
782 	return args;
783 }
784 
destroy_arglist(ArgList ** arglist)785 void	destroy_arglist (ArgList **arglist)
786 {
787 	int	i = 0;
788 
789 	if (!arglist || !*arglist)
790 		return;
791 
792 	for (i = 0; ; i++)
793 	{
794 		if (!(*arglist)->vars[i])
795 			break;
796 		new_free(&(*arglist)->vars[i]);
797 		new_free(&(*arglist)->defaults[i]);
798 	}
799 	new_free((char **)arglist);
800 }
801 
802 /* static ArgList *clone_arglist (ArgList *orig) */
clone_arglist(ArgList * orig)803 ArgList *clone_arglist (ArgList *orig)
804 {
805 	ArgList *args;
806 	int	i = 0;
807 
808 	if (!orig)
809 		return NULL;
810 
811 	args = new_malloc(sizeof(ArgList));
812 	for (i = 0; i < 32; i++)
813 	{
814 		args->vars[i] = NULL;
815 		args->defaults[i] = NULL;
816 		args->words[i] = 0;
817 		args->types[i] = WORD;
818 	}
819 	for (i = 0; ; i++)
820 	{
821 		if (!orig->vars[i])
822 			break;
823 		malloc_strcpy(&args->vars[i], orig->vars[i]);
824 		malloc_strcpy(&args->defaults[i], orig->defaults[i]);
825 		args->words[i] = orig->words[i];
826 		args->types[i] = orig->types[i];
827 	}
828 	args->void_flag = orig->void_flag;
829 	args->dot_flag = orig->dot_flag;
830 
831 	return args;
832 }
833 
prepare_alias_call(void * al,char ** stuff)834 void	prepare_alias_call (void *al, char **stuff)
835 {
836 	ArgList *args = (ArgList *)al;
837 	int	i;
838 
839 	if (!args)
840 		return;
841 
842 	for (i = 0; args->vars[i]; i++)
843 	{
844 		char	*next_val;
845 		char	*expanded = NULL;
846 		int	type = 0, do_dequote_it;
847 
848 		switch (args->types[i])
849 		{
850 			case WORD:
851 				if (!(x_debug & DEBUG_DWORD))
852 				{
853 					type = DWORD_NO;
854 					do_dequote_it = 0;
855 				}
856 				else if (args->words[i] <= 1)
857 				{
858 					type = DWORD_DWORDS;
859 					do_dequote_it = 1;
860 				}
861 				else
862 				{
863 					type = DWORD_YES;
864 					do_dequote_it = 0;
865 				}
866 				break;
867 			case UWORD:
868 				type = DWORD_NO;
869 				do_dequote_it = 0;
870 				break;
871 			case DWORD:
872 				type = DWORD_YES;
873 				do_dequote_it = 1;
874 				break;
875 			case QWORD:
876 				type = DWORD_YES;
877 				do_dequote_it = 0;
878 				break;
879 			default:
880 				panic(1, "Alias list argument [%d] has unsupported typed [%d]", i, args->types[i]);
881 				/* NOTREACHED */
882 				return;
883 		}
884 
885 		/* Last argument on the list and no ... argument? */
886 		if (!args->vars[i + 1] && !args->dot_flag && !args->void_flag)
887 		{
888 			next_val = *stuff;
889 			*stuff = endstr(*stuff);
890 		}
891 
892 		/* Yank the next word from the arglist */
893 		else
894 		{
895 			next_val = universal_next_arg_count(*stuff, stuff, args->words[i], type, 0, "\"");
896 		}
897 
898 		if (!next_val || !*next_val)
899 		{
900 			if ((next_val = args->defaults[i]))
901 				next_val = expanded = expand_alias(next_val, *stuff);
902 			else
903 				next_val = LOCAL_COPY(empty_string);
904 		}
905 
906 		/* Do dequoting last so it's useful for ``defaults'' */
907 		if (next_val && *next_val && do_dequote_it == 1)
908 		{
909 			size_t clue;
910 			clue = strlen(next_val);
911 			dequoter(&next_val, &clue, 1, type, "\"");
912 		}
913 
914 		/* Add the local variable */
915 		add_local_alias(args->vars[i], next_val, 0);
916 		if (expanded)
917 			new_free(&expanded);
918 	}
919 
920 	/* Throw away rest of args if wanted */
921 	if (args->void_flag)
922 		*stuff = endstr(*stuff);
923 }
924 
925 /* static char *	print_arglist (ArgList *args) */
print_arglist(ArgList * args)926 char *	print_arglist (ArgList *args)
927 {
928 	char *	retval = NULL;
929 	size_t	cluep = 0;
930 	int	i;
931 
932 	if (!args)
933 		return NULL;
934 
935 	for (i = 0; args->vars[i]; i++)
936 	{
937 	    if (i > 0)
938 		malloc_strcat_c(&retval, ", ", &cluep);
939 
940 	    malloc_strcat_c(&retval, args->vars[i], &cluep);
941 
942 	    switch (args->types[i])
943 	    {
944 		case WORD:
945 		    malloc_strcat_c(&retval, " words ", &cluep);
946 		    break;
947 		case UWORD:
948 		    malloc_strcat_c(&retval, " uwords ", &cluep);
949 		    break;
950 		case QWORD:
951 		    malloc_strcat_c(&retval, " qwords ", &cluep);
952 		    break;
953 		case DWORD:
954 		    malloc_strcat_c(&retval, " dwords ", &cluep);
955 		    break;
956 	    }
957 	    malloc_strcat_c(&retval, ltoa(args->words[i]), &cluep);
958 
959 	    if (args->defaults[i])
960 	    {
961 		malloc_strcat_c(&retval, " default ", &cluep);
962 		malloc_strcat_c(&retval, args->defaults[i], &cluep);
963 	    }
964 	}
965 
966 	if (args->void_flag)
967 	{
968 	   if (i > 0)
969 	       malloc_strcat_c(&retval, ", ", &cluep);
970 	   malloc_strcat_c(&retval, "void", &cluep);
971 	}
972 	else if (args->dot_flag)
973 	{
974 	   if (i > 0)
975 	       malloc_strcat_c(&retval, ", ", &cluep);
976 	    malloc_strcat_c(&retval, "...", &cluep);
977 	}
978 
979 	return retval;
980 }
981 
982 
983 #undef ew_next_arg
984 /***************************************************************************/
985 
make_new_Symbol(const char * name)986 static Symbol *make_new_Symbol (const char *name)
987 {
988 	Symbol *tmp = (Symbol *) new_malloc(sizeof(Symbol));
989 	tmp->name = malloc_strdup(name);
990 
991 	tmp->user_variable = NULL;
992 	tmp->user_variable_stub = 0;
993 	tmp->user_variable_package = NULL;
994 
995 	tmp->user_command = NULL;
996 	tmp->user_command_stub = 0;
997 	tmp->user_command_package = NULL;
998 	tmp->arglist = NULL;
999 
1000 	tmp->builtin_command = NULL;
1001 	tmp->builtin_function = NULL;
1002 	tmp->builtin_expando = NULL;
1003 	tmp->builtin_variable = NULL;
1004 
1005 	tmp->saved = NULL;
1006 	tmp->saved_hint = 0;
1007 	return tmp;
1008 }
1009 
1010 /*
1011  * Purge a symbol if it is empty.
1012  * A symbol is "empty" IFF
1013  *   - It has no data for any valid symbol type
1014  *   - It does not have any /stack push'ed data below it
1015  * UNLESS
1016  *   - It is a standalone item (list == NULL)
1017  *
1018  * This function returns 1 if this symbol is empty, and it was removed
1019  * from list (if appropriate), and it was free()d; no further use of
1020  * 'item' may be used after this function returns 1.
1021  *
1022  * This function returns 0 if the symbol is not empty.  The symbol will
1023  * not have been changed in any way.
1024  *
1025  * IF this is a standalone item, then the symbol can be considered empty
1026  * even if it has /stack push'd data below it.  A standalone item will
1027  * still be GC'd, even if it has stacked data below it!  You *must* save a
1028  * pointer to 'item->saved' before calling this function on a standalone
1029  * item and you *must* do something with that pointer if this function
1030  * returns 1!  (If you don't, you'll leak your stacked data!)
1031  */
GC_symbol(Symbol * item,array * list,int loc)1032 static int	GC_symbol (Symbol *item, array *list, int loc)
1033 {
1034 	if (item->user_variable)
1035 		return 0;
1036 	if (item->user_command)
1037 		return 0;
1038 	if (item->builtin_command)
1039 		return 0;
1040 	if (item->builtin_function)
1041 		return 0;
1042 	if (item->builtin_expando)
1043 		return 0;
1044 	if (item->builtin_variable)
1045 		return 0;
1046 	if (item->saved && list != NULL)
1047 		return 0;
1048 
1049 	if (list && loc >= 0)
1050 		array_pop(list, loc);
1051 
1052 	new_free(&item->user_variable_package);
1053 	new_free(&item->user_command_package);
1054 	destroy_arglist(&item->arglist);
1055 	new_free(&(item->name));
1056 	new_free((char **)&item);
1057 	return 1;
1058 }
1059 
1060 /* * * */
1061 /*
1062  * add_var_alias: Add a global variable
1063  *
1064  * name -- name of the alias to create (must be canonicalized)
1065  * stuff -- what to have ``name'' expand to.
1066  *
1067  * If ``name'' is FUNCTION_RETURN, then it will create the local
1068  * return value (die die die)
1069  *
1070  * If ``name'' refers to an already created local variable, that
1071  * local variable is used (invisibly)
1072  */
add_var_alias(const char * orig_name,const char * stuff,int noisy)1073 void	add_var_alias	(const char *orig_name, const char *stuff, int noisy)
1074 {
1075 	const char 	*ptr;
1076 	Symbol 	*tmp = NULL;
1077 	int	local = 0;
1078 	char	*save, *name;
1079 
1080 	save = name = remove_brackets(orig_name, NULL);
1081 	if (*name == ':')
1082 	{
1083 		name++, local = 1;
1084 		if (*name == ':')
1085 			name++, local = -1;
1086 	}
1087 
1088 	/*
1089 	 * Weed out invalid variable names
1090 	 */
1091 	ptr = after_expando(name, 1, NULL);
1092 	if (*ptr)
1093 		my_error("ASSIGN names may not contain '%c' (You asked for [%s])", *ptr, name);
1094 
1095 	/*
1096 	 * Weed out FUNCTION_RETURN (die die die)
1097 	 */
1098 	else if (!strcmp(name, "FUNCTION_RETURN"))
1099 		add_local_alias(name, stuff, noisy);
1100 
1101 	/*
1102 	 * Pass the buck on local variables
1103 	 */
1104 	else if ((local == 1) || (local == 0 && find_local_alias(name, NULL)))
1105 		add_local_alias(name, stuff, noisy);
1106 
1107 	else if (stuff && *stuff)
1108 	{
1109 		int cnt, loc;
1110 
1111 		/*
1112 		 * Look to see if the given alias already exists.
1113 		 * If it does, and the ``stuff'' to assign to it is
1114 		 * empty, then we should remove the variable outright
1115 		 */
1116 		tmp = (Symbol *) find_array_item ((array *)&globals, name, &cnt, &loc);
1117 		if (!tmp || cnt >= 0)
1118 		{
1119 			tmp = make_new_Symbol(name);
1120 			add_to_array ((array *)&globals, (array_item *)tmp);
1121 		}
1122 
1123 		if (current_package())
1124 		{
1125 		   if (!tmp->user_variable_package)
1126 			tmp->user_variable_package = malloc_strdup(current_package());
1127 		   else if (strcmp(tmp->user_variable_package, current_package()))
1128 			malloc_strcpy(&(tmp->user_variable_package), current_package());
1129 		}
1130 
1131 		malloc_strcpy(&(tmp->user_variable), stuff);
1132 		tmp->user_variable_stub = 0;
1133 
1134 
1135 		/*
1136 		 * And tell the user.
1137 		 */
1138 		if (noisy)
1139 			say("Assign %s added [%s]", name, stuff);
1140 	}
1141 	else
1142 		delete_var_alias(name, noisy);
1143 
1144 	new_free(&save);
1145 	return;
1146 }
1147 
add_var_stub_alias(const char * orig_name,const char * stuff)1148 void	add_var_stub_alias  (const char *orig_name, const char *stuff)
1149 {
1150 	Symbol *tmp = NULL;
1151 	const char *ptr;
1152 	char *name;
1153 
1154 	name = remove_brackets(orig_name, NULL);
1155 
1156 	ptr = after_expando(name, 1, NULL);
1157 	if (*ptr)
1158 	{
1159 		my_error("Assign names may not contain '%c' (You asked for [%s])", *ptr, name);
1160 		new_free(&name);
1161 		return;
1162 	}
1163 	else if (!strcmp(name, "FUNCTION_RETURN"))
1164 	{
1165 		my_error("You may not stub the FUNCTION_RETURN variable.");
1166 		new_free(&name);
1167 		return;
1168 	}
1169 
1170 
1171 	if (!(tmp = lookup_symbol(name)))
1172 	{
1173 		tmp = make_new_Symbol(name);
1174 		if (current_package())
1175 		    tmp->user_variable_package = malloc_strdup(current_package());
1176 		add_to_array ((array *)&globals, (array_item *)tmp);
1177 	}
1178 	else if (current_package())
1179 	{
1180 	    if (!tmp->user_variable_package ||
1181 			strcmp(tmp->user_variable_package, current_package()))
1182 		 malloc_strcpy(&tmp->user_variable_package, current_package());
1183 	}
1184 
1185 	malloc_strcpy(&(tmp->user_variable), stuff);
1186 	tmp->user_variable_stub = 1;
1187 
1188 	say("Assign %s stubbed to file %s", name, stuff);
1189 	new_free(&name);
1190 	return;
1191 }
1192 
add_local_alias(const char * orig_name,const char * stuff,int noisy)1193 void	add_local_alias	(const char *orig_name, const char *stuff, int noisy)
1194 {
1195 	const char 	*ptr;
1196 	Symbol 	*tmp = NULL;
1197 	SymbolSet *list = NULL;
1198 	char *	name;
1199 
1200 	name = remove_brackets(orig_name, NULL);
1201 
1202 	/*
1203 	 * Weed out invalid variable names
1204 	 */
1205 	ptr = after_expando(name, 1, NULL);
1206 	if (*ptr)
1207 	{
1208 		my_error("LOCAL names may not contain '%c' (You asked for [%s])",
1209 						*ptr, name);
1210 		new_free(&name);
1211 		return;
1212 	}
1213 
1214 	/*
1215 	 * Now we see if this local variable exists anywhere
1216 	 * within our view.  If it is, we dont care where.
1217 	 * If it doesnt, then we add it to the current frame,
1218 	 * where it will be reaped later.
1219 	 */
1220 	if (!(tmp = find_local_alias (name, &list)))
1221 	{
1222 		tmp = make_new_Symbol(name);
1223 		add_to_array ((array *)list, (array_item *)tmp);
1224 	}
1225 
1226 	/* Fill in the interesting stuff */
1227 	malloc_strcpy(&(tmp->user_variable), stuff);
1228 	if (tmp->user_variable)		/* Oh blah. */
1229 	{
1230 		if (x_debug & DEBUG_LOCAL_VARS && noisy)
1231 		    yell("Assign %s (local) added [%s]", name, stuff);
1232 		else if (noisy)
1233 		    say("Assign %s (local) added [%s]", name, stuff);
1234 	}
1235 
1236 	new_free(&name);
1237 	return;
1238 }
1239 
1240 /* * * */
add_cmd_alias(const char * orig_name,ArgList * arglist,const char * stuff)1241 void	add_cmd_alias	(const char *orig_name, ArgList *arglist, const char *stuff)
1242 {
1243 	Symbol *tmp = NULL;
1244 	char *name;
1245 	char *argstr;
1246 
1247 	name = remove_brackets(orig_name, NULL);
1248 	if (!(tmp = lookup_symbol(name)))
1249 	{
1250 		tmp = make_new_Symbol(name);
1251 		if (current_package())
1252 		   tmp->user_command_package = malloc_strdup(current_package());
1253 		add_to_array ((array *)&globals, (array_item *)tmp);
1254 	}
1255 	else if (current_package())
1256 	{
1257 	    if (tmp->user_command_package == NULL ||
1258 			strcmp(tmp->user_command_package, current_package()))
1259 		malloc_strcpy(&tmp->user_command_package, current_package());
1260 	}
1261 
1262 	malloc_strcpy(&(tmp->user_command), stuff);
1263 	tmp->user_command_stub = 0;
1264 	destroy_arglist(&tmp->arglist);
1265 	tmp->arglist = arglist;
1266 
1267 	argstr = print_arglist(arglist);
1268 	if (argstr)
1269 	{
1270 	    say("Alias	%s (%s) added [%s]", name, argstr, stuff);
1271 	    new_free(&argstr);
1272 	}
1273 	else
1274 	    say("Alias	%s added [%s]", name, stuff);
1275 
1276 	new_free(&name);
1277 	return;
1278 }
1279 
1280 
add_cmd_stub_alias(const char * orig_name,const char * stuff)1281 void	add_cmd_stub_alias  (const char *orig_name, const char *stuff)
1282 {
1283 	Symbol *tmp = NULL;
1284 	char *name;
1285 
1286 	name = remove_brackets(orig_name, NULL);
1287 	if (!(tmp = lookup_symbol(name)))
1288 	{
1289 		tmp = make_new_Symbol(name);
1290 		if (current_package())
1291 		   tmp->user_command_package = malloc_strdup(current_package());
1292 		add_to_array ((array *)&globals, (array_item *)tmp);
1293 	}
1294 	else if (current_package())
1295 	{
1296 		if (tmp->user_command_package == NULL ||
1297 			strcmp(tmp->user_command_package, current_package()))
1298 		malloc_strcpy(&(tmp->user_command_package), current_package());
1299 	}
1300 
1301 	malloc_strcpy(&(tmp->user_command), stuff);
1302 	tmp->user_command_stub = 1;
1303 
1304 	say("Alias %s stubbed to file %s", name, stuff);
1305 
1306 	new_free(&name);
1307 	return;
1308 }
1309 
add_builtin_cmd_alias(const char * name,void (* func)(const char *,char *,const char *))1310 void	add_builtin_cmd_alias	(const char *name, void (*func) (const char *, char *, const char *))
1311 {
1312 	Symbol *tmp = NULL;
1313 	int cnt, loc;
1314 
1315 	tmp = (Symbol *) find_array_item ((array *)&globals, name, &cnt, &loc);
1316 	if (!tmp || cnt >= 0)
1317 	{
1318 		tmp = make_new_Symbol(name);
1319 		add_to_array ((array *)&globals, (array_item *)tmp);
1320 	}
1321 
1322 	tmp->builtin_command = func;
1323 	return;
1324 }
1325 
add_builtin_func_alias(const char * name,char * (* func)(char *))1326 void	add_builtin_func_alias	(const char *name, char * (*func) (char *))
1327 {
1328 	Symbol *tmp = NULL;
1329 	int cnt, loc;
1330 
1331 	tmp = (Symbol *) find_array_item ((array *)&globals, name, &cnt, &loc);
1332 	if (!tmp || cnt >= 0)
1333 	{
1334 		tmp = make_new_Symbol(name);
1335 		add_to_array ((array *)&globals, (array_item *)tmp);
1336 	}
1337 
1338 	tmp->builtin_function = func;
1339 	return;
1340 }
1341 
add_builtin_expando(const char * name,char * (* func)(void))1342 void	add_builtin_expando	(const char *name, char *(*func) (void))
1343 {
1344 	Symbol *tmp = NULL;
1345 	int cnt, loc;
1346 
1347 	tmp = (Symbol *) find_array_item ((array *)&globals, name, &cnt, &loc);
1348 	if (!tmp || cnt >= 0)
1349 	{
1350 		tmp = make_new_Symbol(name);
1351 		add_to_array ((array *)&globals, (array_item *)tmp);
1352 	}
1353 
1354 	tmp->builtin_expando = func;
1355 	return;
1356 }
1357 
add_builtin_variable_alias(const char * name,IrcVariable * var)1358 void	add_builtin_variable_alias (const char *name, IrcVariable *var)
1359 {
1360 	Symbol *tmp = NULL;
1361 	int cnt, loc;
1362 
1363 	tmp = (Symbol *) find_array_item ((array *)&globals, name, &cnt, &loc);
1364 	if (!tmp || cnt >= 0)
1365 	{
1366 		tmp = make_new_Symbol(name);
1367 		add_to_array((array *)&globals, (array_item *)tmp);
1368 	}
1369 
1370 	tmp->builtin_variable = var;
1371 	return;
1372 }
1373 
1374 
1375 
1376 /************************ LOW LEVEL INTERFACE *************************/
1377 
1378 static	int	unstub_in_progress = 0;
1379 
unstub_variable(Symbol * item)1380 static Symbol *unstub_variable (Symbol *item)
1381 {
1382 	char *name;
1383 	char *file;
1384 
1385 	if (!item)
1386 		return NULL;
1387 
1388 	name = LOCAL_COPY(item->name);
1389 	if (item->user_variable_stub)
1390 	{
1391 		file = LOCAL_COPY(item->user_variable);
1392 		delete_var_alias(item->name, 0);
1393 
1394 		if (!unstub_in_progress)
1395 		{
1396 			unstub_in_progress = 1;
1397 			load("LOAD", file, empty_string);
1398 			unstub_in_progress = 0;
1399 		}
1400 
1401 		/* Look the name up again */
1402 		item = lookup_symbol(name);
1403 	}
1404 	return item;
1405 }
1406 
unstub_command(Symbol * item)1407 static Symbol *unstub_command (Symbol *item)
1408 {
1409 	char *name;
1410 	char *file;
1411 
1412 	if (!item)
1413 		return NULL;
1414 
1415 	name = LOCAL_COPY(item->name);
1416 	if (item->user_command_stub)
1417 	{
1418 		file = LOCAL_COPY(item->user_command);
1419 		delete_cmd_alias(item->name, 0);
1420 
1421 		if (!unstub_in_progress)
1422 		{
1423 			unstub_in_progress = 1;
1424 			load("LOAD", file, empty_string);
1425 			unstub_in_progress = 0;
1426 		}
1427 
1428 		/* Look the name up again */
1429 		item = lookup_symbol(name);
1430 	}
1431 	return item;
1432 }
1433 
1434 
1435 /*
1436  * 'name' is expected to already be in canonical form (uppercase, dot notation)
1437  */
lookup_symbol(const char * name)1438 static Symbol *	lookup_symbol (const char *name)
1439 {
1440 	Symbol *	item = NULL;
1441 	int 	loc;
1442 	int 	cnt = 0;
1443 
1444 #if 0
1445 	name += strspn(name, ":");		/* Accept ::global */
1446 #endif
1447 
1448 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
1449 	if (cnt >= 0)
1450 		item = NULL;
1451 	if (item && item->user_variable_stub)
1452 		item = unstub_variable(item);
1453 	if (item && item->user_command_stub)
1454 		item = unstub_command(item);
1455 	return item;
1456 }
1457 
1458 /*
1459  * An example will best describe the semantics:
1460  *
1461  * A local variable will be returned if and only if there is already a
1462  * variable that is exactly ``name'', or if there is a variable that
1463  * is an exact leading subset of ``name'' and that variable ends in a
1464  * period (a dot).
1465  */
find_local_alias(const char * orig_name,SymbolSet ** list)1466 static Symbol *	find_local_alias (const char *orig_name, SymbolSet **list)
1467 {
1468 	Symbol 	*alias = NULL;
1469 	int 	c;
1470 	const char 	*ptr;
1471 	int 	implicit = -1;
1472 	int	function_return = 0;
1473 	char *	name;
1474 
1475 	/* No name is an error */
1476 	if (!orig_name)
1477 		return NULL;
1478 
1479 	name = remove_brackets(orig_name, NULL);
1480 	upper(name);
1481 
1482 	ptr = after_expando(name, 1, NULL);
1483 	if (*ptr) {
1484 		new_free(&name);
1485 		return NULL;
1486 	}
1487 
1488 	if (!my_stricmp(name, "FUNCTION_RETURN"))
1489 		function_return = 1;
1490 
1491 	/*
1492 	 * Search our current local variable stack, and wind our way
1493 	 * backwards until we find a NAMED stack -- that is the enclosing
1494 	 * alias or ON call.  If we find a variable in one of those enclosing
1495 	 * stacks, then we use it.  If we dont, we progress.
1496 	 *
1497 	 * This needs to be optimized for the degenerate case, when there
1498 	 * is no local variable available...  It will be true 99.999% of
1499 	 * the time.
1500 	 */
1501 	for (c = wind_index; c >= 0; c = call_stack[c].parent)
1502 	{
1503 		/* XXXXX */
1504 		if (function_return && last_function_call_level != -1)
1505 			c = last_function_call_level;
1506 
1507 		if (x_debug & DEBUG_LOCAL_VARS)
1508 			yell("Looking for [%s] in level [%d]", name, c);
1509 
1510 		if (call_stack[c].alias.list)
1511 		{
1512 			int x, cnt, loc;
1513 
1514 			/* We can always hope that the variable exists */
1515 			find_array_item((array*)&call_stack[c].alias, name, &cnt, &loc);
1516 			if (cnt < 0)
1517 				alias = call_stack[c].alias.list[loc];
1518 
1519 			/* XXXX - This is bletcherous */
1520 			/*
1521 			 * I agree, however, there doesn't seem to be any other
1522 			 * reasonable way to do it.  I guess launching multiple
1523 			 * binary searches on relevant portions of name would
1524 			 * do it, but the overhead could(?) do damage.
1525 			 *
1526 			 * Actualy, thinking about it again, seperating the
1527 			 * implicit variables from the normal ones would
1528 			 * probably work.
1529 			 */
1530 			else if (strchr(name, '.'))
1531 			    for (x = 0; x < loc; x++)
1532 			    {
1533 				Symbol *item;
1534 				size_t len;
1535 
1536 				item = call_stack[c].alias.list[x];
1537 				len =  strlen(item->name);
1538 
1539 				if (streq(item->name, name) == len) {
1540 					if (item->name[len-1] == '.') {
1541 						implicit = c;
1542 						break;
1543 					}
1544 				}
1545 			}
1546 
1547 			if (!alias && implicit >= 0)
1548 			{
1549 				alias = make_new_Symbol(name);
1550 				add_to_array ((array *)&call_stack[implicit].alias, (array_item *)alias);
1551 			}
1552 		}
1553 
1554 		if (alias)
1555 		{
1556 			if (x_debug & DEBUG_LOCAL_VARS)
1557 				yell("I found [%s] in level [%d] (%s)", name, c, alias->user_variable);
1558 			break;
1559 		}
1560 
1561 		if (*call_stack[c].name || call_stack[c].parent == -1 ||
1562 		    (function_return && last_function_call_level != -1))
1563 		{
1564 			if (x_debug & DEBUG_LOCAL_VARS)
1565 				yell("I didnt find [%s], stopped at level [%d]", name, c);
1566 			break;
1567 		}
1568 	}
1569 
1570 	new_free(&name);
1571 
1572 	if (alias)
1573 	{
1574 		if (list)
1575 			*list = &call_stack[c].alias;
1576 		return alias;
1577 	}
1578 	else if (list)
1579 		*list = &call_stack[wind_index].alias;
1580 
1581 	return NULL;
1582 }
1583 
1584 
1585 /* * */
delete_var_alias(const char * orig_name,int noisy)1586 static void	delete_var_alias (const char *orig_name, int noisy)
1587 {
1588 	Symbol *item;
1589 	char *	name;
1590 	int	cnt, loc;
1591 
1592 	name = remove_brackets(orig_name, NULL);
1593 	upper(name);
1594 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
1595 	if (item && cnt < 0)
1596 	{
1597 		new_free(&item->user_variable);
1598 		item->user_variable_stub = 0;
1599 		new_free(&(item->user_variable_package));
1600 		GC_symbol(item, (array *)&globals, loc);
1601 		if (noisy)
1602 			say("Assign %s removed", name);
1603 	}
1604 	else if (noisy)
1605 		say("No such assign: %s", name);
1606 	new_free(&name);
1607 }
1608 
delete_cmd_alias(const char * orig_name,int noisy)1609 static void	delete_cmd_alias (const char *orig_name, int noisy)
1610 {
1611 	Symbol *item;
1612 	char *	name;
1613 	int	cnt, loc;
1614 
1615 	name = remove_brackets(orig_name, NULL);
1616 	upper(name);
1617 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
1618 	if (item && cnt < 0)
1619 	{
1620 		new_free(&item->user_command);
1621 		item->user_command_stub = 0;
1622 		new_free(&(item->user_command_package));
1623 		destroy_arglist(&item->arglist);
1624 		GC_symbol(item, (array *)&globals, loc);
1625 		if (noisy)
1626 			say("Alias %s removed", name);
1627 	}
1628 	else if (noisy)
1629 		say("No such alias: %s", name);
1630 	new_free(&name);
1631 }
1632 
delete_builtin_command(const char * orig_name)1633 void	delete_builtin_command (const char *orig_name)
1634 {
1635 	Symbol *item;
1636 	char *	name;
1637 	int	cnt, loc;
1638 
1639 	name = remove_brackets(orig_name, NULL);
1640 	upper(name);
1641 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
1642 	if (item && cnt < 0)
1643 	{
1644 		item->builtin_command = NULL;
1645 		GC_symbol(item, (array *)&globals, loc);
1646 	}
1647 	new_free(&name);
1648 }
1649 
delete_builtin_function(const char * orig_name)1650 void	delete_builtin_function (const char *orig_name)
1651 {
1652 	Symbol *item;
1653 	char *	name;
1654 	int	cnt, loc;
1655 
1656 	name = remove_brackets(orig_name, NULL);
1657 	upper(name);
1658 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
1659 	if (item && cnt < 0)
1660 	{
1661 		item->builtin_function = NULL;
1662 		GC_symbol(item, (array *)&globals, loc);
1663 	}
1664 	new_free(&name);
1665 }
1666 
delete_builtin_expando(const char * orig_name)1667 void	delete_builtin_expando (const char *orig_name)
1668 {
1669 	Symbol *item;
1670 	char *	name;
1671 	int	cnt, loc;
1672 
1673 	name = remove_brackets(orig_name, NULL);
1674 	upper(name);
1675 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
1676 	if (item && cnt < 0)
1677 	{
1678 		item->builtin_expando = NULL;
1679 		GC_symbol(item, (array *)&globals, loc);
1680 	}
1681 	new_free(&name);
1682 }
1683 
delete_builtin_variable(const char * orig_name)1684 void	delete_builtin_variable (const char *orig_name)
1685 {
1686 	Symbol *item;
1687 	char *	name;
1688 	int	cnt, loc;
1689 
1690 	name = remove_brackets(orig_name, NULL);
1691 	upper(name);
1692 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
1693 	if (item && cnt < 0)
1694 	{
1695 		item->builtin_variable = NULL;
1696 		GC_symbol(item, (array *)&globals, loc);
1697 	}
1698 	new_free(&name);
1699 }
1700 
1701 /* * * */
bucket_local_alias(Bucket * b,const char * name)1702 static void	bucket_local_alias (Bucket *b, const char *name)
1703 {
1704 }
1705 
1706 #define BUCKET_FUNCTION(x, y) \
1707 void	bucket_ ## x (Bucket *b, const char *name)	\
1708 {								\
1709 	int	i;						\
1710 	size_t	len;						\
1711 	Symbol *item;						\
1712 								\
1713 	len = strlen(name);					\
1714 	for (i = 0; i < globals.max; i++)			\
1715 	{							\
1716 		item = globals.list[i];				\
1717 		if (!my_strnicmp(name, item->name, len))	\
1718 		{						\
1719 		    if (item-> y )				\
1720 			add_to_bucket(b, item->name, item-> y);	\
1721 		}						\
1722 	}							\
1723 }
1724 
BUCKET_FUNCTION(var_alias,user_variable)1725 BUCKET_FUNCTION(var_alias, user_variable)
1726 BUCKET_FUNCTION(cmd_alias, user_command)
1727 BUCKET_FUNCTION(builtin_commands, builtin_command)
1728 BUCKET_FUNCTION(builtin_functions, builtin_function)
1729 BUCKET_FUNCTION(builtin_expandos, builtin_expando)
1730 BUCKET_FUNCTION(builtin_variables, builtin_variable)
1731 
1732 /* * * */
1733 static void	list_local_alias (const char *orig_name)
1734 {
1735 	size_t len = 0;
1736 	int cnt;
1737 	int DotLoc, LastDotLoc = 0;
1738 	char *LastStructName = NULL;
1739 	char *s;
1740 	char *name = NULL;
1741 	Symbol *item;
1742 
1743 	say("Visible Local Assigns:");
1744 	if (orig_name)
1745 	{
1746 		name = remove_brackets(orig_name, NULL);
1747 		len = strlen(upper(name));
1748 	}
1749 
1750 	for (cnt = wind_index; cnt >= 0; cnt = call_stack[cnt].parent)
1751 	{
1752 	    int x;
1753 	    if (!call_stack[cnt].alias.list)
1754 		continue;
1755 	    for (x = 0; x < call_stack[cnt].alias.max; x++)
1756 	    {
1757 		item = call_stack[cnt].alias.list[x];
1758 		if (!name || !strncmp(item->name, name, len))
1759 		{
1760 		    if ((s = strchr(item->name + len, '.')))
1761 		    {
1762 			DotLoc = s - item->name;
1763 			if (!LastStructName || (DotLoc != LastDotLoc) ||
1764 				strncmp(item->name, LastStructName, DotLoc))
1765 			{
1766 			    put_it("\t%*.*s\t<Structure>", DotLoc, DotLoc,
1767 						item->name);
1768 			    LastStructName = item->name;
1769 			    LastDotLoc = DotLoc;
1770 			}
1771 		    }
1772 		    else
1773 			put_it("\t%s\t%s", item->name, item->user_variable);
1774 		}
1775 	    }
1776 	}
1777 }
1778 
1779 /*
1780  * This function is strictly O(N).  Its possible to address this.
1781  */
list_var_alias(const char * orig_name)1782 static void	list_var_alias (const char *orig_name)
1783 {
1784 	size_t	len = 0;
1785 	int	DotLoc,
1786 		LastDotLoc = 0;
1787 	const char	*LastStructName = NULL;
1788 	int	cnt;
1789 	char	*s;
1790 	const char *script;
1791 	char *name = NULL;
1792 
1793 	say("Assigns:");
1794 
1795 	if (orig_name)
1796 	{
1797 		name = remove_brackets(orig_name, NULL);
1798 		len = strlen(upper(name));
1799 	}
1800 
1801 	for (cnt = 0; cnt < globals.max; cnt++)
1802 	{
1803 	    Symbol *item = globals.list[cnt];
1804 
1805 	    if (item == NULL)
1806 			continue;
1807 
1808 	    if (!item->user_variable)
1809 		continue;
1810 
1811 	    if (!name || !strncmp(item->name, name, len))
1812 	    {
1813 		script = empty(item->user_variable_package) ? "*" :
1814 				  item->user_variable_package;
1815 
1816 		if ((s = strchr(item->name + len, '.')))
1817 		{
1818 			DotLoc = s - globals.list[cnt]->name;
1819 			if (!LastStructName || (DotLoc != LastDotLoc) ||
1820 				strncmp(item->name, LastStructName, DotLoc))
1821 			{
1822 				say("[%s]\t%*.*s\t<Structure>", script, DotLoc,
1823 					 DotLoc, item->name);
1824 				LastStructName = item->name;
1825 				LastDotLoc = DotLoc;
1826 			}
1827 		}
1828 		else
1829 		{
1830 			if (item->user_variable_stub)
1831 				say("[%s]\t%s STUBBED TO %s", script,
1832 					item->name, item->user_variable);
1833 			else
1834 				say("[%s]\t%s\t%s", script,
1835 					item->name, item->user_variable);
1836 		}
1837 	    }
1838 	}
1839 
1840 	new_free(&name);
1841 }
1842 
1843 /*
1844  * This function is strictly O(N).  Its possible to address this.
1845  */
list_cmd_alias(const char * orig_name)1846 static void	list_cmd_alias (const char *orig_name)
1847 {
1848 	size_t	len = 0;
1849 	int	DotLoc,
1850 		LastDotLoc = 0;
1851 	char	*LastStructName = NULL;
1852 	int	cnt;
1853 	char	*s;
1854 	const char *script;
1855 	char *name = NULL;
1856 
1857 	say("Aliases:");
1858 
1859 	if (orig_name)
1860 	{
1861 		name = remove_brackets(orig_name, NULL);
1862 		len = strlen(upper(name));
1863 	}
1864 
1865 	for (cnt = 0; cnt < globals.max; cnt++)
1866 	{
1867 	    Symbol *item = globals.list[cnt];
1868 
1869 	    if (item == NULL)
1870 		continue;
1871 
1872 	    if (!item->user_command)
1873 		continue;
1874 
1875 	    if (!name || !strncmp(item->name, name, len))
1876 	    {
1877 		script = empty(item->user_command_package) ? "*" :
1878 				  item->user_command_package;
1879 
1880 		if ((s = strchr(item->name + len, '.')))
1881 		{
1882 			DotLoc = s - item->name;
1883 			if (!LastStructName || (DotLoc != LastDotLoc) ||
1884 				strncmp(item->name, LastStructName, DotLoc))
1885 			{
1886 				say("[%s]\t%*.*s\t<Structure>", script, DotLoc,
1887 					 DotLoc, item->name);
1888 				LastStructName = item->name;
1889 				LastDotLoc = DotLoc;
1890 			}
1891 		}
1892 		else
1893 		{
1894 			if (item->user_command_stub)
1895 				say("[%s]\t%s STUBBED TO %s", script,
1896 					item->name, item->user_command);
1897 			else
1898 			{
1899 				char *arglist = print_arglist(item->arglist);
1900 				if (arglist)
1901 				{
1902 				    say("[%s]\t%s (%s)\t%s", script,
1903 					item->name, arglist,
1904 					item->user_command);
1905 				    new_free(&arglist);
1906 				}
1907 				else
1908 				    say("[%s]\t%s\t%s", script,
1909 					item->name, item->user_command);
1910 			}
1911 		}
1912 	    }
1913 	}
1914 	new_free(&name);
1915 }
1916 
list_builtin_commands(const char * orig_name)1917 static void	list_builtin_commands (const char *orig_name)
1918 {
1919 }
1920 
list_builtin_functions(const char * orig_name)1921 static void	list_builtin_functions (const char *orig_name)
1922 {
1923 }
1924 
list_builtin_expandos(const char * orig_name)1925 static void	list_builtin_expandos (const char *orig_name)
1926 {
1927 }
1928 
list_builtin_variables(const char * orig_name)1929 static void	list_builtin_variables (const char *orig_name)
1930 {
1931 }
1932 
1933 /************************* UNLOADING SCRIPTS ************************/
unload_cmd_alias(const char * package)1934 static void	unload_cmd_alias (const char *package)
1935 {
1936 	int 	cnt;
1937 	Symbol *item;
1938 
1939 	for (cnt = 0; cnt < globals.max; cnt++)
1940 	{
1941 	    item = globals.list[cnt];
1942 
1943 	    if (!item->user_command_package)
1944 		continue;
1945 	    else if (!strcmp(item->user_command_package, package)) {
1946 		delete_cmd_alias(item->name, 0);
1947 		cnt = -1;
1948 		continue;
1949 	    }
1950 	}
1951 }
1952 
unload_var_alias(const char * package)1953 static void	unload_var_alias (const char *package)
1954 {
1955 	int 	cnt;
1956 	Symbol	*item;
1957 
1958 	for (cnt = 0; cnt < globals.max; cnt++)
1959 	{
1960 	    item = globals.list[cnt];
1961 
1962 	    if (!item->user_variable_package)
1963 		continue;
1964 	    else if (!strcmp(item->user_variable_package, package)) {
1965 		delete_var_alias(item->name, 0);
1966 		cnt = -1;
1967 		continue;
1968 	    }
1969 	}
1970 }
1971 
unload_builtin_commands(const char * filename)1972 static void	unload_builtin_commands (const char *filename)
1973 {
1974 }
1975 
unload_builtin_functions(const char * filename)1976 static	void	unload_builtin_functions (const char *filename)
1977 {
1978 }
1979 
unload_builtin_expandos(const char * filename)1980 static	void	unload_builtin_expandos (const char *filename)
1981 {
1982 }
1983 
unload_builtin_variables(const char * filename)1984 static	void	unload_builtin_variables (const char *filename)
1985 {
1986 }
1987 
1988 /* * */
1989 /*
1990  * This function is strictly O(N).  This should probably be addressed.
1991  *
1992  * Updated as per get_subarray_elements.
1993  */
glob_cmd_alias(const char * name,int * howmany,int maxret,int start,int rev)1994 char **	glob_cmd_alias (const char *name, int *howmany, int maxret, int start, int rev)
1995 {
1996 	int	pos, max;
1997 	int    	cnt;
1998 	int     len;
1999 	char    **matches = NULL;
2000 	int     matches_size = 0;
2001 
2002 	len = strlen(name);
2003 	*howmany = 0;
2004 	if (len) {
2005 		find_array_item((array*)&globals, name, &max, &pos);
2006 	} else {
2007 		pos = 0;
2008 		max = globals.max;
2009 	}
2010 	if (0 > max) max = -max;
2011 
2012 	for (cnt = 0; cnt < max; cnt++, pos++)
2013 	{
2014 		if (!globals.list[pos]->user_command)
2015 			continue;
2016 		if (strchr(globals.list[pos]->name + len, '.'))
2017 			continue;
2018 
2019 		if (*howmany >= matches_size)
2020 		{
2021 			matches_size += 5;
2022 			RESIZE(matches, char *, matches_size + 1);
2023 		}
2024 		matches[*howmany] = malloc_strdup(globals.list[pos]->name);
2025 		*howmany += 1;
2026 	}
2027 
2028 	if (*howmany)
2029 		matches[*howmany] = NULL;
2030 	else
2031 		new_free((char **)&matches);
2032 
2033 	return matches;
2034 }
2035 
2036 /*
2037  * This function is strictly O(N).  This should probably be addressed.
2038  *
2039  * Updated as per get_subarray_elements.
2040  */
glob_assign_alias(const char * name,int * howmany,int maxret,int start,int rev)2041 char **	glob_assign_alias (const char *name, int *howmany, int maxret, int start, int rev)
2042 {
2043 	int	pos, max;
2044 	int    	cnt;
2045 	int     len;
2046 	char    **matches = NULL;
2047 	int     matches_size = 0;
2048 
2049 	len = strlen(name);
2050 	*howmany = 0;
2051 	if (len) {
2052 		find_array_item((array*)&globals, name, &max, &pos);
2053 	} else {
2054 		pos = 0;
2055 		max = globals.max;
2056 	}
2057 	if (0 > max) max = -max;
2058 
2059 	for (cnt = 0; cnt < max; cnt++, pos++)
2060 	{
2061 		if (!globals.list[pos]->user_variable)
2062 			continue;
2063 		if (strchr(globals.list[pos]->name + len, '.'))
2064 			continue;
2065 
2066 		if (*howmany >= matches_size)
2067 		{
2068 			matches_size += 5;
2069 			RESIZE(matches, char *, matches_size + 1);
2070 		}
2071 		matches[*howmany] = malloc_strdup(globals.list[pos]->name);
2072 		*howmany += 1;
2073 	}
2074 
2075 	if (*howmany)
2076 		matches[*howmany] = NULL;
2077 	else
2078 		new_free((char **)&matches);
2079 
2080 	return matches;
2081 }
2082 
2083 #if 0
2084 char **glob_builtin_commands (const char *name, int *howmany, int maxret, int start, int rev)
2085 {
2086 	return NULL;
2087 }
2088 
2089 char **glob_builtin_functions (const char *name, int *howmany, int maxret, int start, int rev)
2090 {
2091 	return NULL;
2092 }
2093 
2094 char **glob_builtin_expandos (const char *name, int *howmany, int maxret, int start, int rev)
2095 {
2096 	return NULL;
2097 }
2098 
2099 char **glob_builtin_variables (const char *name, int *howmany, int maxret, int start, int rev)
2100 {
2101 	return NULL;
2102 }
2103 #endif
2104 
2105 #define PMATCH_SYMBOL(x, y) \
2106 char **	pmatch_ ## x (const char *name, int *howmany, int maxret, int start, int rev) \
2107 { \
2108 	int    	cnt, cnt1; \
2109 	int     len; \
2110 	char **matches = NULL; \
2111 	int     matches_size = 5; \
2112 \
2113 	len = strlen(name); \
2114 	*howmany = 0; \
2115 	matches = RESIZE(matches, char *, matches_size); \
2116 \
2117 	for (cnt1 = 0; cnt1 < globals.max; cnt1++) \
2118 	{ \
2119 		cnt = rev ? globals.max - cnt1 - 1 : cnt1; \
2120 		if (!globals.list[cnt]-> y ) \
2121 			continue; \
2122 \
2123 		if (wild_match(name, globals.list[cnt]->name)) \
2124 		{ \
2125 			if (start--) \
2126 				continue; \
2127 			else \
2128 				start++; \
2129 			matches[*howmany] = globals.list[cnt]->name; \
2130 			*howmany += 1; \
2131 			if (*howmany == matches_size) \
2132 			{ \
2133 				matches_size += 5; \
2134 				RESIZE(matches, char *, matches_size); \
2135 			} \
2136 			if (maxret && --maxret <= 0) \
2137 				break; \
2138 		} \
2139 	} \
2140 \
2141 	if (*howmany) \
2142 		matches[*howmany] = NULL; \
2143 	else \
2144 		new_free((char **)&matches); \
2145 \
2146 	return matches; \
2147 }
2148 
PMATCH_SYMBOL(assign_alias,user_variable)2149 PMATCH_SYMBOL(assign_alias, user_variable)
2150 PMATCH_SYMBOL(cmd_alias, user_command)
2151 PMATCH_SYMBOL(builtin_variables, builtin_variable)
2152 PMATCH_SYMBOL(builtin_commands, builtin_command)
2153 PMATCH_SYMBOL(builtin_functions, builtin_function)
2154 PMATCH_SYMBOL(builtin_expandos, builtin_expando)
2155 static PMATCH_SYMBOL(any_symbol, name)
2156 
2157 /*****************************************************************************/
2158 /*
2159  * This function is strictly O(N).  This should probably be addressed.
2160  *
2161  * OK, so it isn't _strictly_ O(N) anymore, however, it is still O(N)
2162  * for N being all elements below the subarray being requested.  This
2163  * makes recursive /foreach's such as purge run much faster, however,
2164  * since each variable will be tested no more than its current depth
2165  * times, rather than every single time a /foreach is performed.
2166  *
2167  * In the worst case scenario where the entire variable space consists
2168  * of a single flat subarray, the new algorithm would perform no worse
2169  * than the old.
2170  */
2171 char **	get_subarray_elements (const char *orig_root, int *howmany, int type)
2172 {
2173 	SymbolSet *as;		/* XXXX */
2174 	Symbol *s;
2175 	int pos, cnt, max;
2176 	int cmp = 0;
2177 	char **matches = NULL;
2178 	int matches_size = 0;
2179 	size_t end;
2180 	char *last = NULL;
2181 	char *root = NULL;
2182 
2183 	as = &globals;
2184 	root = malloc_strdup2(orig_root, ".");
2185 	find_array_item((array*)as, root, &max, &pos);
2186 
2187 	if (0 > max)
2188 		max = -max;
2189 	*howmany = 0;
2190 	cmp = strlen(root);
2191 	new_free(&root);
2192 
2193 	for (cnt = 0; cnt < max; cnt++, pos++)
2194 	{
2195 		s = (Symbol *)ARRAY_ITEM(as, pos);
2196 
2197 		end = strcspn(s->name + cmp, ".");
2198 		if (last && !my_strnicmp(s->name, last, cmp + end))
2199 			continue;
2200 
2201 		/* Must have an entry for the correct type */
2202 		if (type == COMMAND_ALIAS && s->user_command == NULL)
2203 			continue;
2204 		if (type == VAR_ALIAS && s->user_variable == NULL)
2205 			continue;
2206 
2207 		if (*howmany >= matches_size)
2208 		{
2209 			matches_size = *howmany + 5;
2210 			RESIZE(matches, char *, matches_size + 1);
2211 		}
2212 		matches[*howmany] = malloc_strndup(s->name, cmp + end);
2213 		last = matches[*howmany];
2214 		*howmany += 1;
2215 	}
2216 
2217 	if (*howmany)
2218 		matches[*howmany] = NULL;
2219 	else
2220 		new_free((char **)&matches);
2221 
2222 	return matches;
2223 }
2224 
2225 
2226 /***************************************************************************/
destroy_cmd_aliases(SymbolSet * my_array)2227 static	void	destroy_cmd_aliases (SymbolSet *my_array)
2228 {
2229 	int cnt = 0;
2230 	Symbol *item;
2231 
2232 	for (;;)
2233 	{
2234 	    for (cnt = 0; cnt < my_array->max; cnt++)
2235 	    {
2236 		item = my_array->list[cnt];
2237 		if (!item->user_command && !item->user_command_stub &&
2238 				!item->arglist && !item->user_command_package)
2239 			continue;
2240 
2241 		new_free((void **)&item->user_command);
2242 		new_free((void **)&item->user_command_package);
2243 		item->user_command_stub = 0;
2244 		destroy_arglist(&item->arglist);
2245 		GC_symbol(item, (array *)my_array, cnt);
2246 		break;
2247 	    }
2248 	    if (cnt >= my_array->max)
2249 		    return;
2250 	}
2251 }
2252 
destroy_var_aliases(SymbolSet * my_array)2253 static	void	destroy_var_aliases (SymbolSet *my_array)
2254 {
2255 	int cnt = 0;
2256 	Symbol *item;
2257 
2258 	for (;;)
2259 	{
2260 	    for (cnt = 0; cnt < my_array->max; cnt++)
2261 	    {
2262 		item = my_array->list[cnt];
2263 		if (!item->user_variable && !item->user_variable_stub)
2264 			continue;
2265 
2266 		new_free((void **)&item->user_variable);
2267 		new_free((void **)&item->user_variable_package);
2268 		item->user_variable_stub = 0;
2269 		GC_symbol(item, (array *)my_array, cnt);
2270 		break;
2271 	    }
2272 	    if (cnt >= my_array->max)
2273 		    return;
2274 	}
2275 }
2276 
destroy_builtin_commands(SymbolSet * my_array)2277 static	void	destroy_builtin_commands (SymbolSet *my_array)
2278 {
2279 	int cnt = 0;
2280 	Symbol *item;
2281 
2282 	for (;;)
2283 	{
2284 	    for (cnt = 0; cnt < my_array->max; cnt++)
2285 	    {
2286 		item = my_array->list[cnt];
2287 		if (!item->builtin_command)
2288 			continue;
2289 		item->builtin_command = NULL;
2290 		GC_symbol(item, (array *)my_array, cnt);
2291 		break;
2292 	    }
2293 	    if (cnt >= my_array->max)
2294 		    return;
2295 	}
2296 }
2297 
destroy_builtin_functions(SymbolSet * my_array)2298 static	void	destroy_builtin_functions (SymbolSet *my_array)
2299 {
2300 	int cnt = 0;
2301 	Symbol *item;
2302 
2303 	for (;;)
2304 	{
2305 	    for (cnt = 0; cnt < my_array->max; cnt++)
2306 	    {
2307 		item = my_array->list[cnt];
2308 		if (!item->builtin_function)
2309 			continue;
2310 		item->builtin_function = NULL;
2311 		GC_symbol(item, (array *)my_array, cnt);
2312 		break;
2313 	    }
2314 	    if (cnt >= my_array->max)
2315 		    return;
2316 	}
2317 }
2318 
destroy_builtin_expandos(SymbolSet * my_array)2319 static	void	destroy_builtin_expandos (SymbolSet *my_array)
2320 {
2321 	int cnt = 0;
2322 	Symbol *item;
2323 
2324 	for (;;)
2325 	{
2326 	    for (cnt = 0; cnt < my_array->max; cnt++)
2327 	    {
2328 		item = my_array->list[cnt];
2329 		if (!item->builtin_expando)
2330 			continue;
2331 		item->builtin_expando = NULL;
2332 		GC_symbol(item, (array *)my_array, cnt);
2333 		break;
2334 	    }
2335 	    if (cnt >= my_array->max)
2336 		    return;
2337 	}
2338 }
2339 
destroy_builtin_variables(SymbolSet * my_array)2340 static	void	destroy_builtin_variables (SymbolSet *my_array)
2341 {
2342 	int cnt = 0;
2343 	Symbol *item;
2344 
2345 	for (;;)
2346 	{
2347 	    for (cnt = 0; cnt < my_array->max; cnt++)
2348 	    {
2349 		item = my_array->list[cnt];
2350 		if (!item->builtin_variable)
2351 			continue;
2352 		item->builtin_variable = NULL;		/* XXX memory leak */
2353 		GC_symbol(item, (array *)my_array, cnt);
2354 		break;
2355 	    }
2356 	    if (cnt >= my_array->max)
2357 		    return;
2358 	}
2359 }
2360 
2361 /******************* RUNTIME STACK SUPPORT **********************************/
2362 
make_local_stack(const char * name)2363 int	make_local_stack 	(const char *name)
2364 {
2365 	wind_index++;
2366 
2367 #ifdef MAX_STACK_FRAMES
2368 	if (wind_index >= MAX_STACK_FRAMES)
2369 	{
2370 		system_exception++;
2371 		return 0;
2372 	}
2373 #endif
2374 
2375 	if (wind_index >= max_wind)
2376 	{
2377 		int tmp_wind = wind_index;
2378 
2379 		if (max_wind == -1)
2380 			max_wind = 8;
2381 		else
2382 			max_wind <<= 1;
2383 
2384 		RESIZE(call_stack, RuntimeStack, max_wind);
2385 		for (; wind_index < max_wind; wind_index++)
2386 		{
2387 			call_stack[wind_index].alias.max = 0;
2388 			call_stack[wind_index].alias.max_alloc = 0;
2389 			call_stack[wind_index].alias.list = NULL;
2390 			call_stack[wind_index].alias.func = strncmp;
2391 			call_stack[wind_index].alias.hash = HASH_INSENSITIVE;
2392 			call_stack[wind_index].current = NULL;
2393 			call_stack[wind_index].name = NULL;
2394 			call_stack[wind_index].parent = -1;
2395 		}
2396 		wind_index = tmp_wind;
2397 	}
2398 
2399 	/* Just in case... */
2400 	destroy_local_stack();
2401 	wind_index++;		/* XXXX - chicanery */
2402 
2403 	if (name)
2404 	{
2405 		call_stack[wind_index].name = name;
2406 		call_stack[wind_index].parent = -1;
2407 	}
2408 	else
2409 	{
2410 		call_stack[wind_index].name = empty_string;
2411 		call_stack[wind_index].parent = wind_index - 1;
2412 	}
2413 	call_stack[wind_index].locked = 0;
2414 	return 1;
2415 }
2416 
find_locked_stack_frame(void)2417 static int	find_locked_stack_frame	(void)
2418 {
2419 	int i;
2420 	for (i = 0; i < wind_index; i++)
2421 		if (call_stack[i].locked)
2422 			return i;
2423 
2424 	return -1;
2425 }
2426 
bless_local_stack(void)2427 void	bless_local_stack	(void)
2428 {
2429 	call_stack[wind_index].name = empty_string;
2430 	call_stack[wind_index].parent = find_locked_stack_frame();
2431 }
2432 
destroy_local_stack(void)2433 void 	destroy_local_stack 	(void)
2434 {
2435 	/*
2436 	 * We clean up as best we can here...
2437 	 */
2438 	if (call_stack[wind_index].alias.list)
2439 		destroy_var_aliases(&call_stack[wind_index].alias);
2440 	if (call_stack[wind_index].current)
2441 		call_stack[wind_index].current = 0;
2442 	if (call_stack[wind_index].name)
2443 		call_stack[wind_index].name = 0;
2444 
2445 	wind_index--;
2446 }
2447 
set_current_command(char * line)2448 void 	set_current_command 	(char *line)
2449 {
2450 	call_stack[wind_index].current = line;
2451 }
2452 
unset_current_command(void)2453 void 	unset_current_command 	(void)
2454 {
2455 	call_stack[wind_index].current = NULL;
2456 }
2457 
lock_stack_frame(void)2458 void	lock_stack_frame 	(void)
2459 {
2460 	call_stack[wind_index].locked = 1;
2461 }
2462 
unlock_stack_frame(void)2463 void	unlock_stack_frame	(void)
2464 {
2465 	int lock = find_locked_stack_frame();
2466 	if (lock >= 0)
2467 		call_stack[lock].locked = 0;
2468 }
2469 
dump_call_stack(void)2470 void 	dump_call_stack 	(void)
2471 {
2472 	int i;
2473 
2474 	yell("Call stack");
2475 	for (i = wind_index; i >= 0; i--)
2476 		yell("[%3d] %s", i, call_stack[i].current ? call_stack[i].current : "<async>");
2477 	yell("End of call stack");
2478 }
2479 
panic_dump_call_stack(void)2480 void 	panic_dump_call_stack 	(void)
2481 {
2482 	if (wind_index >= 0)
2483 	{
2484 		int i;
2485 
2486 		fprintf(stderr, "Call stack\n");
2487 		for (i = wind_index; i >= 0; i--)
2488 			fprintf(stderr, "[%3d] %s\n", i,
2489 				call_stack[i].current ? call_stack[i].current : "<async>");
2490 		fprintf(stderr, "End of call stack\n");
2491 	}
2492 	else
2493 		fprintf(stderr, "Stack is corrupted [wind_index is %d], sorry.\n",
2494 			wind_index);
2495 }
2496 
2497 
2498 /************************* DIRECT VARIABLE EXPANSION ************************/
2499 /*
2500  * get_variable: This returns the rvalue of the symbol "str".
2501  *
2502  * An rvalue is what an expando is substituted with if it is used on the
2503  * right hand side of an assignment.
2504  *
2505  *    1) local variable
2506  *    2) global variable
2507  *    3) environment variable
2508  *    4) The empty string
2509  */
get_variable(const char * str)2510 char 	*get_variable 	(const char *str)
2511 {
2512 	return get_variable_with_args(str, NULL);
2513 }
2514 
2515 
get_variable_with_args(const char * str,const char * args)2516 static char *	get_variable_with_args (const char *str, const char *args)
2517 {
2518 	Symbol	*alias = NULL;
2519 	char	*ret = NULL;
2520 	char	*name = NULL;
2521 	char	*freep = NULL;
2522 	int	copy = 0;
2523 	int	local = 0;
2524 
2525 	freep = name = remove_brackets(str, args);
2526 
2527 	/*
2528 	 * Support $:var to mean local variable ONLY (no globals)
2529 	 * Support $::var to mean global variable ONLY (no locals)
2530 	 * Support $: with nothing after it as a built in expando.  Ick.
2531 	 */
2532 	if (*name == ':' && name[1])
2533 	{
2534 		name++, local = 1;
2535 		if (*name == ':')
2536 			name++, local = -1;
2537 	}
2538 
2539 	/*
2540 	 * local == -1  means "local variables not allowed"
2541 	 * local == 0   means "locals first, then globals"
2542 	 * local == 1   means "global variables not allowed"
2543 	 */
2544 	if ((local != -1) && (alias = find_local_alias(name, NULL)))
2545 		copy = 1, ret = alias->user_variable;
2546 	else if (local == 1)
2547 		(void) 0;
2548 
2549 	if (ret == NULL && ((alias = lookup_symbol(name)) != NULL))
2550 	{
2551 		if (alias->user_variable)
2552 			copy = 1, ret = alias->user_variable;
2553 		else if (alias->builtin_expando)
2554 			copy = 0, ret = alias->builtin_expando();
2555 		else if (alias->builtin_variable)
2556 			copy = 0, ret = make_string_var_bydata((const void *)alias->builtin_variable);
2557 	}
2558 /*
2559 	if (ret == NULL && (ret = make_string_var(str)))
2560 		copy = 0;
2561 */
2562 
2563 	if (ret == NULL)
2564 		copy = 1, ret = getenv(str);
2565 
2566 	if (ret == NULL)
2567 	{
2568 		copy = 0, ret = malloc_strdup(empty_string);
2569 		if (x_debug & DEBUG_UNKNOWN)
2570 		    yell("Variable lookup to non-existant assign [%s]", name);
2571 	}
2572 
2573 	new_free(&freep);
2574 	return (copy ? malloc_strdup(ret) : ret);
2575 }
2576 
2577 /* * */
get_cmd_alias(const char * name,void ** args,void (** func)(const char *,char *,const char *))2578 const char *	get_cmd_alias (const char *name, void **args, void (**func) (const char *, char *, const char *))
2579 {
2580 	Symbol *item;
2581 
2582 	if ((item = lookup_symbol(name)))
2583 	{
2584 		if (args)
2585 			*args = (void *)item->arglist;
2586 		*func = item->builtin_command;
2587 		return item->user_command;
2588 	}
2589 
2590 	*func = NULL;
2591 	return NULL;
2592 }
2593 
2594 /* * */
get_func_alias(const char * name,void ** args,char * (** func)(char *))2595 const char *	get_func_alias (const char *name, void **args, char * (**func) (char *))
2596 {
2597 	Symbol *item;
2598 
2599 	if ((item = lookup_symbol(name)))
2600 	{
2601 		if (args)
2602 			*args = (void *)item->arglist;
2603 		*func = item->builtin_function;
2604 		return item->user_command;
2605 	}
2606 
2607 	*func = NULL;
2608 	return NULL;
2609 }
2610 
get_var_alias(const char * name,char * (** efunc)(void),IrcVariable ** var)2611 const char *	get_var_alias (const char *name, char *(**efunc)(void), IrcVariable **var)
2612 {
2613 	Symbol *item;
2614 
2615 	if ((item = lookup_symbol(name)))
2616 	{
2617 		*efunc = item->builtin_expando;
2618 		*var = item->builtin_variable;
2619 		return item->user_variable;
2620 	}
2621 
2622 	*efunc = NULL;
2623 	*var = NULL;
2624 	return NULL;
2625 }
2626 
2627 
2628 /******************* expression and text parsers ***************************/
2629 /* XXXX - bogus for now */
2630 #include "expr2.c"
2631 #include "expr.c"
2632 
2633 
2634 /****************************** ALIASCTL ************************************/
2635 /* Used by function_aliasctl */
2636 /* MUST BE FIXED */
aliasctl(char * input)2637 char 	*aliasctl 	(char *input)
2638 {
2639 	int list = -1;
2640 	char *listc;
2641 	enum { EXISTS, GET, SET, NMATCH, PMATCH, GETPACKAGE, SETPACKAGE, MAXRET } op;
2642 static	int maxret = 0;
2643 	int start = 0;
2644 	int reverse = 0;
2645 
2646 	GET_FUNC_ARG(listc, input);
2647 	if (!my_strnicmp(listc, "ASSIGN", 2))
2648 		list = VAR_ALIAS;
2649 	else if (!my_strnicmp(listc, "ALIAS", 2))
2650 		list = COMMAND_ALIAS;
2651 	else if (!my_strnicmp(listc, "LOCAL", 2))
2652 		list = VAR_ALIAS_LOCAL;
2653 	else if (!my_strnicmp(listc, "MAXRET", 4))
2654 	{
2655 		int old_maxret = maxret;
2656 		if (input && *input)
2657 			GET_INT_ARG(maxret, input);
2658 		RETURN_INT(old_maxret);
2659 	}
2660 	else
2661 		RETURN_EMPTY;
2662 
2663 	GET_FUNC_ARG(listc, input);
2664 	if ((start = my_atol(listc)))
2665 		GET_FUNC_ARG(listc, input);
2666 	if (!my_strnicmp(listc, "GETPACKAGE", 4))
2667 		op = GETPACKAGE;
2668 	else if (!my_strnicmp(listc, "GET", 1))
2669 		op = GET;
2670 	else if (!my_strnicmp(listc, "SETPACKAGE", 4))
2671 		op = SETPACKAGE;
2672 	else if (!my_strnicmp(listc, "SET", 1))
2673 		op = SET;
2674 	else if (!my_strnicmp(listc, "MATCH", 1))
2675 		op = NMATCH;
2676 	else if (!my_strnicmp(listc, "RMATCH", 2))
2677 		op = NMATCH, reverse = 1;
2678 	else if (!my_strnicmp(listc, "PMATCH", 1))
2679 		op = PMATCH;
2680 	else if (!my_strnicmp(listc, "RPMATCH", 2))
2681 		op = PMATCH, reverse = 1;
2682 	else if (!my_strnicmp(listc, "EXISTS", 1))
2683 		op = EXISTS;
2684 	else
2685 		RETURN_EMPTY;
2686 
2687 	GET_FUNC_ARG(listc, input);
2688 	switch (op)
2689 	{
2690 		case (GET) :
2691 		case (EXISTS) :
2692 		case (GETPACKAGE) :
2693 		case (SETPACKAGE) :
2694 		{
2695 			Symbol *alias = NULL;
2696 			SymbolSet *a_list;
2697 
2698 			upper(listc);
2699 			if (list == VAR_ALIAS_LOCAL)
2700 				alias = find_local_alias(listc, &a_list);
2701 			else
2702 				alias = lookup_symbol(listc);
2703 
2704 			if (alias)
2705 			{
2706 				if (op == GET)
2707 				{
2708 				    if (list == COMMAND_ALIAS)
2709 					RETURN_STR(alias->user_command);
2710                                     else
2711 					RETURN_STR(alias->user_variable);
2712 				}
2713 				else if (op == EXISTS)
2714 					RETURN_INT(1);
2715 				else if (op == GETPACKAGE)
2716 				{
2717 				    if (list == VAR_ALIAS_LOCAL || list == VAR_ALIAS)
2718 					RETURN_STR(alias->user_variable_package);
2719 				    else
2720 					RETURN_STR(alias->user_command_package);
2721 				}
2722 				else /* op == SETPACKAGE */
2723 				{
2724 				    if (list == VAR_ALIAS_LOCAL || list == VAR_ALIAS)
2725 					malloc_strcpy(&alias->user_variable_package, input);
2726 				    else
2727 					malloc_strcpy(&alias->user_command_package, input);
2728 				    RETURN_INT(1);
2729 				}
2730 			}
2731 			else
2732 			{
2733 				if (op == GET || op == GETPACKAGE)
2734 					RETURN_EMPTY;
2735 				else	/* EXISTS or SETPACKAGE */
2736 					RETURN_INT(0);
2737 			}
2738 		}
2739 		case (SET) :
2740 		{
2741 			upper(listc);
2742 			if (list == VAR_ALIAS_LOCAL)
2743 				add_local_alias(listc, input, 0);
2744 			else if (list == VAR_ALIAS)
2745 				add_var_alias(listc, input, 0);
2746 			else
2747 				add_cmd_alias(listc, NULL, input);
2748 
2749 			RETURN_INT(1);
2750 		}
2751 		case (NMATCH) :
2752 		{
2753 			char **mlist = NULL;
2754 			char *mylist = NULL;
2755 			size_t	mylistclue = 0;
2756 			int num = 0, ctr;
2757 
2758 			if (!my_stricmp(listc, "*"))
2759 				listc = LOCAL_COPY(empty_string);
2760 
2761 			upper(listc);
2762 
2763 			if (list == COMMAND_ALIAS)
2764 				mlist = glob_cmd_alias(listc, &num, maxret, start, reverse);
2765 			else if (list == VAR_ALIAS)
2766 				mlist = glob_assign_alias(listc, &num, maxret, start, reverse);
2767 			else
2768 				RETURN_EMPTY;
2769 
2770 			for (ctr = 0; ctr < num; ctr++)
2771 			{
2772 				malloc_strcat_word_c(&mylist, space, mlist[ctr],DWORD_DWORDS, &mylistclue);
2773 				new_free((char **)&mlist[ctr]);
2774 			}
2775 			new_free((char **)&mlist);
2776 			if (mylist)
2777 				return mylist;
2778 			RETURN_EMPTY;
2779 		}
2780 		case (PMATCH) :
2781 		{
2782 			char **	mlist = NULL;
2783 			char *	mylist = NULL;
2784 			size_t	mylistclue = 0;
2785 			int	num = 0,
2786 				ctr;
2787 
2788 			if (list == COMMAND_ALIAS)
2789 				mlist = pmatch_cmd_alias(listc, &num, maxret, start, reverse);
2790 			else if (list == VAR_ALIAS)
2791 				mlist = pmatch_assign_alias(listc, &num, maxret, start, reverse);
2792 			else
2793 				RETURN_EMPTY;
2794 
2795 			for (ctr = 0; ctr < num; ctr++)
2796 				malloc_strcat_word_c(&mylist, space, mlist[ctr], DWORD_DWORDS, &mylistclue);
2797 			new_free((char **)&mlist);
2798 			if (mylist)
2799 				return mylist;
2800 			RETURN_EMPTY;
2801 		}
2802 		default :
2803 			my_error("aliasctl: Error");
2804 			RETURN_EMPTY;
2805 	}
2806 	RETURN_EMPTY;
2807 }
2808 
2809 
2810 
2811 /*************************** stacks **************************************/
2812 /* * * */
stack_push_var_alias(const char * name)2813 int	stack_push_var_alias (const char *name)
2814 {
2815 	Symbol *item, *sym;
2816 	int	cnt = 0, loc = 0;
2817 
2818 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
2819 	if (!item || cnt >= 0)
2820 	{
2821 	    item = make_new_Symbol(name);
2822 	    add_to_array((array *)&globals, (array_item *)item);
2823 	}
2824 
2825 	sym = make_new_Symbol(name);
2826 	malloc_strcpy(&sym->user_variable, item->user_variable);
2827 	sym->user_variable_stub = item->user_variable_stub;
2828 	malloc_strcpy(&sym->user_variable_package, item->user_variable_package);
2829 	sym->saved = item->saved;
2830 	sym->saved_hint = SAVED_VAR;
2831 	item->saved = sym;
2832 	return 0;
2833 }
2834 
stack_pop_var_alias(const char * name)2835 int	stack_pop_var_alias (const char *name)
2836 {
2837 	Symbol *item, *sym, *s, *ss;
2838 	int	cnt = 0, loc = 0;
2839 
2840 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
2841 	if (!item || cnt >= 0)
2842 		return -1;
2843 
2844 	for (sym = item; sym; sym = sym->saved)
2845 		if (sym->saved && sym->saved->saved_hint == SAVED_VAR)
2846 			break;
2847 	if (!sym)
2848 		return -1;
2849 
2850 	s = sym->saved;
2851 	ss = sym->saved->saved;
2852 	malloc_strcpy(&item->user_variable, s->user_variable);
2853 	item->user_variable_stub = s->user_variable_stub;
2854 	malloc_strcpy(&item->user_variable_package, s->user_variable_package);
2855 	new_free(&s->user_variable);
2856 	new_free(&s->user_variable_package);
2857 
2858 	if (GC_symbol(s, NULL, -1))
2859 		sym->saved = ss;
2860 	GC_symbol(item, (array *)&globals, loc);
2861 	return 0;
2862 }
2863 
stack_list_var_alias(const char * name)2864 int	stack_list_var_alias (const char *name)
2865 {
2866 	Symbol *item, *sym;
2867 	int	counter = 0;
2868 
2869 	if (!(item = lookup_symbol(name)))
2870 		return -1;
2871 
2872 	for (sym = item->saved; sym; sym = sym->saved)
2873 	{
2874 	    if (sym->saved_hint == SAVED_VAR)
2875 	    {
2876 		if (sym->user_variable == NULL)
2877 		    say("\t%s\t<Placeholder>", sym->name);
2878 		else if (sym->user_variable_stub)
2879 		    say("\t%s STUBBED TO %s", sym->name, sym->user_variable);
2880 		else
2881 		    say("\t%s\t%s", sym->name, sym->user_variable);
2882 		counter++;
2883 	    }
2884 	}
2885 
2886 	if (counter)
2887 		return 0;
2888 	return -1;
2889 }
2890 
2891 /* * * */
stack_push_cmd_alias(char * name)2892 int	stack_push_cmd_alias (char *name)
2893 {
2894 	Symbol *item, *sym;
2895 	int	cnt = 0, loc = 0;
2896 
2897 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
2898 	if (!item || cnt >= 0)
2899 	{
2900 	    item = make_new_Symbol(name);
2901 	    add_to_array((array *)&globals, (array_item *)item);
2902 	}
2903 
2904 	sym = make_new_Symbol(name);
2905 	malloc_strcpy(&sym->user_command, item->user_command);
2906 	sym->user_command_stub = item->user_command_stub;
2907 	malloc_strcpy(&sym->user_command_package, item->user_command_package);
2908 	sym->arglist = clone_arglist(item->arglist);
2909 	sym->saved = item->saved;
2910 	sym->saved_hint = SAVED_CMD;
2911 	item->saved = sym;
2912 	return 0;
2913 }
2914 
stack_pop_cmd_alias(const char * name)2915 int	stack_pop_cmd_alias (const char *name)
2916 {
2917 	Symbol *item, *sym, *s, *ss;
2918 	int	cnt = 0, loc = 0;
2919 
2920 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
2921 	if (!item || cnt >= 0)
2922 		return -1;
2923 
2924 	for (sym = item; sym; sym = sym->saved)
2925 	{
2926 		if (sym->saved && sym->saved->saved_hint == SAVED_CMD)
2927 			break;
2928 	}
2929 	if (!sym)
2930 		return -1;
2931 
2932 	s = sym->saved;
2933 	ss = sym->saved->saved;
2934 	malloc_strcpy(&item->user_command, s->user_command);
2935 	item->user_command_stub = s->user_command_stub;
2936 	malloc_strcpy(&item->user_command_package, s->user_command_package);
2937 	new_free(&s->user_command);
2938 	new_free(&s->user_command_package);
2939 
2940 	if (GC_symbol(s, NULL, -1))
2941 		sym->saved = ss;
2942 	GC_symbol(item, (array *)&globals, loc);
2943 	return 0;
2944 }
2945 
stack_list_cmd_alias(const char * name)2946 int	stack_list_cmd_alias (const char *name)
2947 {
2948 	Symbol *item, *sym;
2949 	int	counter = 0;
2950 
2951 	if (!(item = lookup_symbol(name)))
2952 		return -1;
2953 
2954 	for (sym = item->saved; sym; sym = sym->saved)
2955 	{
2956 	    if (sym->saved_hint == SAVED_CMD)
2957 	    {
2958 		if (sym->user_command == NULL)
2959 		    say("\t%s\t<Placeholder>", sym->name);
2960 		else if (sym->user_command_stub)
2961 		    say("\t%s STUBBED TO %s", sym->name, sym->user_command);
2962 		else
2963 		    say("\t%s\t%s", sym->name, sym->user_command);
2964 		counter++;
2965 	    }
2966 	}
2967 
2968 	if (counter)
2969 		return 0;
2970 	return -1;
2971 }
2972 
2973 
2974 
2975 /* * * */
2976 #if 0
2977 int	stack_push_builtin_cmd_alias (const char *name)
2978 {
2979 	Symbol *item, *sym;
2980 	int	cnt = 0, loc = 0;
2981 
2982 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
2983 	if (!item || cnt >= 0)
2984 	{
2985 	    item = make_new_Symbol(name);
2986 	    add_to_array((array *)&globals, (array_item *)item);
2987 	}
2988 
2989 	sym = make_new_Symbol(name);
2990 	sym->builtin_command = item->builtin_command;
2991 	sym->saved = item->saved;
2992 	sym->saved_hint = SAVED_BUILTIN_CMD;
2993 	item->saved = sym;
2994 	return 0;
2995 }
2996 
2997 int	stack_pop_builtin_cmd_alias (const char *name)
2998 {
2999 	Symbol *item, *sym, *s, *n;
3000 	int	cnt = 0, loc = 0;
3001 
3002 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
3003 	if (!item || cnt >= 0)
3004 		return -1;
3005 
3006 	for (sym = item; sym; sym = sym->saved)
3007 	{
3008 		if (sym->saved && sym->saved->saved_hint == SAVED_BUILTIN_VAR)
3009 			break;
3010 	}
3011 	if (!sym)
3012 		return -1;
3013 
3014 	s = sym->saved;
3015 	n = sym->saved->saved;
3016 	item->builtin_command = s->builtin_command;
3017 	s->builtin_command = NULL;
3018 
3019 	if (GC_symbol(s, NULL, -1))
3020 		sym->saved = n;
3021 	GC_symbol(item, (array *)&globals, loc);
3022 	return 0;
3023 }
3024 
3025 int	stack_list_builtin_cmd_alias (const char *name)
3026 {
3027 	Symbol *item, *sym;
3028 	int	counter = 0;
3029 
3030 	if (!(item = lookup_symbol(name)))
3031 		return -1;
3032 
3033 	for (sym = item->saved; sym; sym = sym->saved)
3034 	{
3035 	    if (sym->saved_hint == SAVED_BUILTIN_CMD)
3036 	    {
3037 		if (sym->builtin_command == NULL)
3038 		    say("\t%s\t<Placeholder>", sym->name);
3039 		else
3040 		    say("\t%s\t%p", sym->name, sym->builtin_command);
3041 		counter++;
3042 	    }
3043 	}
3044 
3045 	if (counter)
3046 		return 0;
3047 	return -1;
3048 }
3049 #endif
3050 
3051 /* * * */
stack_push_builtin_func_alias(const char * name)3052 int	stack_push_builtin_func_alias (const char *name)
3053 {
3054 	Symbol *item, *sym;
3055 	int	cnt = 0, loc = 0;
3056 
3057 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
3058 	if (!item || cnt >= 0)
3059 	{
3060 	    item = make_new_Symbol(name);
3061 	    add_to_array((array *)&globals, (array_item *)item);
3062 	}
3063 
3064 	sym = make_new_Symbol(name);
3065 	sym->builtin_function = item->builtin_function;
3066 	sym->saved = item->saved;
3067 	sym->saved_hint = SAVED_BUILTIN_FUNCTION;
3068 	item->saved = sym;
3069 	return 0;
3070 }
3071 
stack_pop_builtin_function_alias(const char * name)3072 int	stack_pop_builtin_function_alias (const char *name)
3073 {
3074 	Symbol *item, *sym, *s, *n;
3075 	int	cnt = 0, loc = 0;
3076 
3077 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
3078 	if (!item || cnt >= 0)
3079 		return -1;
3080 
3081 	for (sym = item; sym; sym = sym->saved)
3082 	{
3083 		if (sym->saved && sym->saved->saved_hint == SAVED_BUILTIN_FUNCTION)
3084 			break;
3085 	}
3086 	if (!sym)
3087 		return -1;
3088 
3089 	s = sym->saved;
3090 	n = sym->saved->saved;
3091 	item->builtin_function = s->builtin_function;
3092 	s->builtin_function = NULL;
3093 
3094 	if (GC_symbol(s, NULL, -1))
3095 		sym->saved = n;
3096 	GC_symbol(item, (array *)&globals, loc);
3097 	return 0;
3098 }
3099 
stack_list_builtin_function_alias(const char * name)3100 int	stack_list_builtin_function_alias (const char *name)
3101 {
3102 	Symbol *item, *sym;
3103 	int	counter = 0;
3104 
3105 	if (!(item = lookup_symbol(name)))
3106 		return -1;
3107 
3108 	for (sym = item->saved; sym; sym = sym->saved)
3109 	{
3110 	    if (sym->saved_hint == SAVED_BUILTIN_FUNCTION)
3111 	    {
3112 		if (sym->builtin_function == NULL)
3113 		    say("\t%s\t<Placeholder>", sym->name);
3114 		else
3115 		    say("\t%s\t%p", sym->name, sym->builtin_function);
3116 		counter++;
3117 	    }
3118 	}
3119 
3120 	if (counter)
3121 		return 0;
3122 	return -1;
3123 }
3124 
3125 /* * * */
stack_push_builtin_expando_alias(const char * name)3126 int	stack_push_builtin_expando_alias (const char *name)
3127 {
3128 	Symbol *item, *sym;
3129 	int	cnt = 0, loc = 0;
3130 
3131 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
3132 	if (!item || cnt >= 0)
3133 	{
3134 	    item = make_new_Symbol(name);
3135 	    add_to_array((array *)&globals, (array_item *)item);
3136 	}
3137 
3138 	sym = make_new_Symbol(name);
3139 	sym->builtin_expando = item->builtin_expando;
3140 	sym->saved = item->saved;
3141 	sym->saved_hint = SAVED_BUILTIN_EXPANDO;
3142 	item->saved = sym;
3143 	return 0;
3144 }
3145 
stack_pop_builtin_expando_alias(const char * name)3146 int	stack_pop_builtin_expando_alias (const char *name)
3147 {
3148 	Symbol *item, *sym, *s, *n;
3149 	int	cnt = 0, loc = 0;
3150 
3151 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
3152 	if (!item || cnt >= 0)
3153 		return -1;
3154 
3155 	for (sym = item; sym; sym = sym->saved)
3156 	{
3157 		if (sym->saved && sym->saved->saved_hint == SAVED_BUILTIN_EXPANDO)
3158 			break;
3159 	}
3160 	if (!sym)
3161 		return -1;
3162 
3163 	s = sym->saved;
3164 	n = sym->saved->saved;
3165 	item->builtin_expando = s->builtin_expando;
3166 	s->builtin_expando = NULL;
3167 
3168 	if (GC_symbol(s, NULL, -1))
3169 		sym->saved = n;
3170 	GC_symbol(item, (array *)&globals, loc);
3171 	return 0;
3172 }
3173 
stack_list_builtin_expando_alias(const char * name)3174 int	stack_list_builtin_expando_alias (const char *name)
3175 {
3176 	Symbol *item, *sym;
3177 	int	counter = 0;
3178 
3179 	if (!(item = lookup_symbol(name)))
3180 		return -1;
3181 
3182 	for (sym = item->saved; sym; sym = sym->saved)
3183 	{
3184 	    if (sym->saved_hint == SAVED_BUILTIN_EXPANDO)
3185 	    {
3186 		if (sym->builtin_expando == NULL)
3187 		    say("\t%s\t<Placeholder>", sym->name);
3188 		else
3189 		    say("\t%s\t%p", sym->name, sym->builtin_expando);
3190 		counter++;
3191 	    }
3192 	}
3193 
3194 	if (counter)
3195 		return 0;
3196 	return -1;
3197 }
3198 
3199 
3200 /* * * */
stack_push_builtin_var_alias(const char * name)3201 int	stack_push_builtin_var_alias (const char *name)
3202 {
3203 	Symbol *item, *sym;
3204 	int	cnt = 0, loc = 0;
3205 
3206 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
3207 	if (!item || cnt >= 0)
3208 	{
3209 	    item = make_new_Symbol(name);
3210 	    add_to_array((array *)&globals, (array_item *)item);
3211 	}
3212 
3213 	sym = make_new_Symbol(name);
3214 	sym->builtin_variable = clone_biv(item->builtin_variable);
3215 	sym->saved = item->saved;
3216 	sym->saved_hint = SAVED_BUILTIN_VAR;
3217 	item->saved = sym;
3218 	return 0;
3219 }
3220 
stack_pop_builtin_var_alias(const char * name)3221 int	stack_pop_builtin_var_alias (const char *name)
3222 {
3223 	Symbol *item, *sym, *s, *n;
3224 	int	cnt = 0, loc = 0;
3225 
3226 	item = (Symbol *)find_array_item((array *)&globals, name, &cnt, &loc);
3227 	if (!item || cnt >= 0)
3228 		return -1;
3229 
3230 	for (sym = item; sym; sym = sym->saved)
3231 	{
3232 		if (sym->saved && sym->saved->saved_hint == SAVED_BUILTIN_VAR)
3233 			break;
3234 	}
3235 	if (!sym)
3236 		return -1;
3237 
3238 	s = sym->saved;
3239 	n = sym->saved->saved;
3240 	unclone_biv(name, s->builtin_variable);
3241 	s->builtin_variable = NULL;
3242 
3243 	if (GC_symbol(s, NULL, -1))
3244 		sym->saved = n;
3245 	GC_symbol(item, (array *)&globals, loc);
3246 	return 0;
3247 }
3248 
stack_list_builtin_var_alias(const char * name)3249 int	stack_list_builtin_var_alias (const char *name)
3250 {
3251 	Symbol *item, *sym;
3252 	int	counter = 0;
3253 
3254 	if (!(item = lookup_symbol(name)))
3255 		return -1;
3256 
3257 	for (sym = item->saved; sym; sym = sym->saved)
3258 	{
3259 	    if (sym->saved_hint == SAVED_BUILTIN_VAR)
3260 	    {
3261 		if (sym->builtin_variable == NULL)
3262 		    say("\t%s\t<Placeholder>", sym->name);
3263 		else
3264 		{
3265 		    char *s;
3266 		    s = make_string_var_bydata((const void *)sym->builtin_variable);
3267 		    if (!s)
3268 			s = malloc_strdup("<EMPTY>");
3269 
3270 		    say("\t%s\t%s", sym->name, s);
3271 		    new_free(&s);
3272 		}
3273 		counter++;
3274 	    }
3275 	}
3276 
3277 	if (counter)
3278 		return 0;
3279 	return -1;
3280 }
3281 
3282 /*
3283  * Here's the plan.  An all-encompasing low-level symbol manipulation thingee.
3284  * 	This interface is not intended to replace $aliasctl(), but rather to
3285  *	supplement it.  No, $aliasctl() will never be removed.  Yes, much of
3286  *	its functionality (but not all) is duplicated here.
3287  *
3288  * $symbolctl(TYPES)
3289  *	Return all of the types supported in this version of EPIC:
3290  *	ALIAS			ASSIGN			BUILTIN_COMMAND
3291  *	BUILTIN_FUNCTION	BUILTIN_EXPANDO		BUILTIN_VARIABLE
3292  *
3293  * $symbolctl(PMATCH <type> <pattern>)
3294  *	Return all symbols of type <type> that match <pattern>.  You can use
3295  *	the special value "*" for <type> to get symbols of all types.
3296  *
3297  * $symbolctl(CREATE <symbol>)
3298  *	Ensure that <symbol> exists in the global symbol table.  When symbols
3299  *	are first created, they do not contain any actual values, but rather
3300  *	act as a placeholder in case you want to set any.  You must ensure
3301  *	that a symbol exists before you try to change its values.  CREATEing
3302  *	a symbol that already exists is harmless; feel free to do it.
3303  *
3304  * $symbolctl(DELETE <symbol>)
3305  * $symbolctl(DELETE <symbol> <type>)
3306  *	Delete all of the values of a particular symbol, or just one type.
3307  *	Example: $symbolctl(DELETE booya ALIAS) is the same as /alias -booya
3308  *	Warning: You can delete built in variables/functions/etc with this!
3309  *	         There's no way to restore them back if you do!  Caution!
3310  *
3311  * $symbolctl(CHECK <symbol>)
3312  *	Inspects <symbol> to see if it has any values left.  If there are no
3313  *	values left for <symbol>, it is removed from the global symbol table.
3314  *	You must then CREATE it again if you want to use it later.
3315  *
3316  * 		*** IMPORTANT NOTE ABOUT "LEVELS" ****
3317  * In order to "get" or "set" a symbol's values, the symbol needs to exist.
3318  * If you try to "get" or "set" a symbol that doesn't exist, $symbolctl() will
3319  * return the empty string to tell you that it failed.  You need to use the
3320  * CREATE operation above to bootstrap a new symbol before using it.
3321  *
3322  * Now, /STACK PUSH and /STACK POP work by manipulating "levels" in the symbol
3323  * table.  By rule, <level> == 1 always refers to the "current" value of a
3324  * symbol.  If you do /STACK PUSH, then the value you pushed will be copied to
3325  * <level> == 2.  If you /STACK PUSH something else, that values moves to
3326  * <level> == 3.  So what you can do is use "GET x LEVELS" to find out how
3327  * many levels a symbol has, and then use "GET x <num>" to find out if there
3328  * is a symbol type that interest you at that level.  IN THIS WAY you can
3329  * directly manipulate the /stack push values without having to actually use
3330  * the /stack command.
3331  *
3332  * In general, <level> is always 1 for everything you want to do, unless you
3333  * are intentionally monkeying around with your /stack values.
3334  *
3335  *	    *** NOW BACK TO YOUR REGULARLY SCHEDULED HELP ***
3336  * $symbolctl(GET <symbol> LEVELS)
3337  *	Return the number of levels of <symbol> that are /STACKed.  This
3338  *	value is always 1 unless you have /STACK PUSHed something.
3339  * $symbolctl(GET <symbol> <level>)
3340  *	Return all of the <type>s that are defined for <symbol> at <level>.
3341  *	If <level> is 1, it gets the current value(s).  If <level> is > 1,
3342  *	it starts looking at the /STACK PUSHed values.
3343  *
3344  * $symbolctl(GET <symbol> <level> ALIAS VALUE)
3345  * $symbolctl(GET <symbol> <level> ALIAS STUB)
3346  * $symbolctl(GET <symbol> <level> ALIAS PACKAGE)
3347  * $symbolctl(GET <symbol> <level> ALIAS ARGLIST)
3348  *	Retrieve one of the values for one of your aliases
3349  *
3350  * $symbolctl(GET <symbol> <level> ASSIGN VALUE)
3351  * $symbolctl(GET <symbol> <level> ASSIGN STUB)
3352  * $symbolctl(GET <symbol> <level> ASSIGN PACKAGE)
3353  *	Retrieve one of the values for one of your assigns
3354  *
3355  * $symbolctl(GET <symbol> <level> BUILTIN_COMMAND)
3356  * $symbolctl(GET <symbol> <level> BUILTIN_FUNCTION)
3357  * $symbolctl(GET <symbol> <level> BUILTIN_EXPANDO)
3358  *	Returns 0 if these types are not in use (ie, if there is not a built
3359  *	in command), and returns non-zero if there is.  If you're smart, you
3360  *	won't try to do anything with the non-zero value.
3361  *
3362  * $symbolctl(GET <symbol> <level> BUILTIN_VARIABLE TYPE)
3363  * $symbolctl(GET <symbol> <level> BUILTIN_VARIABLE DATA)
3364  * $symbolctl(GET <symbol> <level> BUILTIN_VARIABLE BUILTIN)
3365  * $symbolctl(GET <symbol> <level> BUILTIN_VARIABLE SCRIPT)
3366  * $symbolctl(GET <symbol> <level> BUILTIN_VARIABLE FLAGS)
3367  *	Retrieve information about a /SET.
3368  *	The "TYPE" is one of "STR", "INT", "BOOL", or "CHAR"
3369  *	Generally, either "BUILTIN" or "SCRIPT" is set, but not both.
3370  *
3371  * $symbolctl(SET <symbol> <level> ALIAS VALUE <string>)
3372  * $symbolctl(SET <symbol> <level> ALIAS STUB <string>)
3373  * $symbolctl(SET <symbol> <level> ALIAS PACKAGE <string>)
3374  * $symbolctl(SET <symbol> <level> ALIAS ARGLIST <string>)
3375  *	Change one of the values for one of your aliases.  If you omit
3376  *	the <string>, it will clear the value.
3377  *
3378  * $symbolctl(SET <symbol> <level> ASSIGN VALUE <string>)
3379  * $symbolctl(SET <symbol> <level> ASSIGN STUB <string>)
3380  * $symbolctl(SET <symbol> <level> ASSIGN PACKAGE <string>)
3381  *	Change one of the values for one of your assigns.  If you omit
3382  *	the <string>, it will clear the value.
3383  *
3384  * $symbolctl(SET <symbol> <level> BUILTIN_VARIABLE)
3385  *	Create a new user-created /SET with default values (type == BOOL,
3386  *	data == OFF, script is <empty>.)
3387  * $symbolctl(SET <symbol> <level> BUILTIN_VARIABLE TYPE <set-type>)
3388  * $symbolctl(SET <symbol> <level> BUILTIN_VARIABLE DATA <string>)
3389  * $symbolctl(SET <symbol> <level> BUILTIN_VARIABLE BUILTIN)
3390  * $symbolctl(SET <symbol> <level> BUILTIN_VARIABLE SCRIPT <code>)
3391  * $symbolctl(SET <symbol> <level> BUILTIN_VARIABLE FLAGS)
3392  *	Change one of the values for one of your /set's.  You cannot change
3393  *	values for system /set's, sorry.  Setting the TYPE value changes the
3394  *	DATA value to a default (<empty> for strings, 0 for everything else)
3395  *	so always set DATA after setting TYPE.
3396  *	Yes, you can change the TYPE of a /set after you create it!
3397  *	It's probably a bad idea to set FLAGS for the present.
3398  */
symbolctl(char * input)3399 char    *symbolctl      (char *input)
3400 {
3401         int     len;
3402         char    *listc;
3403         char    *ret = NULL;
3404 	size_t	clue = 0;
3405         Symbol  *s;
3406 	int	i;
3407 	int	level;
3408 	char	*symbol, *type, *pattern, *attr;
3409 	int	cnt, l;
3410 
3411         GET_FUNC_ARG(listc, input);
3412         len = strlen(listc);
3413 
3414         if (!my_strnicmp(listc, "TYPES", len)) {
3415 	    for (i = 0; symbol_types[i]; i++)
3416 		malloc_strcat_word_c(&ret, space, symbol_types[i], DWORD_DWORDS, &clue);
3417 	    RETURN_MSTR(ret);
3418 
3419         } else if (!my_strnicmp(listc, "PMATCH", len)) {
3420 	    char **names;
3421 	    int	num = 0;
3422 	    int	maxret = 0;
3423 	    int	start = 0;
3424 	    int	rev = 0;
3425 
3426 	    GET_FUNC_ARG(type, input);
3427 	    GET_FUNC_ARG(pattern, input);
3428 	    if (!my_stricmp(type, "ALIAS")) {
3429 		names = pmatch_cmd_alias(pattern, &num, maxret, start, rev);
3430 	    } else if (!my_stricmp(type, "ASSIGN")) {
3431 		names = pmatch_assign_alias(pattern, &num, maxret, start, rev);
3432 	    } else if (!my_stricmp(type, "BUILTIN_COMMAND")) {
3433 		names = pmatch_builtin_commands(pattern, &num, maxret, start, rev);
3434 	    } else if (!my_stricmp(type, "BUILTIN_FUNCTION")) {
3435 		names = pmatch_builtin_functions(pattern, &num, maxret, start, rev);
3436 	    } else if (!my_stricmp(type, "BUILTIN_EXPANDO")) {
3437 		names = pmatch_builtin_expandos(pattern, &num, maxret, start, rev);
3438 	    } else if (!my_stricmp(type, "BUILTIN_VARIABLE")) {
3439 		names = pmatch_builtin_variables(pattern, &num, maxret, start, rev);
3440 	    } else if (!my_stricmp(type, "*")) {
3441 		names = pmatch_any_symbol(pattern, &num, maxret, start, rev);
3442 	    } else
3443 		RETURN_EMPTY;
3444 
3445 	    for (i = 0; i < num; i++)
3446 		malloc_strcat_word_c(&ret, space, names[i], DWORD_DWORDS, &clue);
3447 	    new_free((char **)&names);
3448 	    RETURN_MSTR(ret);
3449 
3450         } else if (!my_strnicmp(listc, "CREATE", len)) {
3451             GET_FUNC_ARG(symbol, input);
3452 	    upper(symbol);
3453 	    s = (Symbol *)find_array_item((array *)&globals, symbol, &cnt, &l);
3454 	    if (!s || cnt >= 0)
3455 	    {
3456 		s = make_new_Symbol(symbol);
3457 		add_to_array((array *)&globals, (array_item *)s);
3458 		RETURN_INT(1);
3459 	    }
3460 	    RETURN_INT(0);
3461 
3462         } else if (!my_strnicmp(listc, "DELETE", len)) {
3463             GET_FUNC_ARG(symbol, input);
3464 	    upper(symbol);
3465 	    s = (Symbol *)find_array_item((array *)&globals, symbol, &cnt, &l);
3466 	    if (s && cnt < 0) {
3467 		int	all = 0;
3468 
3469 		if (!input || !*input)
3470 		    all = 1;
3471 		if (all || !my_stricmp(input, "ASSIGN")) {
3472 		    new_free(&s->user_variable);
3473 		    s->user_variable_stub = 0;
3474 		    new_free(&s->user_variable_package);
3475 		}
3476 		if (all || !my_stricmp(input, "ALIAS")) {
3477 		    new_free(&s->user_command);
3478 		    s->user_command_stub = 0;
3479 		    new_free(&s->user_command_package);
3480 		    destroy_arglist(&s->arglist);
3481 		}
3482 		if (all || !my_stricmp(input, "BUILTIN_COMMAND"))
3483 		    s->builtin_command = NULL;
3484 		if (all || !my_stricmp(input, "BUILTIN_FUNCTION"))
3485 		    s->builtin_function = NULL;
3486 		if (all || !my_stricmp(input, "BUILTIN_EXPANDO"))
3487 		    s->builtin_expando = NULL;
3488 		if (all || !my_stricmp(input, "BUILTIN_VARIABLE")) {
3489 		    if (s->builtin_variable && is_var_builtin(symbol))
3490 			RETURN_INT(0);
3491 		    if (s->builtin_variable) {
3492 			new_free(&s->builtin_variable->script);
3493 			if (s->builtin_variable->type == STR_VAR)
3494 				new_free(&s->builtin_variable->data->string);
3495 			new_free(&s->builtin_variable->data);
3496 			new_free(&s->builtin_variable);
3497 		    }
3498 		}
3499 		GC_symbol(s, (array *)&globals, l);
3500 		RETURN_INT(1);
3501 	    }
3502 	    RETURN_INT(0);
3503 
3504         } else if (!my_strnicmp(listc, "CHECK", len)) {
3505             GET_FUNC_ARG(symbol, input);
3506 	    upper(symbol);
3507 	    s = (Symbol *)find_array_item((array *)&globals, symbol, &cnt, &l);
3508 	    if (s && cnt < 0) {
3509 		GC_symbol(s, (array *)&globals, l);
3510 		RETURN_INT(1);
3511 	    }
3512 	    RETURN_INT(0);
3513 
3514         } else if (!my_strnicmp(listc, "GET", len)) {
3515 	    char *x;
3516 
3517             GET_FUNC_ARG(symbol, input);
3518 	    upper(symbol);
3519 	    s = (Symbol *)find_array_item((array *)&globals, symbol, &cnt, &l);
3520 	    if (!s || cnt >= 0)
3521                 RETURN_EMPTY;
3522 
3523 	    GET_FUNC_ARG(x, input)
3524 	    if (!(my_stricmp(x, "LEVELS"))) {
3525 		i = 1;
3526 		while (s->saved)
3527 		    i++, s = s->saved;
3528 		RETURN_INT(i);
3529 	    }
3530 
3531 	    GET_INT_ARG(level, x);
3532 	    for (i = 1; i < level; i++)
3533 	    {
3534 		if (!s->saved)
3535 		    RETURN_EMPTY;
3536 		s = s->saved;
3537 	    }
3538 
3539 	    if (!input || !*input) {
3540 		if (s->user_variable || s->user_variable_stub)
3541 		    malloc_strcat_word_c(&ret, space, "ASSIGN", DWORD_NO, &clue);
3542 		if (s->user_command || s->user_command_stub)
3543 		    malloc_strcat_word_c(&ret, space, "ALIAS", DWORD_NO, &clue);
3544 		if (s->builtin_command)
3545 		    malloc_strcat_word_c(&ret, space, "BUILTIN_COMMAND", DWORD_NO, &clue);
3546 		if (s->builtin_function)
3547 		    malloc_strcat_word_c(&ret, space, "BUILTIN_FUNCTION", DWORD_NO, &clue);
3548 		if (s->builtin_expando)
3549 		    malloc_strcat_word_c(&ret, space, "BUILTIN_EXPANDO", DWORD_NO, &clue);
3550 		if (s->builtin_variable)
3551 		    malloc_strcat_word_c(&ret, space, "BUILTIN_VARIABLE", DWORD_NO, &clue);
3552 		RETURN_MSTR(ret);
3553 	    }
3554 
3555 	    GET_FUNC_ARG(type, input);
3556             if (!my_stricmp(type, "ALIAS")) {
3557 		GET_FUNC_ARG(attr, input);
3558 		if (!my_stricmp(attr, "VALUE"))
3559 		    RETURN_STR(s->user_command);
3560 		else if (!my_stricmp(attr, "STUB"))
3561 		    RETURN_INT(s->user_command_stub);
3562 		else if (!my_stricmp(attr, "PACKAGE"))
3563 		    RETURN_STR(s->user_command_package);
3564 		else if (!my_stricmp(attr, "ARGLIST"))
3565 		    RETURN_MSTR(print_arglist(s->arglist));
3566 		else
3567 		    RETURN_EMPTY;
3568             } else if (!my_stricmp(type, "ASSIGN")) {
3569 		GET_FUNC_ARG(attr, input);
3570        		if (!my_stricmp(attr, "VALUE"))
3571 		    RETURN_STR(s->user_variable);
3572 		else if (!my_stricmp(attr, "STUB"))
3573 		    RETURN_INT(s->user_variable_stub);
3574 		else if (!my_stricmp(attr, "PACKAGE"))
3575 		    RETURN_STR(s->user_variable_package);
3576 		else
3577 		    RETURN_EMPTY;
3578 	    } else if (!my_stricmp(type, "BUILTIN_COMMAND")) {
3579 		RETURN_INT((long)s->builtin_command);
3580             } else if (!my_stricmp(type, "BUILTIN_FUNCTION")) {
3581 		RETURN_INT((long)s->builtin_function);
3582             } else if (!my_stricmp(type, "BUILTIN_EXPANDO")) {
3583 		RETURN_INT((long)s->builtin_expando);
3584             } else if (!my_stricmp(type, "BUILTIN_VARIABLE")) {
3585 		if (!s->builtin_variable)
3586 			RETURN_EMPTY;
3587 
3588 		GET_FUNC_ARG(attr, input);
3589        		if (!my_stricmp(attr, "TYPE")) {
3590 		    switch (s->builtin_variable->type) {
3591 			case STR_VAR: RETURN_STR("STR");
3592 			case INT_VAR: RETURN_STR("INT");
3593 			case BOOL_VAR: RETURN_STR("BOOL");
3594 			case CHAR_VAR: RETURN_STR("CHAR");
3595 			default: RETURN_STR("???");
3596 		    }
3597 		} else if (!my_stricmp(attr, "DATA"))
3598 		    RETURN_MSTR(make_string_var_bydata((const void *)s->builtin_variable));
3599 		else if (!my_stricmp(attr, "FUNC"))
3600 		    RETURN_INT((long)s->builtin_variable->func);
3601 		else if (!my_stricmp(attr, "SCRIPT"))
3602 		    RETURN_STR(s->builtin_variable->script);
3603 		else if (!my_stricmp(attr, "FLAGS"))
3604 		    RETURN_INT(s->builtin_variable->pending);
3605 		else if (!my_stricmp(attr, "PENDING"))
3606 		    RETURN_INT(s->builtin_variable->pending);
3607 		else
3608 		    RETURN_EMPTY;
3609 	    } else
3610 		RETURN_EMPTY;
3611         } else if (!my_strnicmp(listc, "SET", len)) {
3612 	    char *x;
3613 
3614             GET_FUNC_ARG(symbol, input);
3615 	    upper(symbol);
3616 	    s = (Symbol *)find_array_item((array *)&globals, symbol, &cnt, &l);
3617 	    if (!s || cnt >= 0)
3618                 RETURN_EMPTY;
3619 
3620 	    GET_FUNC_ARG(x, input)
3621 	    GET_INT_ARG(level, x);
3622 	    for (i = 1; i < level; i++)
3623 	    {
3624 		if (!s->saved)
3625 		    RETURN_EMPTY;
3626 		s = s->saved;
3627 	    }
3628 
3629 	    GET_FUNC_ARG(type, input);
3630             if (!my_stricmp(type, "ALIAS")) {
3631 		GET_FUNC_ARG(attr, input);
3632 		if (!my_stricmp(attr, "VALUE")) {
3633 		    if (input && *input)
3634 		        malloc_strcpy(&s->user_command, input);
3635 		    else
3636 			new_free(&s->user_command);
3637 		    RETURN_INT(1);
3638 		} else if (!my_stricmp(attr, "STUB")) {
3639 		    if (is_number(input)) {
3640 		        s->user_command_stub = my_atol(input);
3641 			RETURN_INT(1);
3642 		    }
3643 		    RETURN_EMPTY;
3644 		} else if (!my_stricmp(attr, "PACKAGE")) {
3645 		    if (input && *input)
3646 		        malloc_strcpy(&s->user_command_package, input);
3647 		    else
3648 			new_free(&s->user_command_package);
3649 		    RETURN_INT(1);
3650 		} else if (!my_stricmp(attr, "ARGLIST")) {
3651 		    destroy_arglist(&s->arglist);
3652 		    if (input && *input)
3653 		    	s->arglist = parse_arglist(input);
3654 		    RETURN_INT(1);
3655 		} else
3656 		    RETURN_EMPTY;
3657             } else if (!my_stricmp(type, "ASSIGN")) {
3658 		GET_FUNC_ARG(attr, input);
3659 		if (!my_stricmp(attr, "VALUE")) {
3660 		    if (input && *input)
3661 		        malloc_strcpy(&s->user_variable, input);
3662 		    else
3663 			new_free(&s->user_variable);
3664 		    RETURN_INT(1);
3665 		} else if (!my_stricmp(attr, "STUB")) {
3666 		    if (is_number(input)) {
3667 		        s->user_variable_stub = my_atol(input);
3668 			RETURN_INT(1);
3669 		    }
3670 		    RETURN_EMPTY;
3671 		} else if (!my_stricmp(attr, "PACKAGE")) {
3672 		    if (input && *input)
3673 		        malloc_strcpy(&s->user_variable_package, input);
3674 		    else
3675 			new_free(&s->user_variable_package);
3676 		    RETURN_INT(1);
3677 		} else
3678 		    RETURN_EMPTY;
3679 	    } else if (!my_stricmp(type, "BUILTIN_COMMAND")) {
3680 		RETURN_EMPTY;
3681             } else if (!my_stricmp(type, "BUILTIN_FUNCTION")) {
3682 		RETURN_EMPTY;
3683             } else if (!my_stricmp(type, "BUILTIN_EXPANDO")) {
3684 		RETURN_EMPTY;
3685             } else if (!my_stricmp(type, "BUILTIN_VARIABLE")) {
3686 		IrcVariable *v;
3687 
3688 		if (s->builtin_variable) {
3689 		    /* Not permitted to change a builtin variable */
3690 		    if (s->builtin_variable->func)
3691 			RETURN_EMPTY;
3692 		    v = s->builtin_variable;
3693 		} else {
3694 		    v = (IrcVariable *)new_malloc(sizeof(IrcVariable));
3695 		    v->type = BOOL_VAR;
3696 		    v->data = new_malloc(sizeof(union builtin_variable));
3697 		    v->data->integer = 0;
3698 		    v->pending = 0;
3699 		    v->func = NULL;
3700 		    v->script = NULL;
3701 		    add_builtin_variable_alias(symbol, v);
3702 		}
3703 
3704 		GET_FUNC_ARG(attr, input);
3705        		if (!my_stricmp(attr, "TYPE")) {
3706 		    int newval;
3707 
3708 		    if (!input)
3709 			RETURN_EMPTY;
3710 
3711 		    if (!my_stricmp(input, "BOOL")) 	 newval = BOOL_VAR;
3712 		    else if (!my_stricmp(input, "STR"))  newval = STR_VAR;
3713 		    else if (!my_stricmp(input, "INT"))  newval = INT_VAR;
3714 		    else if (!my_stricmp(input, "CHAR")) newval = CHAR_VAR;
3715 		    else
3716 			RETURN_EMPTY;
3717 
3718 		    if (v->type == STR_VAR)
3719 			new_free(&v->data->string);
3720 		    if (newval == STR_VAR)
3721 			v->data->string = NULL;
3722 		    else
3723 			v->data->integer = 0;
3724 
3725 		    v->type = newval;
3726 		    RETURN_INT(1);
3727 		} else if (!my_stricmp(attr, "DATA")) {
3728 		    if (!*input)
3729 			input = NULL;
3730 		    RETURN_INT(set_variable(symbol, v, input, 0));
3731 		} else if (!my_stricmp(attr, "FUNC")) {
3732 		    RETURN_EMPTY;	/* Can't do this */
3733 		} else if (!my_stricmp(attr, "SCRIPT")) {
3734 		    if (input && *input)
3735 			malloc_strcpy(&v->script, input);
3736 		    else
3737 			new_free(&v->script);
3738 		    RETURN_INT(1);
3739 		} else if (!my_stricmp(attr, "FLAGS")) {
3740 		    if (!is_number(input))
3741 			RETURN_EMPTY;
3742 		    v->pending = my_atol(input);
3743 		    RETURN_INT(1);
3744 		} else if (!my_stricmp(attr, "PENDING")) {
3745 		    if (!is_number(input))
3746 			RETURN_EMPTY;
3747 		    v->pending = my_atol(input);
3748 		    RETURN_INT(1);
3749 		} else
3750 		    RETURN_EMPTY;
3751 	    } else
3752 		RETURN_EMPTY;
3753 	} else
3754 	    RETURN_EMPTY;
3755 }
3756 
3757 /* Pure fantasy for now. */
3758 #if 0
3759 /* Statements are either blocks, expressions, or commands */
3760 enum StatementTypeE {
3761 	BLOCK_STATEMENT,
3762 	EXPR_STATEMENT,
3763 	CMD_STATEMENT
3764 };
3765 
3766 /* A block is a collection of statements */
3767 struct BlockT {
3768 	size_t	numcmds;
3769 	union StatementT *cmds;
3770 };
3771 
3772 /* A command statement has a command, and an argument list */
3773 struct CommandStatementT {
3774 	enum StatementTypeE type;
3775 	wchar_t *	cmd;
3776 	wchar_t *	args;
3777 };
3778 
3779 /* An expression statement has a math expression */
3780 struct ExpressionStatementT {
3781 	enum StatementTypeE type;
3782 	wchar_t *	expr;
3783 };
3784 
3785 /* A block statement has a block (natch) */
3786 struct BlockStatementT {
3787 	enum StatementTypeE type;
3788 	struct BlockT 	block;
3789 };
3790 
3791 /*
3792  * A statement is either a command statement,
3793  * an expression statement, or a block statement.
3794  */
3795 union StatementT {
3796 	enum StatementTypeE type;
3797 	struct CommandStatementT;
3798 	struct ExpressionStatementT;
3799 	struct BlockStatementT;
3800 #endif
3801 
3802