1 /*
2  * if.c: handles the IF command for IRCII
3  *
4  * Written By Michael Sandrof
5  *
6  * Copyright(c) 1990, 1991
7  *
8  * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
9  */
10 
11 
12 #include "irc.h"
13 static char cvsrevision[] = "$Id: if.c 513 2014-10-27 12:05:16Z keaston $";
CVS_REVISION(if_c)14 CVS_REVISION(if_c)
15 #include "struct.h"
16 
17 #include "alias.h"
18 #include "ircaux.h"
19 #include "window.h"
20 #include "vars.h"
21 #include "output.h"
22 #include "if.h"
23 #include "commands.h"
24 #include "misc.h"
25 #define MAIN_SOURCE
26 #include "modval.h"
27 
28 /*
29  * next_expr finds the next expression delimited by brackets. The type
30  * of bracket expected is passed as a parameter. Returns NULL on error.
31  */
32 char	*my_next_expr(char **args, char type, int whine)
33 {
34 	char	*ptr,
35 		*ptr2,
36 		*ptr3;
37 
38 	if (!*args)
39 		return NULL;
40 	ptr2 = *args;
41 	if (!*ptr2)
42 		return 0;
43 	if (*ptr2 != type)
44 	{
45 		if (whine)
46 			say("Expression syntax");
47 		return 0;
48 	}							/* { */
49 	ptr = MatchingBracket(ptr2 + 1, type, (type == '(') ? ')' : '}');
50 	if (!ptr)
51 	{
52 		say("Unmatched '%c'", type);
53 		return 0;
54 	}
55 	*ptr = '\0';
56 
57 	do
58 		ptr2++;
59 	while (my_isspace(*ptr2));
60 
61 	ptr3 = ptr+1;
62 	while (my_isspace(*ptr3))
63 		ptr3++;
64 	*args = ptr3;
65 	if (*ptr2)
66 	{
67 		ptr--;
68 		while (my_isspace(*ptr))
69 			*ptr-- = '\0';
70 	}
71 	return ptr2;
72 }
73 
next_expr_failok(char ** args,char type)74 extern char *next_expr_failok (char **args, char type)
75 {
76 	return my_next_expr (args, type, 0);
77 }
78 
next_expr(char ** args,char type)79 extern char *next_expr (char **args, char type)
80 {
81 	return my_next_expr (args, type, 1);
82 }
83 
84 /*
85  * All new /if command -- (jfn, 1997)
86  *
87  * Here's the plan:
88  *
89  *		if (expr) ......
90  *		if (expr) {......}
91  *		if (expr) {......} {......}
92  *		if (expr) {......} else {......}
93  *		if (expr) {......} elsif (expr2) {......}
94  * etc.
95  */
96 
BUILT_IN_COMMAND(ifcmd)97 BUILT_IN_COMMAND(ifcmd)
98 {
99 	int unless_cmd;
100 	char *current_expr;
101 	char *current_expr_val;
102 	int result;
103 	char *current_line = NULL;
104 	int flag = 0;
105 
106 	unless_cmd = (*command == 'U');
107 	if (!subargs)
108 		subargs = empty_string;
109 
110 	while (args && *args)
111 	{
112 		while (my_isspace(*args))
113 			args++;
114 
115 		current_expr = next_expr(&args, '(');
116 		if (!current_expr)
117 		{
118 			error("IF: Missing expression");
119 			return;
120 		}
121 		current_expr_val = parse_inline(current_expr, subargs, &flag);
122 		if (internal_debug & DEBUG_EXPANSIONS && !in_debug_yell)
123 			debugyell("%s expression expands to: (%s)", command, current_expr_val);
124 		result = check_val(current_expr_val);
125 		new_free(&current_expr_val);
126 
127 		if (*args == '{')
128 		{
129 			current_line = next_expr(&args, '{');
130 		}
131 		else
132 			current_line = args, args = NULL;
133 
134 		/* If the expression was FALSE for IF, and TRUE for UNLESS */
135 		if (unless_cmd == result)
136 		{
137 			if (args)
138 			{
139 				if (!my_strnicmp(args, "elsif ", 6))
140 				{
141 					args += 6;
142 					continue;
143 				}
144 				else if (!my_strnicmp(args, "else ", 5))
145 					args += 5;
146 
147 				while (my_isspace(*args))
148 					args++;
149 
150 				if (*args == '{')
151 					current_line = next_expr(&args, '{');
152 				else
153 					current_line = args, args = NULL;
154 
155 			}
156 			else
157 				current_line = NULL;
158 		}
159 
160 		if (current_line && *current_line)
161 			parse_line(NULL, current_line, subargs, 0, 0, 1);
162 
163 		break;
164 	}
165 }
166 
BUILT_IN_COMMAND(docmd)167 BUILT_IN_COMMAND(docmd)
168 {
169 	char *body, *expr, *cmd, *ptr;
170 	char *newexp = NULL;
171 	int args_used = 0;
172 	int result;
173 
174 	if (*args == '{')
175 	{
176 		if (!(body = next_expr(&args, '{')))
177 		{
178 			error("DO: unbalanced {");
179 			return;
180 		}
181 		if (args && *args && (cmd = next_arg(args, &args)) &&
182 		     !my_stricmp (cmd, "while"))
183 		{
184 			if (!(expr = next_expr(&args, '(')))
185 			{
186 				error("DO: unbalanced (");
187 				return;
188 			}
189 			will_catch_break_exceptions++;
190 			will_catch_return_exceptions++;
191 
192 			while (1)
193 			{
194 				parse_line (NULL, body, subargs ? subargs : empty_string, 0, 0, 1);
195 				if (break_exception)
196 				{
197 					break_exception = 0;
198 					break;
199 				}
200 				if (continue_exception)
201 				{
202 					continue_exception = 0;
203 					continue;
204 				}
205 
206 				if (return_exception)
207 					break;
208 				malloc_strcpy(&newexp, expr);
209 				ptr = parse_inline(newexp, subargs ? subargs : empty_string,
210 					&args_used);
211 				result = check_val(ptr);
212 				new_free(&ptr);
213 				if (!result)
214 					break;
215 			}
216 			new_free(&newexp);
217 			will_catch_break_exceptions--;
218 			will_catch_continue_exceptions--;
219 			return;
220 		}
221 		/* falls through to here if its /do {...} */
222 		parse_line (NULL, body, subargs ? subargs : empty_string, 0, 0, 1);
223 	}
224 	/* falls through to here if it its /do ... */
225 	parse_line (NULL, args, subargs ? subargs : empty_string, 0, 0, 1);
226 }
227 
228 /*ARGSUSED*/
BUILT_IN_COMMAND(whilecmd)229 BUILT_IN_COMMAND(whilecmd)
230 {
231 	char	*exp = NULL,
232 		*ptr = NULL,
233 		*body = NULL,
234 		*newexp = NULL;
235 	int	args_used;	/* this isn't used here, but is passed
236 				 * to expand_alias() */
237 	int whileval = !strcmp(command, "WHILE");
238 
239 	if (!(ptr = next_expr(&args, '(')))
240 	{
241 		error("WHILE: missing boolean expression");
242 		return;
243 	}
244 	exp = LOCAL_COPY(ptr);
245 	if ((ptr = next_expr_failok(&args, '{')) == (char *) 0)
246 		ptr = args;
247 
248 	body = LOCAL_COPY(ptr);
249 
250 	will_catch_break_exceptions++;
251 	will_catch_continue_exceptions++;
252 	make_local_stack(NULL);
253 	while (1)
254 	{
255 		newexp = LOCAL_COPY(exp);
256 		ptr = parse_inline(newexp, subargs ? subargs : empty_string, &args_used);
257 		if (check_val(ptr) != whileval)
258 			break;
259 		new_free(&ptr);
260 		parse_line(NULL, body, subargs ?  subargs : empty_string, 0, 0, 0);
261 		if (continue_exception)
262 		{
263 			continue_exception = 0;
264 			continue;
265 		}
266 		if (break_exception)
267 		{
268 			break_exception = 0;
269 			break;
270 		}
271 		if (return_exception)
272 			break;
273 	}
274 	will_catch_break_exceptions--;
275 	will_catch_continue_exceptions--;
276 	destroy_local_stack();
277 	new_free(&ptr);
278 }
279 
BUILT_IN_COMMAND(foreach)280 BUILT_IN_COMMAND(foreach)
281 {
282 	char	*struc = NULL,
283 		*ptr,
284 		*body = NULL,
285 		*var = NULL;
286 	char	**sublist;
287 	int	total;
288 	int	i;
289 	int	slen;
290 	int	old_display;
291 	int     list = VAR_ALIAS;
292 	int	af;
293 
294         while (args && my_isspace(*args))
295         	args++;
296 
297         if (*args == '-')
298                 args++, list = COMMAND_ALIAS;
299 
300 	if ((ptr = new_next_arg(args, &args)) == NULL)
301 	{
302 		error("FOREACH: missing structure expression");
303 		return;
304 	}
305 	struc = upper(remove_brackets(ptr, subargs, &af));
306 
307 	if ((var = next_arg(args, &args)) == NULL)
308 	{
309 		new_free(&struc);
310 		error("FOREACH: missing variable");
311 		return;
312 	}
313 	while (my_isspace(*args))
314 		args++;
315 
316 	if ((body = next_expr(&args, '{')) == NULL)	/* } */
317 	{
318 		new_free(&struc);
319 		error("FOREACH: missing statement");
320 		return;
321 	}
322 
323 	if ((sublist = get_subarray_elements(struc, &total, list)) == NULL)
324 	{
325 		new_free(&struc);
326 		return;		/* Nothing there. */
327 	}
328 
329 	slen=strlen(struc);
330 	old_display=window_display;
331 	make_local_stack(NULL);
332 	for (i=0;i<total;i++)
333 	{
334 		window_display=0;
335 		add_local_alias(var, sublist[i]+slen+1);
336 		window_display=old_display;
337 		parse_line(NULL, body, subargs ? subargs:empty_string, 0, 0, 0);
338 		new_free(&sublist[i]);
339 	}
340 	destroy_local_stack();
341 	new_free((char **)&sublist);
342 	new_free(&struc);
343 }
344 
345 /*
346  * FE:  Written by Jeremy Nelson (jnelson@iastate.edu)
347  *
348  * FE: replaces recursion
349  *
350  * The thing about it is that you can nest variables, as this command calls
351  * expand_alias until the list doesnt change.  So you can nest lists in
352  * lists, and hopefully that will work.  However, it also makes it
353  * impossible to have $s anywhere in the list.  Maybe ill change that
354  * some day.
355  */
356 
BUILT_IN_COMMAND(fe)357 BUILT_IN_COMMAND(fe)
358 {
359 	char    *list = NULL,
360 		*templist = NULL,
361 		*placeholder,
362 		*sa,
363 		*vars,
364         *varmem = NULL,
365 		*var[255],
366 		*word = NULL,
367 		*todo = NULL,
368 		fec_buffer[2] = { 0 };
369 	int     ind, y, args_flag;
370 	int     old_display;
371 	int	doing_fe = !my_stricmp(command, "FE");
372 
373 	list = next_expr(&args, '(');
374 
375 	if (!list)
376 	{
377 		error("%s: Missing List for /%s", command, command);
378 		return;
379 	}
380 
381 	sa = subargs ? subargs : space;
382 
383 	templist = expand_alias(list, sa, &args_flag, NULL);
384 	if (!templist || !*templist)
385 	{
386 		new_free(&templist);
387 		return;
388 	}
389 
390 	vars = args;
391 	if (!(args = strchr(args, '{')))		/* } */
392 	{
393 		error("%s: Missing commands", command);
394 		new_free(&templist);
395 		return;
396 	}
397 
398     /* This is subtle - we have to create a duplicate of the string
399      * containing the var names, in case there's no space between
400      * it and the commands. */
401 	args[0] = '\0';
402     malloc_strcpy(&varmem, vars);
403     vars = varmem;
404     args[0] = '{';
405 
406 	ind = 0;
407 	while ((var[ind] = next_arg(vars, &vars)))
408 	{
409         ind++;
410 
411 		if (ind == 255)
412 		{
413 			error("%s: Too many variables", command);
414 			new_free(&templist);
415             new_free(&varmem);
416 			return;
417 		}
418 	}
419 
420 	if (ind < 1)
421 	{
422 		error("%s: You did not specify any variables", command);
423 		new_free(&templist);
424         new_free(&varmem);
425 		return;
426 	}
427 
428 	if (!(todo = next_expr(&args, '{')))		/* } { */
429 	{
430 		error("%s: Missing }", command);
431 		new_free(&templist);
432         new_free(&varmem);
433 		return;
434 	}
435 
436 	old_display = window_display;
437 
438 	placeholder = templist;
439 
440 	will_catch_break_exceptions++;
441 	will_catch_continue_exceptions++;
442 
443 	make_local_stack(NULL);
444 
445     if (doing_fe) {
446         /* FE */
447         word = new_next_arg(templist, &templist);
448     } else {
449         /* FEC */
450         word = fec_buffer;
451         word[0] = *templist++;
452         if (word[0] == '\0')
453             word = NULL;
454     }
455 
456     while (word)
457     {
458         window_display = 0;
459         for ( y = 0 ; y < ind ; y++ )
460         {
461             if (word) {
462                 add_local_alias(var[y], word);
463 
464                 if (doing_fe) {
465                     /* FE */
466                     word = new_next_arg(templist, &templist);
467                 } else {
468                     /* FEC */
469                     word[0] = *templist++;
470                     if (word[0] == '\0')
471                         word = NULL;
472                 }
473             } else {
474                 add_local_alias(var[y], empty_string);
475             }
476         }
477         window_display = old_display;
478         parse_line(NULL, todo, subargs?subargs:empty_string, 0, 0, 0);
479         if (continue_exception)
480         {
481             continue_exception = 0;
482             continue;
483         }
484         if (break_exception)
485         {
486             break_exception = 0;
487             break;
488         }
489         if (return_exception)
490             break;
491     }
492 
493 	destroy_local_stack();
494 	will_catch_break_exceptions--;
495 	will_catch_continue_exceptions--;
496 
497 	window_display = old_display;
498 	new_free(&placeholder);
499     new_free(&varmem);
500 }
501 
502 /* FOR command..... prototype:
503  *  for (commence,evaluation,iteration)
504  * in the same style of C's for, the for loop is just a specific
505  * type of WHILE loop.
506  *
507  * IMPORTANT: Since ircII uses ; as a delimeter between commands,
508  * commas were chosen to be the delimiter between expressions,
509  * so that semicolons may be used in the expressions (think of this
510  * as the reverse as C, where commas seperate commands in expressions,
511  * and semicolons end expressions.
512  */
513 /*  I suppose someone could make a case that since the
514  *  foreach_handler() routine weeds out any for command that doesnt have
515  *  two commans, that checking for those 2 commas is a waste.  I suppose.
516  */
BUILT_IN_COMMAND(forcmd)517 BUILT_IN_COMMAND(forcmd)
518 {
519 	char        *working        = NULL;
520 	char        *commence       = NULL;
521 	char        *evaluation     = NULL;
522 	char        *lameeval       = NULL;
523 	char        *iteration      = NULL;
524 	char        *sa             = NULL;
525 	int         argsused        = 0;
526 	char        *blah           = NULL;
527 	char        *commands       = NULL;
528 
529 	/* Get the whole () thing */
530 	if ((working = next_expr(&args, '(')) == NULL)	/* ) */
531 	{
532 		error("FOR: missing closing parenthesis");
533 		return;
534 	}
535 	commence = LOCAL_COPY(working);
536 
537 	/* Find the beginning of the second expression */
538 
539 	evaluation = strchr(commence, ',');
540 	if (!evaluation)
541 	{
542 		error("FOR: no components!");
543 		return;
544 	}
545 	do
546 		*evaluation++ = '\0';
547 	while (my_isspace(*evaluation));
548 
549 	/* Find the beginning of the third expression */
550 	iteration = strchr(evaluation, ',');
551 	if (!iteration)
552 	{
553 		error("FOR: Only two components!");
554 		return;
555 	}
556 	do
557 	{
558 		*iteration++ = '\0';
559 	}
560 	while (my_isspace(*iteration));
561 
562 	working = args;
563 	while (my_isspace(*working))
564 		*working++ = '\0';
565 
566 	if ((working = next_expr(&working, '{')) == NULL)		/* } */
567 	{
568 		error("FOR: badly formed commands");
569 		return;
570 	}
571 
572 	make_local_stack(NULL);
573 
574 	commands = LOCAL_COPY(working);
575 
576 	sa = subargs?subargs:empty_string;
577 	parse_line(NULL, commence, sa, 0, 0, 0);
578 
579 	will_catch_break_exceptions++;
580 	will_catch_continue_exceptions++;
581 
582 	while (1)
583 	{
584 		lameeval = LOCAL_COPY(evaluation);
585 
586 		blah = parse_inline(lameeval,sa,&argsused);
587 		if (!check_val(blah))
588 		{
589 			new_free(&blah);
590 			break;
591 		}
592 
593 		new_free(&blah);
594 		parse_line(NULL, commands, sa, 0, 0, 0);
595 		if (break_exception)
596 		{
597 			break_exception = 0;
598 			break;
599 		}
600 		if (continue_exception)
601 			continue_exception = 0;	/* Dont continue here! */
602 		if (return_exception)
603 			break;
604 		parse_line(NULL, iteration, sa, 0, 0, 0);
605 	}
606 
607 	destroy_local_stack();
608 	will_catch_break_exceptions--;
609 	will_catch_continue_exceptions--;
610 
611 	new_free(&blah);
612 }
613 
614 /*
615 
616   Need to support something like this:
617 
618 	switch (text to be matched)
619 	{
620 		(sample text)
621 		{
622 			...
623 		}
624 		(sample text2)
625 		(sample text3)
626 		{
627 			...
628 		}
629 		...
630 	}
631 
632 How it works:
633 
634 	The command is technically made up a single (...) expression and
635 	a single {...} expression.  The (...) expression is taken to be
636 	regular expando-text (much like the (...) body of /fe.
637 
638 	The {...} body is taken to be a series of [(...)] {...} pairs.
639 	The [(...)] expression is taken to be one or more consecutive
640 	(...) structures, which are taken to be text expressions to match
641 	against the header text.  If any of the (...) expressions are found
642 	to match, then the commands in the {...} body are executed.
643 
644 	There may be as many such [(...)] {...} pairs as you need.  However,
645 	only the *first* pair found to be matching is actually executed,
646 	and the others are ignored, so placement of your switches are
647 	rather important:  Put your most general ones last.
648 
649 */
BUILT_IN_COMMAND(switchcmd)650 BUILT_IN_COMMAND(switchcmd)
651 {
652 	char *control, *body, *header, *commands;
653 	int af;
654 	int found_def = 0;
655 	char *def = NULL;
656 
657 	if (!(control = next_expr(&args, '(')))
658 	{
659 		error("SWITCH: String to be matched not found where expected");
660 		return;
661 	}
662 
663 	control = expand_alias(control, subargs, &af, NULL);
664 	if (internal_debug & DEBUG_EXPANSIONS && !in_debug_yell)
665 		debugyell("%s expression expands to: (%s)", command, control);
666 
667 	if (!(body = next_expr(&args, '{')))
668 		error("SWITCH: Execution body not found where expected");
669 
670 	make_local_stack(NULL);
671 	while (body && *body)
672 	{
673 		int hooked = 0;
674 
675 		while (*body == '(')
676 		{
677 			if (!(header = next_expr(&body, '(')))
678 			{
679 				error("SWITCH: Case label not found where expected");
680 				new_free(&control);
681 				return;
682 			}
683 			if (!strcmp(header, "default"))
684 			{
685 				if (def)
686 				{
687 					error("SWITCH: No more than one \"default\" case");
688 					new_free(&control);
689 					return;
690 				}
691 				found_def = 1;
692 			}
693 			header = expand_alias(header, subargs, &af, NULL);
694 			if (internal_debug & DEBUG_EXPANSIONS && !in_debug_yell)
695 				debugyell("%s expression expands to: (%s)", command, header);
696 			if (wild_match(header, control))
697 				hooked = 1;
698 			new_free(&header);
699 			if (*body == ';')
700 				body++;		/* ugh. */
701 		}
702 
703 		if (!(commands = next_expr(&body, '{')))
704 		{
705 			error("SWITCH: case body not found where expected");
706 			break;
707 		}
708 
709 		if (hooked)
710 		{
711 			parse_line(NULL, commands, subargs, 0, 0, 0);
712 			def = NULL;
713 			break;
714 		}
715 		else if (!def && found_def)
716 		{
717 			def = LOCAL_COPY(commands);
718 			found_def = 0;
719 		}
720 
721 		if (*body == ';')
722 			body++;		/* grumble */
723 	}
724 	if (def && *def)
725 		parse_line(NULL, def, subargs, 0, 0, 0);
726 	destroy_local_stack();
727 	new_free(&control);
728 }
729 
BUILT_IN_COMMAND(repeatcmd)730 BUILT_IN_COMMAND(repeatcmd)
731 {
732 	char *num_expr = NULL;
733 	int value;
734 
735 	while (isspace((unsigned char)*args))
736 		args++;
737 
738 	if (*args == '(')
739 	{
740 		char *tmp_val;
741 		char *dumb_copy;
742 		int argsused;
743 		char *sa = subargs ? subargs : empty_string;
744 
745 		num_expr = next_expr(&args, '(');
746 		dumb_copy = LOCAL_COPY(num_expr);
747 		tmp_val = parse_inline(dumb_copy,sa,&argsused);
748 		value = my_atol(tmp_val);
749 		new_free(&tmp_val);
750 	}
751 	else
752 	{
753 		char *tmp_val;
754 		int af;
755 
756 		num_expr = new_next_arg(args, &args);
757 		tmp_val = expand_alias(num_expr, subargs, &af, NULL);
758 		value = my_atol(tmp_val);
759 		new_free(&tmp_val);
760 	}
761 
762 	if (value <= 0)
763 		return;
764 	while (value--)
765 		parse_line(NULL, args, subargs ? subargs : empty_string, 0, 0, 1);
766 
767 	return;
768 }
769