1 /*
2  * if.c: handles the IF command for IRCII
3  *
4  * Written By Michael Sandrof
5  *
6  * Copyright (c) 1990 Michael Sandrof.
7  * Copyright (c) 1991, 1992 Troy Rollo.
8  * Copyright (c) 1992-2017 Matthew R. Green.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include "irc.h"
36 IRCII_RCSID("@(#)$eterna: if.c,v 1.41 2017/07/03 14:24:55 mrg Exp $");
37 
38 #include "alias.h"
39 #include "ircaux.h"
40 #include "window.h"
41 #include "vars.h"
42 #include "output.h"
43 #include "if.h"
44 
45 static	int	charcount(u_char *, int);
46 
47 /*
48  * next_expr finds the next expression delimited by brackets. The type
49  * of bracket expected is passed as a parameter. Returns NULL on error.
50  */
51 u_char	*
next_expr(u_char ** args,int itype)52 next_expr(u_char **args, int itype)
53 {
54 	u_char	*ptr,
55 		*ptr2,
56 		*ptr3;
57 	u_char	type = (u_char)itype;
58 
59 	if (!*args)
60 		return NULL;
61 	ptr2 = *args;
62 	if (!*ptr2)
63 		return 0;
64 	if (*ptr2 != type)
65 	{
66 		say("Expression syntax");
67 		return 0;
68 	}							/* { */
69 	ptr = matching_bracket(ptr2 + 1, type, (type == '(') ? ')' : '}');
70 	if (!ptr)
71 	{
72 		say("Unmatched '%c'", type);
73 		return 0;
74 	}
75 	*ptr = '\0';
76 	do
77 	{
78 		ptr2++;
79 	}
80 	while (isspace(*ptr2));
81 	ptr3 = ptr+1;
82 	while (isspace(*ptr3))
83 		ptr3++;
84 	*args = ptr3;
85 	if (*ptr2)
86 	{
87 		ptr--;
88 		while (isspace(*ptr))
89 			*ptr-- = '\0';
90 	}
91 	return ptr2;
92 }
93 
94 void
ifcmd(u_char * command,u_char * args,u_char * subargs)95 ifcmd(u_char *command, u_char *args, u_char *subargs)
96 {
97 	u_char	*expr;
98 	u_char	*sub;
99 	int	flag = 0;
100 	int	result;
101 
102 	if (!(expr = next_expr(&args, '(')))
103 	{
104 		yell("Missing CONDITION in IF");
105 		return;
106 	}
107 	sub = parse_inline(expr, subargs ? subargs : empty_string(), &flag);
108 	if (get_int_var(DEBUG_VAR) & DEBUG_EXPANSIONS)
109 		yell("If expression expands to: (%s)", sub);
110 	if (!*sub || *sub == '0')
111 		result = 0;
112 	else
113 		result = 1;
114 	new_free(&sub);
115 	if (!(expr = next_expr(&args, '{')))
116 	{
117 		yell("Missing THEN portion in IF");
118 		return;
119 	}
120 	if (!result && !(expr = next_expr(&args, '{')))
121 		return;
122 	parse_line(NULL, expr, subargs ? subargs : empty_string(), 0, 0, 0);
123 		return;
124 }
125 
126 void
whilecmd(u_char * command,u_char * args,u_char * subargs)127 whilecmd(u_char *command, u_char *args, u_char *subargs)
128 {
129 	u_char	*expr = NULL,
130 		*ptr,
131 		*body = NULL,
132 		*newexp = NULL;
133 	int	args_used;	/* this isn't used here, but is passed
134 				 * to expand_alias() */
135 
136 	if ((ptr = next_expr(&args, '(')) == NULL)
137 	{
138 		yell("WHILE: missing boolean expression");
139 		return;
140 	}
141 	malloc_strcpy(&expr, ptr);
142 	if ((ptr = next_expr(&args, '{')) == NULL)
143 	{
144 		say("WHILE: missing expression");
145 		new_free(&expr);
146 		return;
147 	}
148 	malloc_strcpy(&body, ptr);
149 	while (1)
150 	{
151 		malloc_strcpy(&newexp, expr);
152 		ptr = parse_inline(newexp, subargs ? subargs : empty_string(),
153 			&args_used);
154 		if (*ptr && *ptr !='0')
155 		{
156 			new_free(&ptr);
157 			parse_line(NULL, body, subargs ?
158 				subargs : empty_string(), 0, 0, 0);
159 		}
160 		else
161 			break;
162 	}
163 	new_free(&newexp);
164 	new_free(&ptr);
165 	new_free(&expr);
166 	new_free(&body);
167 }
168 
169 static int
charcount(u_char * string,int what)170 charcount(u_char *string, int what)
171 {
172 	int	x = 0;
173 	u_char	*place = string - 1;
174 
175 	while ((place = my_index(place + 1, what)))
176 		x++;
177 	return x;
178 }
179 
180 /*
181  * How it works -- if There are no parenthesis, it must be a
182  * foreach array command.  If there are parenthesis, and there are
183  * exactly two commas, it must be a C-like for command, else it must
184  * must be an foreach word command
185  */
186 void
foreach_handler(u_char * command,u_char * args,u_char * subargs)187 foreach_handler(u_char *command, u_char *args, u_char *subargs)
188 {
189 	u_char	*temp = NULL;
190 	u_char	*placeholder;
191 	u_char	*temp2 = NULL;
192 
193 	malloc_strcpy(&temp, args);
194 	placeholder = temp;
195 	if (*temp == '(')
196 	{
197 		if ((temp2 = next_expr(&temp, '(')) == NULL) {
198 			new_free(&placeholder);
199 			return;
200 		}
201 		if (charcount(temp2, ',') == 2)
202 			forcmd(command, args, subargs);
203 		else
204 			fe(command, args, subargs);
205 	}
206 	else
207 		foreach(command, args, subargs);
208 	new_free(&placeholder);
209 }
210 
211 void
foreach(u_char * command,u_char * args,u_char * subargs)212 foreach(u_char *command, u_char *args, u_char *subargs)
213 {
214 	u_char	*struc = NULL,
215 		*ptr,
216 		*body = NULL,
217 		*var = NULL;
218 	u_char	**sublist;
219 	int	total;
220 	int	i;
221 	int	slen;
222 
223 	if ((ptr = new_next_arg(args, &args)) == NULL)
224 	{
225 		yell("FOREACH: missing structure expression");
226 		return;
227 	}
228 	malloc_strcpy(&struc, ptr);
229 	malloc_strcat(&struc, UP("."));
230 	upper(struc);
231 	if ((var = next_arg(args, &args)) == NULL)
232 	{
233 		new_free(&struc);
234 		yell("FOREACH: missing variable");
235 		return;
236 	}
237 	while (isspace(*args))
238 		args++;
239 	if ((body = next_expr(&args, '{')) == NULL)	/* } */
240 	{
241 		new_free(&struc);
242 		yell("FOREACH: missing statement");
243 		return;
244 	}
245 	sublist = match_alias(struc, &total, VAR_ALIAS);
246 	slen = my_strlen(struc);
247 	for (i = 0; i < total; i++)
248 	{
249 		unsigned	display;
250 
251 		display = set_display_off();
252 		add_alias(VAR_ALIAS, var, sublist[i]+slen);
253 		set_display(display);
254 		parse_line(NULL, body, subargs ?
255 		    subargs : empty_string(), 0, 0, 0);
256 	}
257 	free_alias(sublist, total);
258 	new_free(&struc);
259 }
260 
261 /*
262  * FE:  Written by Jeremy Nelson (jnelson@iastate.edu)
263  *
264  * FE: replaces recursion
265  *
266  * The thing about it is that you can nest variables, as this command calls
267  * expand_alias until the list doesnt change.  So you can nest lists in
268  * lists, and hopefully that will work.  However, it also makes it
269  * impossible to have $s anywhere in the list.  Maybe ill change that
270  * some day.
271  */
272 
273 void
fe(u_char * command,u_char * args,u_char * subargs)274 fe(u_char *command, u_char *args, u_char *subargs)
275 {
276 	u_char	*list = NULL,
277 		*templist = NULL,
278 		*placeholder,
279 		*oldlist = NULL,
280 		*sa,
281 		*vars,
282 		*var[255],
283 		*word = NULL,
284 		*todo = NULL;
285 	int	ind, x, y, count, args_flag;
286 	unsigned display;
287 
288 	for (x = 0; x < 255; var[x++] = NULL)
289 		;
290 
291 	list = next_expr(&args, '(');	/* ) */
292 	if (!list)
293 	{
294 		yell ("FE: Missing List for /FE");
295 		return;
296 	}
297 
298 	sa = subargs ? subargs : (u_char *) " ";
299 	malloc_strcpy(&templist, list);
300 	do
301 	{
302 		malloc_strcpy(&oldlist, templist);
303 		new_free(&templist);
304 		templist = expand_alias(NULL, oldlist, sa, &args_flag, NULL);
305 	} while (my_strcmp(templist, oldlist));
306 
307 	new_free(&oldlist);
308 
309 	if (*templist == '\0')
310 	{
311 		new_free(&templist);
312 		return;
313 	}
314 
315 	vars = args;
316 	if (!(args = my_index(args, '{')))		/* } */
317 	{
318 		yell ("FE: Missing commands");
319 		new_free(&templist);
320 		return;
321 	}
322 	*(args-1) = '\0';
323 	ind = 0;
324 	while ((var[ind++] = next_arg(vars, &vars)))
325 	{
326 		if (ind == 255)
327 		{
328 			yell ("FE: Too many variables");
329 			new_free(&templist);
330 			return;
331 		}
332 	}
333 	ind = ind ? ind - 1: 0;
334 
335 	if (!(todo = next_expr(&args, '{')))		/* } { */
336 	{
337 		yell ("FE: Missing }");
338 		new_free(&templist);
339 		return;
340 	}
341 
342 	count = word_count(templist);
343 	display = get_display();
344 	placeholder = templist;
345 	for (x = 0; x < count;)
346 	{
347 		set_display_off();
348 		for (y = 0; y < ind; y++)
349 		{
350 			word = ((x + y) < count)
351 			    ? next_arg(templist, &templist)
352 			    : NULL;
353 			add_alias(VAR_ALIAS, var[y], word);
354 		}
355 		set_display(display);
356 		x += ind;
357 		parse_line(NULL, todo,
358 		    subargs ? subargs : empty_string(), 0, 0, 0);
359 	}
360 	set_display_off();
361 	for (y = 0; y < ind; y++)  {
362 		delete_alias(VAR_ALIAS, var[y]);
363 	}
364 	set_display(display);
365 	new_free(&placeholder);
366 }
367 
368 /* FOR command..... prototype:
369  *  for (commence,evaluation,iteration)
370  * in the same style of C's for, the for loop is just a specific
371  * type of WHILE loop.
372  *
373  * IMPORTANT: Since ircII uses ; as a delimeter between commands,
374  * commas were chosen to be the delimiter between expressions,
375  * so that semicolons may be used in the expressions (think of this
376  * as the reverse as C, where commas seperate commands in expressions,
377  * and semicolons end expressions.
378  */
379 /*  I suppose someone could make a case that since the
380  *  foreach_handler() routine weeds out any for command that doesnt have
381  *  two commands, that checking for those 2 commas is a waste.  I suppose.
382  */
383 void
forcmd(u_char * command,u_char * args,u_char * subargs)384 forcmd(u_char *command, u_char *args, u_char *subargs)
385 {
386 	u_char	*working = NULL;
387 	u_char	*commence = NULL;
388 	u_char	*evaluation = NULL;
389 	u_char	*lameeval = NULL;
390 	u_char	*iteration = NULL;
391 	u_char	*sa = NULL;
392 	int	argsused = 0;
393 	u_char	*line = NULL;
394 	u_char	*commands = NULL;
395 
396 	/* Get the whole () thing */
397 	if ((working = next_expr(&args, '(')) == NULL)	/* ) */
398 	{
399 		yell("FOR: missing closing parenthesis");
400 		return;
401 	}
402 	malloc_strcpy(&commence, working);
403 
404 	/* Find the beginning of the second expression */
405 	evaluation = my_index(commence, ',');
406 	if (!evaluation)
407 	{
408 		yell("FOR: no components!");
409 		new_free(&commence);
410 		return;
411 	}
412 	do
413 		*evaluation++ = '\0';
414 	while (isspace(*evaluation));
415 
416 	/* Find the beginning of the third expression */
417 	iteration = my_index(evaluation, ',');
418 	if (!iteration)
419 	{
420 		yell("FOR: Only two components!");
421 		new_free(&commence);
422 		return;
423 	}
424 	do
425 	{
426 		*iteration++ = '\0';
427 	}
428 	while (isspace(*iteration));
429 
430 	working = args;
431 	while (isspace(*working))
432 		*working++ = '\0';
433 
434 	if ((working = next_expr(&working, '{')) == NULL)	/* } */
435 	{
436 		yell("FOR: badly formed commands");
437 		new_free(&commence);
438 		return;
439 	}
440 
441 	malloc_strcpy(&commands, working);
442 
443 	sa = subargs ? subargs : empty_string();
444 	parse_line(NULL, commence, sa, 0, 0, 0);
445 
446 	while (1)
447 	{
448 		malloc_strcpy(&lameeval, evaluation);
449 		line = parse_inline(lameeval, sa, &argsused);
450 		if (*line && *line != '0')
451 		{
452 			new_free(&line);
453 			parse_line(NULL, commands, sa, 0, 0, 0);
454 			parse_line(NULL, iteration, sa, 0, 0, 0);
455 		}
456 		else break;
457 	}
458 	new_free(&line);
459 	new_free(&lameeval);
460 	new_free(&commence);
461 	new_free(&commands);
462 }
463 
464 /* fec - iterate over a list of characters */
465 
466 void
fec(u_char * command,u_char * args,u_char * subargs)467 fec(u_char *command, u_char *args, u_char *subargs)
468 {
469 	u_char	*pointer;
470 	u_char	*list = NULL;
471 	u_char	*var = NULL;
472 	u_char	stuff[2];
473 	int	args_flag = 0;
474 	unsigned display;
475 	u_char	*sa, *todo;
476 
477 	list = next_expr(&args, '(');		/* ) */
478 	if (list == NULL)
479 	{
480 		yell ("FEC: Missing List for /FEC");
481 		return;
482 	}
483 
484 	sa = subargs ? subargs : empty_string();
485 	list = expand_alias(NULL, list, sa, &args_flag, NULL);
486 	pointer = list;
487 
488 	var = next_arg(args, &args);
489 	args = my_index(args, '{');		/* } */
490 
491 	if ((todo = next_expr(&args, '{')) == NULL)
492 	{
493 		yell ("FE: Missing }");
494 		return;
495 	}
496 
497 	stuff[1] = '\0';
498 
499 	while (*pointer)
500 	{
501 		display = set_display_off();
502 		stuff[0] = *pointer++;
503 		add_alias(VAR_ALIAS, var, stuff);
504 		set_display(display);
505 		parse_line(NULL, todo,
506 		    subargs ? subargs : empty_string(), 0, 0, 0);
507 	}
508 	display = set_display_off();
509 	delete_alias(VAR_ALIAS, var);
510 	set_display(display);
511 
512 	new_free(&list);
513 }
514