1 /* Copyright (c) 1981 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_cmds.c 7.8 03/19/85"; 3 #include "ex.h" 4 #include "ex_argv.h" 5 #include "ex_temp.h" 6 #include "ex_tty.h" 7 #include "ex_vis.h" 8 9 bool pflag, nflag; 10 int poffset; 11 12 #define nochng() lchng = chng 13 14 /* 15 * Main loop for command mode command decoding. 16 * A few commands are executed here, but main function 17 * is to strip command addresses, do a little address oriented 18 * processing and call command routines to do the real work. 19 */ 20 commands(noprompt, exitoneof) 21 bool noprompt, exitoneof; 22 { 23 register line *addr; 24 register int c; 25 register int lchng; 26 int given; 27 int seensemi; 28 int cnt; 29 bool hadpr; 30 31 resetflav(); 32 nochng(); 33 for (;;) { 34 /* 35 * If dot at last command 36 * ended up at zero, advance to one if there is a such. 37 */ 38 if (dot <= zero) { 39 dot = zero; 40 if (dol > zero) 41 dot = one; 42 } 43 shudclob = 0; 44 45 /* 46 * If autoprint or trailing print flags, 47 * print the line at the specified offset 48 * before the next command. 49 */ 50 if (pflag || 51 lchng != chng && value(AUTOPRINT) && !inglobal && !inopen && endline) { 52 pflag = 0; 53 nochng(); 54 if (dol != zero) { 55 addr1 = addr2 = dot + poffset; 56 if (addr1 < one || addr1 > dol) 57 error("Offset out-of-bounds|Offset after command too large"); 58 setdot1(); 59 goto print; 60 } 61 } 62 nochng(); 63 64 /* 65 * Print prompt if appropriate. 66 * If not in global flush output first to prevent 67 * going into pfast mode unreasonably. 68 */ 69 if (inglobal == 0) { 70 flush(); 71 if (!hush && value(PROMPT) && !globp && !noprompt && endline) { 72 putchar(':'); 73 hadpr = 1; 74 } 75 TSYNC(); 76 } 77 78 /* 79 * Gobble up the address. 80 * Degenerate addresses yield ".". 81 */ 82 addr2 = 0; 83 given = seensemi = 0; 84 do { 85 addr1 = addr2; 86 addr = address(0); 87 c = getcd(); 88 if (addr == 0) 89 if (c == ',') 90 addr = dot; 91 else if (addr1 != 0) { 92 addr2 = dot; 93 break; 94 } else 95 break; 96 addr2 = addr; 97 given++; 98 if (c == ';') { 99 c = ','; 100 dot = addr; 101 seensemi = 1; 102 } 103 } while (c == ','); 104 if (c == '%') { 105 /* %: same as 1,$ */ 106 addr1 = one; 107 addr2 = dol; 108 given = 2; 109 c = getchar(); 110 } 111 if (addr1 == 0) 112 addr1 = addr2; 113 if (c == ':') 114 c = getchar(); 115 116 /* 117 * Set command name for special character commands. 118 */ 119 tailspec(c); 120 121 /* 122 * If called via : escape from open or visual, limit 123 * the set of available commands here to save work below. 124 */ 125 if (inopen) { 126 if (c=='\n' || c=='\r' || c==CTRL(d) || c==EOF) { 127 if (addr2) 128 dot = addr2; 129 if (c == EOF) 130 return; 131 continue; 132 } 133 if (any(c, "o")) 134 notinvis: 135 tailprim(Command, 1, 1); 136 } 137 choice: 138 switch (c) { 139 140 case 'a': 141 142 switch(peekchar()) { 143 case 'b': 144 /* abbreviate */ 145 tail("abbreviate"); 146 setnoaddr(); 147 mapcmd(0, 1); 148 anyabbrs = 1; 149 continue; 150 case 'r': 151 /* args */ 152 tail("args"); 153 setnoaddr(); 154 eol(); 155 pargs(); 156 continue; 157 } 158 159 /* append */ 160 if (inopen) 161 goto notinvis; 162 tail("append"); 163 setdot(); 164 aiflag = exclam(); 165 newline(); 166 vmacchng(0); 167 deletenone(); 168 setin(addr2); 169 inappend = 1; 170 ignore(append(gettty, addr2)); 171 inappend = 0; 172 nochng(); 173 continue; 174 175 case 'c': 176 switch (peekchar()) { 177 178 /* copy */ 179 case 'o': 180 tail("copy"); 181 vmacchng(0); 182 move(); 183 continue; 184 185 #ifdef CHDIR 186 /* cd */ 187 case 'd': 188 tail("cd"); 189 goto changdir; 190 191 /* chdir */ 192 case 'h': 193 ignchar(); 194 if (peekchar() == 'd') { 195 register char *p; 196 tail2of("chdir"); 197 changdir: 198 if (savedfile[0] == '/' || !value(WARN)) 199 ignore(exclam()); 200 else 201 ignore(quickly()); 202 if (skipend()) { 203 p = getenv("HOME"); 204 if (p == NULL) 205 error("Home directory unknown"); 206 } else 207 getone(), p = file; 208 eol(); 209 if (chdir(p) < 0) 210 filioerr(p); 211 if (savedfile[0] != '/') 212 edited = 0; 213 continue; 214 } 215 if (inopen) 216 tailprim("change", 2, 1); 217 tail2of("change"); 218 break; 219 220 #endif 221 default: 222 if (inopen) 223 goto notinvis; 224 tail("change"); 225 break; 226 } 227 /* change */ 228 aiflag = exclam(); 229 setCNL(); 230 vmacchng(0); 231 setin(addr1); 232 delete(0); 233 inappend = 1; 234 ignore(append(gettty, addr1 - 1)); 235 inappend = 0; 236 nochng(); 237 continue; 238 239 /* delete */ 240 case 'd': 241 /* 242 * Caution: dp and dl have special meaning already. 243 */ 244 tail("delete"); 245 c = cmdreg(); 246 setCNL(); 247 vmacchng(0); 248 if (c) 249 YANKreg(c); 250 delete(0); 251 appendnone(); 252 continue; 253 254 /* edit */ 255 /* ex */ 256 case 'e': 257 tail(peekchar() == 'x' ? "ex" : "edit"); 258 editcmd: 259 if (!exclam() && chng) 260 c = 'E'; 261 filename(c); 262 if (c == 'E') { 263 ungetchar(lastchar()); 264 ignore(quickly()); 265 } 266 setnoaddr(); 267 doecmd: 268 init(); 269 addr2 = zero; 270 laste++; 271 sync(); 272 rop(c); 273 #ifdef VMUNIX 274 tlaste(); 275 #endif 276 laste = 0; 277 sync(); 278 nochng(); 279 continue; 280 281 /* file */ 282 case 'f': 283 tail("file"); 284 setnoaddr(); 285 filename(c); 286 noonl(); 287 /* 288 synctmp(); 289 */ 290 continue; 291 292 /* global */ 293 case 'g': 294 tail("global"); 295 global(!exclam()); 296 nochng(); 297 continue; 298 299 /* insert */ 300 case 'i': 301 if (inopen) 302 goto notinvis; 303 tail("insert"); 304 setdot(); 305 nonzero(); 306 aiflag = exclam(); 307 newline(); 308 vmacchng(0); 309 deletenone(); 310 setin(addr2); 311 inappend = 1; 312 ignore(append(gettty, addr2 - 1)); 313 inappend = 0; 314 if (dot == zero && dol > zero) 315 dot = one; 316 nochng(); 317 continue; 318 319 /* join */ 320 case 'j': 321 tail("join"); 322 c = exclam(); 323 setcount(); 324 nonzero(); 325 newline(); 326 vmacchng(0); 327 if (given < 2 && addr2 != dol) 328 addr2++; 329 join(c); 330 continue; 331 332 /* k */ 333 case 'k': 334 casek: 335 pastwh(); 336 c = getchar(); 337 if (endcmd(c)) 338 serror("Mark what?|%s requires following letter", Command); 339 newline(); 340 if (!islower(c)) 341 error("Bad mark|Mark must specify a letter"); 342 setdot(); 343 nonzero(); 344 names[c - 'a'] = *addr2 &~ 01; 345 anymarks = 1; 346 continue; 347 348 /* list */ 349 case 'l': 350 tail("list"); 351 setCNL(); 352 ignorf(setlist(1)); 353 pflag = 0; 354 goto print; 355 356 case 'm': 357 if (peekchar() == 'a') { 358 ignchar(); 359 if (peekchar() == 'p') { 360 /* map */ 361 tail2of("map"); 362 setnoaddr(); 363 mapcmd(0, 0); 364 continue; 365 } 366 /* mark */ 367 tail2of("mark"); 368 goto casek; 369 } 370 /* move */ 371 tail("move"); 372 vmacchng(0); 373 move(); 374 continue; 375 376 case 'n': 377 if (peekchar() == 'u') { 378 tail("number"); 379 goto numberit; 380 } 381 /* next */ 382 tail("next"); 383 setnoaddr(); 384 ckaw(); 385 ignore(quickly()); 386 if (getargs()) 387 makargs(); 388 next(); 389 c = 'e'; 390 filename(c); 391 goto doecmd; 392 393 /* open */ 394 case 'o': 395 tail("open"); 396 oop(); 397 pflag = 0; 398 nochng(); 399 continue; 400 401 case 'p': 402 case 'P': 403 switch (peekchar()) { 404 405 /* put */ 406 case 'u': 407 tail("put"); 408 setdot(); 409 c = cmdreg(); 410 eol(); 411 vmacchng(0); 412 if (c) 413 putreg(c); 414 else 415 put(); 416 continue; 417 418 case 'r': 419 ignchar(); 420 if (peekchar() == 'e') { 421 /* preserve */ 422 tail2of("preserve"); 423 eol(); 424 if (preserve() == 0) 425 error("Preserve failed!"); 426 else 427 error("File preserved."); 428 } 429 tail2of("print"); 430 break; 431 432 default: 433 tail("print"); 434 break; 435 } 436 /* print */ 437 setCNL(); 438 pflag = 0; 439 print: 440 nonzero(); 441 if (CL && span() > LINES) { 442 flush1(); 443 vclear(); 444 } 445 plines(addr1, addr2, 1); 446 continue; 447 448 /* quit */ 449 case 'q': 450 tail("quit"); 451 setnoaddr(); 452 c = quickly(); 453 eol(); 454 if (!c) 455 quit: 456 nomore(); 457 if (inopen) { 458 vgoto(WECHO, 0); 459 if (!ateopr()) 460 vnfl(); 461 else { 462 tostop(); 463 } 464 flush(); 465 setty(normf); 466 } 467 cleanup(1); 468 exit(0); 469 470 case 'r': 471 if (peekchar() == 'e') { 472 ignchar(); 473 switch (peekchar()) { 474 475 /* rewind */ 476 case 'w': 477 tail2of("rewind"); 478 setnoaddr(); 479 if (!exclam()) { 480 ckaw(); 481 if (chng && dol > zero) 482 error("No write@since last chage (:rewind! overrides)"); 483 } 484 eol(); 485 erewind(); 486 next(); 487 c = 'e'; 488 ungetchar(lastchar()); 489 filename(c); 490 goto doecmd; 491 492 /* recover */ 493 case 'c': 494 tail2of("recover"); 495 setnoaddr(); 496 c = 'e'; 497 if (!exclam() && chng) 498 c = 'E'; 499 filename(c); 500 if (c == 'E') { 501 ungetchar(lastchar()); 502 ignore(quickly()); 503 } 504 init(); 505 addr2 = zero; 506 laste++; 507 sync(); 508 recover(); 509 rop2(); 510 revocer(); 511 if (status == 0) 512 rop3(c); 513 if (dol != zero) 514 change(); 515 #ifdef VMUNIX 516 tlaste(); 517 #endif 518 laste = 0; 519 nochng(); 520 continue; 521 } 522 tail2of("read"); 523 } else 524 tail("read"); 525 /* read */ 526 if (savedfile[0] == 0 && dol == zero) 527 c = 'e'; 528 pastwh(); 529 vmacchng(0); 530 if (peekchar() == '!') { 531 setdot(); 532 ignchar(); 533 unix0(0); 534 filter(0); 535 continue; 536 } 537 filename(c); 538 rop(c); 539 nochng(); 540 if (inopen && endline && addr1 > zero && addr1 < dol) 541 dot = addr1 + 1; 542 continue; 543 544 case 's': 545 switch (peekchar()) { 546 /* 547 * Caution: 2nd char cannot be c, g, or r 548 * because these have meaning to substitute. 549 */ 550 551 /* set */ 552 case 'e': 553 tail("set"); 554 setnoaddr(); 555 set(); 556 continue; 557 558 /* shell */ 559 case 'h': 560 tail("shell"); 561 setNAEOL(); 562 vnfl(); 563 putpad(TE); 564 flush(); 565 unixwt(1, unixex("-i", (char *) 0, 0, 0)); 566 vcontin(0); 567 continue; 568 569 /* source */ 570 case 'o': 571 #ifdef notdef 572 if (inopen) 573 goto notinvis; 574 #endif 575 tail("source"); 576 setnoaddr(); 577 getone(); 578 eol(); 579 source(file, 0); 580 continue; 581 #ifdef SIGTSTP 582 /* stop, suspend */ 583 case 't': 584 tail("stop"); 585 goto suspend; 586 case 'u': 587 tail("suspend"); 588 suspend: 589 if (!ldisc) 590 error("Old tty driver|Not using new tty driver/shell"); 591 c = exclam(); 592 eol(); 593 if (!c) 594 ckaw(); 595 onsusp(); 596 continue; 597 #endif 598 599 } 600 /* fall into ... */ 601 602 /* & */ 603 /* ~ */ 604 /* substitute */ 605 case '&': 606 case '~': 607 Command = "substitute"; 608 if (c == 's') 609 tail(Command); 610 vmacchng(0); 611 if (!substitute(c)) 612 pflag = 0; 613 continue; 614 615 /* t */ 616 case 't': 617 if (peekchar() == 'a') { 618 tail("tag"); 619 tagfind(exclam()); 620 if (!inopen) 621 lchng = chng - 1; 622 else 623 nochng(); 624 continue; 625 } 626 tail("t"); 627 vmacchng(0); 628 move(); 629 continue; 630 631 case 'u': 632 if (peekchar() == 'n') { 633 ignchar(); 634 switch(peekchar()) { 635 /* unmap */ 636 case 'm': 637 tail2of("unmap"); 638 setnoaddr(); 639 mapcmd(1, 0); 640 continue; 641 /* unabbreviate */ 642 case 'a': 643 tail2of("unabbreviate"); 644 setnoaddr(); 645 mapcmd(1, 1); 646 anyabbrs = 1; 647 continue; 648 } 649 /* undo */ 650 tail2of("undo"); 651 } else 652 tail("undo"); 653 setnoaddr(); 654 markDOT(); 655 c = exclam(); 656 newline(); 657 undo(c); 658 continue; 659 660 case 'v': 661 switch (peekchar()) { 662 663 case 'e': 664 /* version */ 665 tail("version"); 666 setNAEOL(); 667 printf("@(#) Version 3.7, 03/19/85."+5); 668 noonl(); 669 continue; 670 671 /* visual */ 672 case 'i': 673 tail("visual"); 674 if (inopen) { 675 c = 'e'; 676 goto editcmd; 677 } 678 vop(); 679 pflag = 0; 680 nochng(); 681 continue; 682 } 683 /* v */ 684 tail("v"); 685 global(0); 686 nochng(); 687 continue; 688 689 /* write */ 690 case 'w': 691 c = peekchar(); 692 tail(c == 'q' ? "wq" : "write"); 693 wq: 694 if (skipwh() && peekchar() == '!') { 695 pofix(); 696 ignchar(); 697 setall(); 698 unix0(0); 699 filter(1); 700 } else { 701 setall(); 702 wop(1); 703 nochng(); 704 } 705 if (c == 'q') 706 goto quit; 707 continue; 708 709 /* xit */ 710 case 'x': 711 tail("xit"); 712 if (!chng) 713 goto quit; 714 c = 'q'; 715 goto wq; 716 717 /* yank */ 718 case 'y': 719 tail("yank"); 720 c = cmdreg(); 721 setcount(); 722 eol(); 723 vmacchng(0); 724 if (c) 725 YANKreg(c); 726 else 727 yank(); 728 continue; 729 730 /* z */ 731 case 'z': 732 zop(0); 733 pflag = 0; 734 continue; 735 736 /* * */ 737 /* @ */ 738 case '*': 739 case '@': 740 c = getchar(); 741 if (c=='\n' || c=='\r') 742 ungetchar(c); 743 if (any(c, "@*\n\r")) 744 c = lastmac; 745 if (isupper(c)) 746 c = tolower(c); 747 if (!islower(c)) 748 error("Bad register"); 749 newline(); 750 setdot(); 751 cmdmac(c); 752 continue; 753 754 /* | */ 755 case '|': 756 endline = 0; 757 goto caseline; 758 759 /* \n */ 760 case '\n': 761 endline = 1; 762 caseline: 763 notempty(); 764 if (addr2 == 0) { 765 if (UP != NOSTR && c == '\n' && !inglobal) 766 c = CTRL(k); 767 if (inglobal) 768 addr1 = addr2 = dot; 769 else { 770 if (dot == dol) 771 error("At EOF|At end-of-file"); 772 addr1 = addr2 = dot + 1; 773 } 774 } 775 setdot(); 776 nonzero(); 777 if (seensemi) 778 addr1 = addr2; 779 getline(*addr1); 780 if (c == CTRL(k)) { 781 flush1(); 782 destline--; 783 if (hadpr) 784 shudclob = 1; 785 } 786 plines(addr1, addr2, 1); 787 continue; 788 789 /* " */ 790 case '"': 791 comment(); 792 continue; 793 794 /* # */ 795 case '#': 796 numberit: 797 setCNL(); 798 ignorf(setnumb(1)); 799 pflag = 0; 800 goto print; 801 802 /* = */ 803 case '=': 804 newline(); 805 setall(); 806 if (inglobal == 2) 807 pofix(); 808 printf("%d", lineno(addr2)); 809 noonl(); 810 continue; 811 812 /* ! */ 813 case '!': 814 if (addr2 != 0) { 815 vmacchng(0); 816 unix0(0); 817 setdot(); 818 filter(2); 819 } else { 820 unix0(1); 821 pofix(); 822 putpad(TE); 823 flush(); 824 unixwt(1, unixex("-c", uxb, 0, 0)); 825 vclrech(1); /* vcontin(0); */ 826 nochng(); 827 } 828 continue; 829 830 /* < */ 831 /* > */ 832 case '<': 833 case '>': 834 for (cnt = 1; peekchar() == c; cnt++) 835 ignchar(); 836 setCNL(); 837 vmacchng(0); 838 shift(c, cnt); 839 continue; 840 841 /* ^D */ 842 /* EOF */ 843 case CTRL(d): 844 case EOF: 845 if (exitoneof) { 846 if (addr2 != 0) 847 dot = addr2; 848 return; 849 } 850 if (!isatty(0)) { 851 if (intty) 852 /* 853 * Chtty sys call at UCB may cause a 854 * input which was a tty to suddenly be 855 * turned into /dev/null. 856 */ 857 onhup(); 858 return; 859 } 860 if (addr2 != 0) { 861 setlastchar('\n'); 862 putnl(); 863 } 864 if (dol == zero) { 865 if (addr2 == 0) 866 putnl(); 867 notempty(); 868 } 869 ungetchar(EOF); 870 zop(hadpr); 871 continue; 872 873 default: 874 if (!isalpha(c)) 875 break; 876 ungetchar(c); 877 tailprim("", 0, 0); 878 } 879 error("What?|Unknown command character '%c'", c); 880 } 881 } 882