1 /*
2  * loop.c - loop execution
3  *
4  * This file is part of zsh, the Z shell.
5  *
6  * Copyright (c) 1992-1997 Paul Falstad
7  * All rights reserved.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and to distribute modified versions of this software for any
12  * purpose, provided that the above copyright notice and the following
13  * two paragraphs appear in all copies of this software.
14  *
15  * In no event shall Paul Falstad or the Zsh Development Group be liable
16  * to any party for direct, indirect, special, incidental, or consequential
17  * damages arising out of the use of this software and its documentation,
18  * even if Paul Falstad and the Zsh Development Group have been advised of
19  * the possibility of such damage.
20  *
21  * Paul Falstad and the Zsh Development Group specifically disclaim any
22  * warranties, including, but not limited to, the implied warranties of
23  * merchantability and fitness for a particular purpose.  The software
24  * provided hereunder is on an "as is" basis, and Paul Falstad and the
25  * Zsh Development Group have no obligation to provide maintenance,
26  * support, updates, enhancements, or modifications.
27  *
28  */
29 
30 #include "zsh.mdh"
31 #include "loop.pro"
32 
33 /* # of nested loops we are in */
34 
35 /**/
36 int loops;
37 
38 /* # of continue levels */
39 
40 /**/
41 mod_export int contflag;
42 
43 /* # of break levels */
44 
45 /**/
46 mod_export int breaks;
47 
48 /**/
49 int
execfor(Estate state,int do_exec)50 execfor(Estate state, int do_exec)
51 {
52     Wordcode end, loop;
53     wordcode code = state->pc[-1];
54     int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND), ctok = 0, atok = 0;
55     int last = 0;
56     char *name, *str, *cond = NULL, *advance = NULL;
57     zlong val = 0;
58     LinkList vars = NULL, args = NULL;
59     int old_simple_pline = simple_pline;
60 
61     /* See comments in execwhile() */
62     simple_pline = 1;
63 
64     end = state->pc + WC_FOR_SKIP(code);
65 
66     if (iscond) {
67 	str = dupstring(ecgetstr(state, EC_NODUP, NULL));
68 	singsub(&str);
69 	if (isset(XTRACE)) {
70 	    char *str2 = dupstring(str);
71 	    untokenize(str2);
72 	    printprompt4();
73 	    fprintf(xtrerr, "%s\n", str2);
74 	    fflush(xtrerr);
75 	}
76 	if (!errflag) {
77 	    matheval(str);
78 	}
79 	if (errflag) {
80 	    state->pc = end;
81 	    simple_pline = old_simple_pline;
82 	    return 1;
83 	}
84 	cond = ecgetstr(state, EC_NODUP, &ctok);
85 	advance = ecgetstr(state, EC_NODUP, &atok);
86     } else {
87 	vars = ecgetlist(state, *state->pc++, EC_NODUP, NULL);
88 
89 	if (WC_FOR_TYPE(code) == WC_FOR_LIST) {
90 	    int htok = 0;
91 
92 	    if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
93 		state->pc = end;
94 		simple_pline = old_simple_pline;
95 		return 0;
96 	    }
97 	    if (htok) {
98 		execsubst(args);
99 		if (errflag) {
100 		    state->pc = end;
101 		    simple_pline = old_simple_pline;
102 		    return 1;
103 		}
104 	    }
105 	} else {
106 	    char **x;
107 
108 	    args = newlinklist();
109 	    for (x = pparams; *x; x++)
110 		addlinknode(args, dupstring(*x));
111 	}
112     }
113 
114     if (!args || empty(args))
115 	lastval = 0;
116 
117     loops++;
118     pushheap();
119     cmdpush(CS_FOR);
120     loop = state->pc;
121     while (!last) {
122 	if (iscond) {
123 	    if (ctok) {
124 		str = dupstring(cond);
125 		singsub(&str);
126 	    } else
127 		str = cond;
128 	    if (!errflag) {
129 		while (iblank(*str))
130 		    str++;
131 		if (*str) {
132 		    if (isset(XTRACE)) {
133 			printprompt4();
134 			fprintf(xtrerr, "%s\n", str);
135 			fflush(xtrerr);
136 		    }
137 		    val = mathevali(str);
138 		} else
139 		    val = 1;
140 	    }
141 	    if (errflag) {
142 		if (breaks)
143 		    breaks--;
144 		lastval = 1;
145 		break;
146 	    }
147 	    if (!val)
148 		break;
149 	} else {
150 	    LinkNode node;
151 	    int count = 0;
152 	    for (node = firstnode(vars); node; incnode(node))
153 	    {
154 		name = (char *)getdata(node);
155 		if (!args || !(str = (char *) ugetnode(args)))
156 		{
157 		    if (count) {
158 			str = "";
159 			last = 1;
160 		    } else
161 			break;
162 		}
163 		if (isset(XTRACE)) {
164 		    printprompt4();
165 		    fprintf(xtrerr, "%s=%s\n", name, str);
166 		    fflush(xtrerr);
167 		}
168 		setsparam(name, ztrdup(str));
169 		count++;
170 	    }
171 	    if (!count)
172 		break;
173 	}
174 	state->pc = loop;
175 	execlist(state, 1, do_exec && args && empty(args));
176 	if (breaks) {
177 	    breaks--;
178 	    if (breaks || !contflag)
179 		break;
180 	    contflag = 0;
181 	}
182 	if (retflag)
183 	    break;
184 	if (iscond && !errflag) {
185 	    if (atok) {
186 		str = dupstring(advance);
187 		singsub(&str);
188 	    } else
189 		str = advance;
190 	    if (isset(XTRACE)) {
191 		printprompt4();
192 		fprintf(xtrerr, "%s\n", str);
193 		fflush(xtrerr);
194 	    }
195 	    if (!errflag)
196 		matheval(str);
197 	}
198 	if (errflag) {
199 	    if (breaks)
200 		breaks--;
201 	    lastval = 1;
202 	    break;
203 	}
204 	freeheap();
205     }
206     popheap();
207     cmdpop();
208     loops--;
209     simple_pline = old_simple_pline;
210     state->pc = end;
211     this_noerrexit = 1;
212     return lastval;
213 }
214 
215 /**/
216 int
execselect(Estate state,UNUSED (int do_exec))217 execselect(Estate state, UNUSED(int do_exec))
218 {
219     Wordcode end, loop;
220     wordcode code = state->pc[-1];
221     char *str, *s, *name;
222     LinkNode n;
223     int i, usezle;
224     FILE *inp;
225     size_t more;
226     LinkList args;
227     int old_simple_pline = simple_pline;
228 
229     /* See comments in execwhile() */
230     simple_pline = 1;
231 
232     end = state->pc + WC_FOR_SKIP(code);
233     name = ecgetstr(state, EC_NODUP, NULL);
234 
235     if (WC_SELECT_TYPE(code) == WC_SELECT_PPARAM) {
236 	char **x;
237 
238 	args = newlinklist();
239 	for (x = pparams; *x; x++)
240 	    addlinknode(args, dupstring(*x));
241     } else {
242 	int htok = 0;
243 
244 	if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
245 	    state->pc = end;
246 	    simple_pline = old_simple_pline;
247 	    return 0;
248 	}
249 	if (htok) {
250 	    execsubst(args);
251 	    if (errflag) {
252 		state->pc = end;
253 		simple_pline = old_simple_pline;
254 		return 1;
255 	    }
256 	}
257     }
258     if (!args || empty(args)) {
259 	state->pc = end;
260 	simple_pline = old_simple_pline;
261 	return 0;
262     }
263     loops++;
264 
265     pushheap();
266     cmdpush(CS_SELECT);
267     usezle = interact && SHTTY != -1 && isset(USEZLE);
268     inp = fdopen(dup(usezle ? SHTTY : 0), "r");
269     more = selectlist(args, 0);
270     loop = state->pc;
271     for (;;) {
272 	for (;;) {
273 	    if (empty(bufstack)) {
274 	    	if (usezle) {
275 		    int oef = errflag;
276 
277 		    isfirstln = 1;
278 		    str = zleentry(ZLE_CMD_READ, &prompt3, NULL,
279 				   0, ZLCON_SELECT);
280 		    if (errflag)
281 			str = NULL;
282 		    /* Keep any user interrupt error status */
283 		    errflag = oef | (errflag & ERRFLAG_INT);
284 	    	} else {
285 		    str = promptexpand(prompt3, 0, NULL, NULL, NULL);
286 		    zputs(str, stderr);
287 		    free(str);
288 		    fflush(stderr);
289 		    str = fgets(zhalloc(256), 256, inp);
290 	    	}
291 	    } else
292 		str = (char *)getlinknode(bufstack);
293             if (!str && !errflag)
294                 setsparam("REPLY", ztrdup("")); /* EOF (user pressed Ctrl+D) */
295 	    if (!str || errflag) {
296 		if (breaks)
297 		    breaks--;
298 		fprintf(stderr, "\n");
299 		fflush(stderr);
300 		goto done;
301 	    }
302 	    if ((s = strchr(str, '\n')))
303 		*s = '\0';
304 	    if (*str)
305 	      break;
306 	    more = selectlist(args, more);
307 	}
308 	setsparam("REPLY", ztrdup(str));
309 	i = atoi(str);
310 	if (!i)
311 	    str = "";
312 	else {
313 	    for (i--, n = firstnode(args); n && i; incnode(n), i--);
314 	    if (n)
315 		str = (char *) getdata(n);
316 	    else
317 		str = "";
318 	}
319 	setsparam(name, ztrdup(str));
320 	state->pc = loop;
321 	execlist(state, 1, 0);
322 	freeheap();
323 	if (breaks) {
324 	    breaks--;
325 	    if (breaks || !contflag)
326 		break;
327 	    contflag = 0;
328 	}
329 	if (retflag || errflag)
330 	    break;
331     }
332   done:
333     cmdpop();
334     popheap();
335     fclose(inp);
336     loops--;
337     simple_pline = old_simple_pline;
338     state->pc = end;
339     this_noerrexit = 1;
340     return lastval;
341 }
342 
343 /* And this is used to print select lists. */
344 
345 /**/
346 size_t
selectlist(LinkList l,size_t start)347 selectlist(LinkList l, size_t start)
348 {
349     size_t longest = 1, fct, fw = 0, colsz, t0, t1, ct;
350     char **arr, **ap;
351 
352     zleentry(ZLE_CMD_TRASH);
353     arr = hlinklist2array(l, 0);
354     for (ap = arr; *ap; ap++)
355 	if (strlen(*ap) > longest)
356 	    longest = strlen(*ap);
357     t0 = ct = ap - arr;
358     longest++;
359     while (t0)
360 	t0 /= 10, longest++;
361     /* to compensate for added ')' */
362     fct = (zterm_columns - 1) / (longest + 3);
363     if (fct == 0)
364 	fct = 1;
365     else
366 	fw = (zterm_columns - 1) / fct;
367     colsz = (ct + fct - 1) / fct;
368     for (t1 = start; t1 != colsz && t1 - start < zterm_lines - 2; t1++) {
369 	ap = arr + t1;
370 	do {
371 	    size_t t2 = strlen(*ap) + 2;
372 	    int t3;
373 
374 	    fprintf(stderr, "%d) %s", t3 = ap - arr + 1, *ap);
375 	    while (t3)
376 		t2++, t3 /= 10;
377 	    for (; t2 < fw; t2++)
378 		fputc(' ', stderr);
379 	    for (t0 = colsz; t0 && *ap; t0--, ap++);
380 	}
381 	while (*ap);
382 	fputc('\n', stderr);
383     }
384 
385  /* Below is a simple attempt at doing it the Korn Way..
386        ap = arr;
387        t0 = 0;
388        do {
389            t0++;
390            fprintf(stderr,"%d) %s\n",t0,*ap);
391            ap++;
392        }
393        while (*ap);*/
394     fflush(stderr);
395 
396     return t1 < colsz ? t1 : 0;
397 }
398 
399 /**/
400 int
execwhile(Estate state,UNUSED (int do_exec))401 execwhile(Estate state, UNUSED(int do_exec))
402 {
403     Wordcode end, loop;
404     wordcode code = state->pc[-1];
405     int olderrexit, oldval, isuntil = (WC_WHILE_TYPE(code) == WC_WHILE_UNTIL);
406     int old_simple_pline = simple_pline;
407 
408     end = state->pc + WC_WHILE_SKIP(code);
409     olderrexit = noerrexit;
410     oldval = 0;
411     pushheap();
412     cmdpush(isuntil ? CS_UNTIL : CS_WHILE);
413     loops++;
414     loop = state->pc;
415 
416     if (loop[0] == WC_END && loop[1] == WC_END) {
417 
418         /* This is an empty loop.  Make sure the signal handler sets the
419         * flags and then just wait for someone hitting ^C. */
420 
421         simple_pline = 1;
422 
423         while (!breaks)
424             ;
425         breaks--;
426 
427         simple_pline = old_simple_pline;
428     } else {
429         for (;;) {
430             state->pc = loop;
431             noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
432 
433 	    /* In case the test condition is a functional no-op,
434 	     * make sure signal handlers recognize ^C to end the loop. */
435 	    simple_pline = 1;
436 
437             execlist(state, 1, 0);
438 
439 	    simple_pline = old_simple_pline;
440             noerrexit = olderrexit;
441             if (!((lastval == 0) ^ isuntil)) {
442                 if (breaks)
443                     breaks--;
444 		if (!retflag)
445 		    lastval = oldval;
446                 break;
447             }
448             if (retflag) {
449 		if (breaks)
450 		    breaks--;
451                 break;
452 	    }
453 
454 	    /* In case the loop body is also a functional no-op,
455 	     * make sure signal handlers recognize ^C as above. */
456 	    simple_pline = 1;
457 
458             execlist(state, 1, 0);
459 
460 	    simple_pline = old_simple_pline;
461             if (breaks) {
462                 breaks--;
463                 if (breaks || !contflag)
464                     break;
465                 contflag = 0;
466             }
467             if (errflag) {
468                 lastval = 1;
469                 break;
470             }
471             if (retflag)
472                 break;
473             freeheap();
474             oldval = lastval;
475         }
476     }
477     cmdpop();
478     popheap();
479     loops--;
480     state->pc = end;
481     this_noerrexit = 1;
482     return lastval;
483 }
484 
485 /**/
486 int
execrepeat(Estate state,UNUSED (int do_exec))487 execrepeat(Estate state, UNUSED(int do_exec))
488 {
489     Wordcode end, loop;
490     wordcode code = state->pc[-1];
491     int count, htok = 0;
492     char *tmp;
493     int old_simple_pline = simple_pline;
494 
495     /* See comments in execwhile() */
496     simple_pline = 1;
497 
498     end = state->pc + WC_REPEAT_SKIP(code);
499 
500     lastval = 0;
501     tmp = ecgetstr(state, EC_DUPTOK, &htok);
502     if (htok)
503 	singsub(&tmp);
504     count = mathevali(tmp);
505     if (errflag)
506 	return 1;
507     pushheap();
508     cmdpush(CS_REPEAT);
509     loops++;
510     loop = state->pc;
511     while (count-- > 0) {
512 	state->pc = loop;
513 	execlist(state, 1, 0);
514 	freeheap();
515 	if (breaks) {
516 	    breaks--;
517 	    if (breaks || !contflag)
518 		break;
519 	    contflag = 0;
520 	}
521 	if (errflag) {
522 	    lastval = 1;
523 	    break;
524 	}
525 	if (retflag)
526 	    break;
527     }
528     cmdpop();
529     popheap();
530     loops--;
531     simple_pline = old_simple_pline;
532     state->pc = end;
533     this_noerrexit = 1;
534     return lastval;
535 }
536 
537 /**/
538 int
execif(Estate state,int do_exec)539 execif(Estate state, int do_exec)
540 {
541     Wordcode end, next;
542     wordcode code = state->pc[-1];
543     int olderrexit, s = 0, run = 0;
544 
545     olderrexit = noerrexit;
546     end = state->pc + WC_IF_SKIP(code);
547 
548     noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN;
549     while (state->pc < end) {
550 	code = *state->pc++;
551 	if (wc_code(code) != WC_IF ||
552 	    (run = (WC_IF_TYPE(code) == WC_IF_ELSE))) {
553 	    if (run)
554 		run = 2;
555 	    break;
556 	}
557 	next = state->pc + WC_IF_SKIP(code);
558 	cmdpush(s ? CS_ELIF : CS_IF);
559 	execlist(state, 1, 0);
560 	cmdpop();
561 	if (!lastval) {
562 	    run = 1;
563 	    break;
564 	}
565 	if (retflag)
566 	    break;
567 	s = 1;
568 	state->pc = next;
569     }
570 
571     if (run) {
572 	/* we need to ignore lastval until we reach execcmd() */
573 	if (olderrexit || run == 2)
574 	    noerrexit = olderrexit;
575 	else if (lastval)
576 	    noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN | NOERREXIT_UNTIL_EXEC;
577 	else
578 	    noerrexit &= ~ (NOERREXIT_EXIT | NOERREXIT_RETURN);
579 	cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN));
580 	execlist(state, 1, do_exec);
581 	cmdpop();
582     } else {
583 	noerrexit = olderrexit;
584 	if (!retflag)
585 	    lastval = 0;
586     }
587     state->pc = end;
588     this_noerrexit = 1;
589 
590     return lastval;
591 }
592 
593 /**/
594 int
execcase(Estate state,int do_exec)595 execcase(Estate state, int do_exec)
596 {
597     Wordcode end, next;
598     wordcode code = state->pc[-1];
599     char *word, *pat;
600     int npat, save, nalts, ialt, patok, anypatok;
601     Patprog *spprog, pprog;
602 
603     end = state->pc + WC_CASE_SKIP(code);
604 
605     word = ecgetstr(state, EC_DUP, NULL);
606     singsub(&word);
607     untokenize(word);
608     anypatok = 0;
609 
610     cmdpush(CS_CASE);
611     while (state->pc < end) {
612 	code = *state->pc++;
613 	if (wc_code(code) != WC_CASE)
614 	    break;
615 
616 	save = 0;
617 	next = state->pc + WC_CASE_SKIP(code);
618 	nalts = *state->pc++;
619 	ialt = patok = 0;
620 
621 	if (isset(XTRACE)) {
622 	    printprompt4();
623 	    fprintf(xtrerr, "case %s (", word);
624 	}
625 
626 	while (!patok && nalts) {
627 	    npat = state->pc[1];
628 	    spprog = state->prog->pats + npat;
629 	    pprog = NULL;
630 	    pat = NULL;
631 
632 	    queue_signals();
633 
634 	    if (isset(XTRACE)) {
635 		int htok = 0;
636 		pat = dupstring(ecrawstr(state->prog, state->pc, &htok));
637 		if (htok)
638 		    singsub(&pat);
639 
640 		if (ialt++)
641 		    fprintf(stderr, " | ");
642 		quote_tokenized_output(pat, xtrerr);
643 	    }
644 
645 	    if (*spprog != dummy_patprog1 && *spprog != dummy_patprog2)
646 		pprog = *spprog;
647 
648 	    if (!pprog) {
649 		if (!pat) {
650 		    char *opat;
651 		    int htok = 0;
652 
653 		    pat = dupstring(opat = ecrawstr(state->prog,
654 						    state->pc, &htok));
655 		    if (htok)
656 			singsub(&pat);
657 		    save = (!(state->prog->flags & EF_HEAP) &&
658 			    !strcmp(pat, opat) && *spprog != dummy_patprog2);
659 		}
660 		if (!(pprog = patcompile(pat, (save ? PAT_ZDUP : PAT_STATIC),
661 					 NULL)))
662 		    zerr("bad pattern: %s", pat);
663 		else if (save)
664 		    *spprog = pprog;
665 	    }
666 	    if (pprog && pattry(pprog, word))
667 		patok = anypatok = 1;
668 	    state->pc += 2;
669 	    nalts--;
670 
671 	    unqueue_signals();
672 	}
673 	state->pc += 2 * nalts;
674 	if (isset(XTRACE)) {
675 	    fprintf(xtrerr, ")\n");
676 	    fflush(xtrerr);
677 	}
678 	if (patok) {
679 	    execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) &&
680 				do_exec));
681 	    while (!retflag && wc_code(code) == WC_CASE &&
682 		   WC_CASE_TYPE(code) == WC_CASE_AND && state->pc < end) {
683 		state->pc = next;
684 		code = *state->pc++;
685 		next = state->pc + WC_CASE_SKIP(code);
686 		nalts = *state->pc++;
687 		state->pc += 2 * nalts;
688 		execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) &&
689 				    do_exec));
690 	    }
691 	    if (WC_CASE_TYPE(code) != WC_CASE_TESTAND)
692 		break;
693 	}
694 	state->pc = next;
695     }
696     cmdpop();
697 
698     state->pc = end;
699 
700     if (!anypatok)
701 	lastval = 0;
702     this_noerrexit = 1;
703 
704     return lastval;
705 }
706 
707 /*
708  * Errflag from `try' block, may be reset in `always' block.
709  * Accessible from an integer parameter, so needs to be a zlong.
710  */
711 
712 /**/
713 zlong
714 try_errflag = -1;
715 
716 /**
717  * Corresponding interrupt error status form `try' block.
718  */
719 
720 /**/
721 zlong
722 try_interrupt = -1;
723 
724 /**/
725 zlong
726 try_tryflag = 0;
727 
728 /**/
729 int
exectry(Estate state,int do_exec)730 exectry(Estate state, int do_exec)
731 {
732     Wordcode end, always;
733     int endval;
734     int save_retflag, save_breaks, save_contflag;
735     zlong save_try_errflag, save_try_interrupt;
736 
737     end = state->pc + WC_TRY_SKIP(state->pc[-1]);
738     always = state->pc + 1 + WC_TRY_SKIP(*state->pc);
739     state->pc++;
740     pushheap();
741     cmdpush(CS_CURSH);
742 
743     /* The :try clause */
744     ++try_tryflag;
745     execlist(state, 1, do_exec);
746     --try_tryflag;
747 
748     /* Don't record errflag here, may be reset.  However, */
749     /* endval should show failure when there is an error. */
750     endval = lastval ? lastval : errflag;
751 
752     freeheap();
753 
754     cmdpop();
755     cmdpush(CS_ALWAYS);
756 
757     /* The always clause. */
758     save_try_errflag = try_errflag;
759     save_try_interrupt = try_interrupt;
760     try_errflag = (zlong)(errflag & ERRFLAG_ERROR);
761     try_interrupt = (zlong)((errflag & ERRFLAG_INT) ? 1 : 0);
762     /* We need to reset all errors to allow the block to execute */
763     errflag = 0;
764     save_retflag = retflag;
765     retflag = 0;
766     save_breaks = breaks;
767     breaks = 0;
768     save_contflag = contflag;
769     contflag = 0;
770 
771     state->pc = always;
772     execlist(state, 1, do_exec);
773 
774     if (try_errflag)
775 	errflag |= ERRFLAG_ERROR;
776     else
777 	errflag &= ~ERRFLAG_ERROR;
778     if (try_interrupt)
779 	errflag |= ERRFLAG_INT;
780     else
781 	errflag &= ~ERRFLAG_INT;
782     try_errflag = save_try_errflag;
783     try_interrupt = save_try_interrupt;
784     if (!retflag)
785 	retflag = save_retflag;
786     if (!breaks)
787 	breaks = save_breaks;
788     if (!contflag)
789 	contflag = save_contflag;
790 
791     cmdpop();
792     popheap();
793     state->pc = end;
794 
795     return endval;
796 }
797