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/**/
36int loops;
37
38/* # of continue levels */
39
40/**/
41mod_export int contflag;
42
43/* # of break levels */
44
45/**/
46mod_export int breaks;
47
48/**/
49int
50execfor(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
60    end = state->pc + WC_FOR_SKIP(code);
61
62    if (iscond) {
63	str = dupstring(ecgetstr(state, EC_NODUP, NULL));
64	singsub(&str);
65	if (isset(XTRACE)) {
66	    char *str2 = dupstring(str);
67	    untokenize(str2);
68	    printprompt4();
69	    fprintf(xtrerr, "%s\n", str2);
70	    fflush(xtrerr);
71	}
72	if (!errflag)
73	    matheval(str);
74	if (errflag) {
75	    state->pc = end;
76	    return lastval = errflag;
77	}
78	cond = ecgetstr(state, EC_NODUP, &ctok);
79	advance = ecgetstr(state, EC_NODUP, &atok);
80    } else {
81	vars = ecgetlist(state, *state->pc++, EC_NODUP, NULL);
82
83	if (WC_FOR_TYPE(code) == WC_FOR_LIST) {
84	    int htok = 0;
85
86	    if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
87		state->pc = end;
88		return 0;
89	    }
90	    if (htok)
91		execsubst(args);
92	} else {
93	    char **x;
94
95	    args = newlinklist();
96	    for (x = pparams; *x; x++)
97		addlinknode(args, dupstring(*x));
98	}
99    }
100    lastval = 0;
101    loops++;
102    pushheap();
103    cmdpush(CS_FOR);
104    loop = state->pc;
105    while (!last) {
106	if (iscond) {
107	    if (ctok) {
108		str = dupstring(cond);
109		singsub(&str);
110	    } else
111		str = cond;
112	    if (!errflag) {
113		while (iblank(*str))
114		    str++;
115		if (*str) {
116		    if (isset(XTRACE)) {
117			printprompt4();
118			fprintf(xtrerr, "%s\n", str);
119			fflush(xtrerr);
120		    }
121		    val = mathevali(str);
122		} else
123		    val = 1;
124	    }
125	    if (errflag) {
126		if (breaks)
127		    breaks--;
128		lastval = 1;
129		break;
130	    }
131	    if (!val)
132		break;
133	} else {
134	    LinkNode node;
135	    int count = 0;
136	    for (node = firstnode(vars); node; incnode(node))
137	    {
138		name = (char *)getdata(node);
139		if (!args || !(str = (char *) ugetnode(args)))
140		{
141		    if (count) {
142			str = "";
143			last = 1;
144		    } else
145			break;
146		}
147		if (isset(XTRACE)) {
148		    printprompt4();
149		    fprintf(xtrerr, "%s=%s\n", name, str);
150		    fflush(xtrerr);
151		}
152		setsparam(name, ztrdup(str));
153		count++;
154	    }
155	    if (!count)
156		break;
157	}
158	state->pc = loop;
159	execlist(state, 1, do_exec && args && empty(args));
160	if (breaks) {
161	    breaks--;
162	    if (breaks || !contflag)
163		break;
164	    contflag = 0;
165	}
166	if (retflag)
167	    break;
168	if (iscond && !errflag) {
169	    if (atok) {
170		str = dupstring(advance);
171		singsub(&str);
172	    } else
173		str = advance;
174	    if (isset(XTRACE)) {
175		printprompt4();
176		fprintf(xtrerr, "%s\n", str);
177		fflush(xtrerr);
178	    }
179	    if (!errflag)
180		matheval(str);
181	}
182	if (errflag) {
183	    if (breaks)
184		breaks--;
185	    lastval = 1;
186	    break;
187	}
188	freeheap();
189    }
190    popheap();
191    cmdpop();
192    loops--;
193    state->pc = end;
194    return lastval;
195}
196
197/**/
198int
199execselect(Estate state, UNUSED(int do_exec))
200{
201    Wordcode end, loop;
202    wordcode code = state->pc[-1];
203    char *str, *s, *name;
204    LinkNode n;
205    int i, usezle;
206    FILE *inp;
207    size_t more;
208    LinkList args;
209
210    end = state->pc + WC_FOR_SKIP(code);
211    name = ecgetstr(state, EC_NODUP, NULL);
212
213    if (WC_SELECT_TYPE(code) == WC_SELECT_PPARAM) {
214	char **x;
215
216	args = newlinklist();
217	for (x = pparams; *x; x++)
218	    addlinknode(args, dupstring(*x));
219    } else {
220	int htok = 0;
221
222	if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
223	    state->pc = end;
224	    return 0;
225	}
226	if (htok)
227	    execsubst(args);
228    }
229    if (!args || empty(args)) {
230	state->pc = end;
231	return 1;
232    }
233    loops++;
234    lastval = 0;
235    pushheap();
236    cmdpush(CS_SELECT);
237    usezle = interact && SHTTY != -1 && isset(USEZLE);
238    inp = fdopen(dup(usezle ? SHTTY : 0), "r");
239    more = selectlist(args, 0);
240    loop = state->pc;
241    for (;;) {
242	for (;;) {
243	    if (empty(bufstack)) {
244	    	if (usezle) {
245		    int oef = errflag;
246
247		    isfirstln = 1;
248		    str = zleentry(ZLE_CMD_READ, &prompt3, NULL,
249				   0, ZLCON_SELECT);
250		    if (errflag)
251			str = NULL;
252		    errflag = oef;
253	    	} else {
254		    str = promptexpand(prompt3, 0, NULL, NULL, NULL);
255		    zputs(str, stderr);
256		    free(str);
257		    fflush(stderr);
258		    str = fgets(zalloc(256), 256, inp);
259	    	}
260	    } else
261		str = (char *)getlinknode(bufstack);
262	    if (!str || errflag) {
263		if (breaks)
264		    breaks--;
265		fprintf(stderr, "\n");
266		fflush(stderr);
267		goto done;
268	    }
269	    if ((s = strchr(str, '\n')))
270		*s = '\0';
271	    if (*str)
272	      break;
273	    more = selectlist(args, more);
274	}
275	setsparam("REPLY", ztrdup(str));
276	i = atoi(str);
277	if (!i)
278	    str = "";
279	else {
280	    for (i--, n = firstnode(args); n && i; incnode(n), i--);
281	    if (n)
282		str = (char *) getdata(n);
283	    else
284		str = "";
285	}
286	setsparam(name, ztrdup(str));
287	state->pc = loop;
288	execlist(state, 1, 0);
289	freeheap();
290	if (breaks) {
291	    breaks--;
292	    if (breaks || !contflag)
293		break;
294	    contflag = 0;
295	}
296	if (retflag || errflag)
297	    break;
298    }
299  done:
300    cmdpop();
301    popheap();
302    fclose(inp);
303    loops--;
304    state->pc = end;
305    return lastval;
306}
307
308/* And this is used to print select lists. */
309
310/**/
311size_t
312selectlist(LinkList l, size_t start)
313{
314    size_t longest = 1, fct, fw = 0, colsz, t0, t1, ct;
315    char **arr, **ap;
316
317    zleentry(ZLE_CMD_TRASH);
318    arr = hlinklist2array(l, 0);
319    for (ap = arr; *ap; ap++)
320	if (strlen(*ap) > longest)
321	    longest = strlen(*ap);
322    t0 = ct = ap - arr;
323    longest++;
324    while (t0)
325	t0 /= 10, longest++;
326    /* to compensate for added ')' */
327    fct = (zterm_columns - 1) / (longest + 3);
328    if (fct == 0)
329	fct = 1;
330    else
331	fw = (zterm_columns - 1) / fct;
332    colsz = (ct + fct - 1) / fct;
333    for (t1 = start; t1 != colsz && t1 - start < zterm_lines - 2; t1++) {
334	ap = arr + t1;
335	do {
336	    size_t t2 = strlen(*ap) + 2;
337	    int t3;
338
339	    fprintf(stderr, "%d) %s", t3 = ap - arr + 1, *ap);
340	    while (t3)
341		t2++, t3 /= 10;
342	    for (; t2 < fw; t2++)
343		fputc(' ', stderr);
344	    for (t0 = colsz; t0 && *ap; t0--, ap++);
345	}
346	while (*ap);
347	fputc('\n', stderr);
348    }
349
350 /* Below is a simple attempt at doing it the Korn Way..
351       ap = arr;
352       t0 = 0;
353       do {
354           t0++;
355           fprintf(stderr,"%d) %s\n",t0,*ap);
356           ap++;
357       }
358       while (*ap);*/
359    fflush(stderr);
360
361    return t1 < colsz ? t1 : 0;
362}
363
364/**/
365int
366execwhile(Estate state, UNUSED(int do_exec))
367{
368    Wordcode end, loop;
369    wordcode code = state->pc[-1];
370    int olderrexit, oldval, isuntil = (WC_WHILE_TYPE(code) == WC_WHILE_UNTIL);
371
372    end = state->pc + WC_WHILE_SKIP(code);
373    olderrexit = noerrexit;
374    oldval = 0;
375    pushheap();
376    cmdpush(isuntil ? CS_UNTIL : CS_WHILE);
377    loops++;
378    loop = state->pc;
379
380    if (loop[0] == WC_END && loop[1] == WC_END) {
381
382        /* This is an empty loop.  Make sure the signal handler sets the
383        * flags and then just wait for someone hitting ^C. */
384
385        int old_simple_pline = simple_pline;
386
387        simple_pline = 1;
388
389        while (!breaks)
390            ;
391        breaks--;
392
393        simple_pline = old_simple_pline;
394    } else
395        for (;;) {
396            state->pc = loop;
397            noerrexit = 1;
398            execlist(state, 1, 0);
399            noerrexit = olderrexit;
400            if (!((lastval == 0) ^ isuntil)) {
401                if (breaks)
402                    breaks--;
403                lastval = oldval;
404                break;
405            }
406            if (retflag) {
407                lastval = oldval;
408                break;
409            }
410            execlist(state, 1, 0);
411            if (breaks) {
412                breaks--;
413                if (breaks || !contflag)
414                    break;
415                contflag = 0;
416            }
417            if (errflag) {
418                lastval = 1;
419                break;
420            }
421            if (retflag)
422                break;
423            freeheap();
424            oldval = lastval;
425        }
426    cmdpop();
427    popheap();
428    loops--;
429    state->pc = end;
430    return lastval;
431}
432
433/**/
434int
435execrepeat(Estate state, UNUSED(int do_exec))
436{
437    Wordcode end, loop;
438    wordcode code = state->pc[-1];
439    int count, htok = 0;
440    char *tmp;
441
442    end = state->pc + WC_REPEAT_SKIP(code);
443
444    lastval = 0;
445    tmp = ecgetstr(state, EC_DUPTOK, &htok);
446    if (htok)
447	singsub(&tmp);
448    count = atoi(tmp);
449    pushheap();
450    cmdpush(CS_REPEAT);
451    loops++;
452    loop = state->pc;
453    while (count-- > 0) {
454	state->pc = loop;
455	execlist(state, 1, 0);
456	freeheap();
457	if (breaks) {
458	    breaks--;
459	    if (breaks || !contflag)
460		break;
461	    contflag = 0;
462	}
463	if (errflag) {
464	    lastval = 1;
465	    break;
466	}
467	if (retflag)
468	    break;
469    }
470    cmdpop();
471    popheap();
472    loops--;
473    state->pc = end;
474    return lastval;
475}
476
477/**/
478int
479execif(Estate state, int do_exec)
480{
481    Wordcode end, next;
482    wordcode code = state->pc[-1];
483    int olderrexit, s = 0, run = 0;
484
485    olderrexit = noerrexit;
486    end = state->pc + WC_IF_SKIP(code);
487
488    if (!noerrexit)
489	noerrexit = 1;
490    while (state->pc < end) {
491	code = *state->pc++;
492	if (wc_code(code) != WC_IF ||
493	    (run = (WC_IF_TYPE(code) == WC_IF_ELSE))) {
494	    if (run)
495		run = 2;
496	    break;
497	}
498	next = state->pc + WC_IF_SKIP(code);
499	cmdpush(s ? CS_ELIF : CS_IF);
500	execlist(state, 1, 0);
501	cmdpop();
502	if (!lastval) {
503	    run = 1;
504	    break;
505	}
506	if (retflag)
507	    break;
508	s = 1;
509	state->pc = next;
510    }
511    noerrexit = olderrexit;
512
513    if (run) {
514	cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN));
515	execlist(state, 1, do_exec);
516	cmdpop();
517    } else
518	lastval = 0;
519    state->pc = end;
520
521    return lastval;
522}
523
524/**/
525int
526execcase(Estate state, int do_exec)
527{
528    Wordcode end, next;
529    wordcode code = state->pc[-1];
530    char *word, *pat;
531    int npat, save;
532    Patprog *spprog, pprog;
533
534    end = state->pc + WC_CASE_SKIP(code);
535
536    word = ecgetstr(state, EC_DUP, NULL);
537    singsub(&word);
538    untokenize(word);
539    lastval = 0;
540
541    cmdpush(CS_CASE);
542    while (state->pc < end) {
543	code = *state->pc++;
544	if (wc_code(code) != WC_CASE)
545	    break;
546
547	pat = NULL;
548	pprog = NULL;
549	save = 0;
550	npat = state->pc[1];
551	spprog = state->prog->pats + npat;
552
553	next = state->pc + WC_CASE_SKIP(code);
554
555	if (isset(XTRACE)) {
556	    char *opat;
557
558	    pat = dupstring(opat = ecrawstr(state->prog, state->pc, NULL));
559	    singsub(&pat);
560	    save = (!(state->prog->flags & EF_HEAP) &&
561		    !strcmp(pat, opat) && *spprog != dummy_patprog2);
562
563	    printprompt4();
564	    fprintf(xtrerr, "case %s (", word);
565	    quote_tokenized_output(pat, xtrerr);
566	    fprintf(xtrerr, ")\n");
567	    fflush(xtrerr);
568	}
569	state->pc += 2;
570
571	if (*spprog != dummy_patprog1 && *spprog != dummy_patprog2)
572	    pprog = *spprog;
573
574	if (!pprog) {
575	    if (!pat) {
576		char *opat;
577		int htok = 0;
578
579		pat = dupstring(opat = ecrawstr(state->prog,
580						state->pc - 2, &htok));
581		if (htok)
582		    singsub(&pat);
583		save = (!(state->prog->flags & EF_HEAP) &&
584			!strcmp(pat, opat) && *spprog != dummy_patprog2);
585	    }
586	    if (!(pprog = patcompile(pat, (save ? PAT_ZDUP : PAT_STATIC),
587				     NULL)))
588		zerr("bad pattern: %s", pat);
589	    else if (save)
590		*spprog = pprog;
591	}
592	if (pprog && pattry(pprog, word)) {
593	    execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) &&
594				do_exec));
595	    while (!retflag && wc_code(code) == WC_CASE &&
596		   WC_CASE_TYPE(code) == WC_CASE_AND) {
597		state->pc = next;
598		code = *state->pc;
599		state->pc += 3;
600		next = state->pc + WC_CASE_SKIP(code) - 2;
601		execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) &&
602				    do_exec));
603	    }
604	    if (WC_CASE_TYPE(code) != WC_CASE_TESTAND)
605		break;
606	}
607	state->pc = next;
608    }
609    cmdpop();
610
611    state->pc = end;
612
613    return lastval;
614}
615
616/*
617 * Errflag from `try' block, may be reset in `always' block.
618 * Accessible from an integer parameter, so needs to be a zlong.
619 */
620
621/**/
622zlong
623try_errflag = -1;
624
625/**/
626zlong
627try_tryflag = 0;
628
629/**/
630int
631exectry(Estate state, int do_exec)
632{
633    Wordcode end, always;
634    int endval;
635    int save_retflag, save_breaks, save_contflag;
636    zlong save_try_errflag, save_try_tryflag;
637
638    end = state->pc + WC_TRY_SKIP(state->pc[-1]);
639    always = state->pc + 1 + WC_TRY_SKIP(*state->pc);
640    state->pc++;
641    pushheap();
642    cmdpush(CS_CURSH);
643
644    /* The :try clause */
645    save_try_tryflag = try_tryflag;
646    try_tryflag = 1;
647
648    execlist(state, 1, do_exec);
649
650    try_tryflag = save_try_tryflag;
651
652    /* Don't record errflag here, may be reset. */
653    endval = lastval;
654
655    freeheap();
656
657    cmdpop();
658    cmdpush(CS_ALWAYS);
659
660    /* The always clause. */
661    save_try_errflag = try_errflag;
662    try_errflag = (zlong)errflag;
663    errflag = 0;
664    save_retflag = retflag;
665    retflag = 0;
666    save_breaks = breaks;
667    breaks = 0;
668    save_contflag = contflag;
669    contflag = 0;
670
671    state->pc = always;
672    execlist(state, 1, do_exec);
673
674    errflag = try_errflag ? 1 : 0;
675    try_errflag = save_try_errflag;
676    if (!retflag)
677	retflag = save_retflag;
678    if (!breaks)
679	breaks = save_breaks;
680    if (!contflag)
681	contflag = save_contflag;
682
683    cmdpop();
684    popheap();
685    state->pc = end;
686
687    return endval;
688}
689