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(¤t_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