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