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