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