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