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