1: /* Copyright (c) 1979 Regents of the University of California */ 2: #include "ex.h" 3: #include "ex_tty.h" 4: #include "ex_vis.h" 5: 6: /* 7: * Low level routines for operations sequences, 8: * and mostly, insert mode (and a subroutine 9: * to read an input line, including in the echo area.) 10: */ 11: char *vUA1, *vUA2; 12: char *vUD1, *vUD2; 13: 14: /* 15: * Obleeperate characters in hardcopy 16: * open with \'s. 17: */ 18: bleep(i, cp) 19: register int i; 20: char *cp; 21: { 22: 23: i -= column(cp); 24: do 25: putchar('\\' | QUOTE); 26: while (--i >= 0); 27: rubble = 1; 28: } 29: 30: /* 31: * Common code for middle part of delete 32: * and change operating on parts of lines. 33: */ 34: vdcMID() 35: { 36: register char *cp; 37: 38: squish(); 39: setLAST(); 40: vundkind = VCHNG, CP(vutmp, linebuf); 41: if (wcursor < cursor) 42: cp = wcursor, wcursor = cursor, cursor = cp; 43: vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor; 44: return (column(wcursor - 1)); 45: } 46: 47: /* 48: * Take text from linebuf and stick it 49: * in the VBSIZE buffer BUF. Used to save 50: * deleted text of part of line. 51: */ 52: takeout(BUF) 53: char *BUF; 54: { 55: register char *cp; 56: 57: if (wcursor < linebuf) 58: wcursor = linebuf; 59: if (cursor == wcursor) { 60: beep(); 61: return; 62: } 63: if (wcursor < cursor) { 64: cp = wcursor; 65: wcursor = cursor; 66: cursor = cp; 67: } 68: setBUF(BUF); 69: if ((BUF[0] & (QUOTE|TRIM)) == OVERBUF) 70: beep(); 71: } 72: 73: /* 74: * Are we at the end of the printed representation of the 75: * line? Used internally in hardcopy open. 76: */ 77: ateopr() 78: { 79: register int i, c; 80: register char *cp = vtube[destline] + destcol; 81: 82: for (i = WCOLS - destcol; i > 0; i--) { 83: c = *cp++; 84: if (c == 0) 85: return (1); 86: if (c != ' ' && (c & QUOTE) == 0) 87: return (0); 88: } 89: return (1); 90: } 91: 92: /* 93: * Append. 94: * 95: * This routine handles the top level append, doing work 96: * as each new line comes in, and arranging repeatability. 97: * It also handles append with repeat counts, and calculation 98: * of autoindents for new lines. 99: */ 100: bool vaifirst; 101: bool gobbled; 102: char *ogcursor; 103: 104: vappend(ch, cnt, indent) 105: char ch; 106: int cnt, indent; 107: { 108: register int i; 109: register char *gcursor; 110: bool escape; 111: int repcnt; 112: short oldhold = hold; 113: 114: #ifdef OPENCODE 115: /* 116: * Before a move in hardopen when the line is dirty 117: * or we are in the middle of the printed representation, 118: * we retype the line to the left of the cursor so the 119: * insert looks clean. 120: */ 121: if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) { 122: rubble = 1; 123: gcursor = cursor; 124: i = *gcursor; 125: *gcursor = ' '; 126: wcursor = gcursor; 127: vmove(); 128: *gcursor = i; 129: } 130: #endif 131: vaifirst = indent == 0; 132: 133: /* 134: * Handle replace character by (eventually) 135: * limiting the number of input characters allowed 136: * in the vgetline routine. 137: */ 138: if (ch == 'r') 139: repcnt = 2; 140: else 141: repcnt = 0; 142: 143: /* 144: * If an autoindent is specified, then 145: * generate a mixture of blanks to tabs to implement 146: * it and place the cursor after the indent. 147: * Text read by the vgetline routine will be placed in genbuf, 148: * so the indent is generated there. 149: */ 150: if (value(AUTOINDENT) && indent != 0) { 151: gcursor = genindent(indent); 152: *gcursor = 0; 153: vgotoCL(qcolumn(cursor - 1, genbuf)); 154: } else { 155: gcursor = genbuf; 156: *gcursor = 0; 157: if (ch == 'o') 158: vfixcurs(); 159: } 160: 161: /* 162: * Prepare for undo. Pointers delimit inserted portion of line. 163: */ 164: vUA1 = vUA2 = cursor; 165: 166: /* 167: * If we are not in a repeated command and a ^@ comes in 168: * then this means the previous inserted text. 169: * If there is none or it was too long to be saved, 170: * then beep() and also arrange to undo any damage done 171: * so far (e.g. if we are a change.) 172: */ 173: if ((vglobp && *vglobp == 0) || peekbr()) { 174: if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) { 175: beep(); 176: if (!splitw) 177: ungetkey('u'); 178: doomed = 0; 179: hold = oldhold; 180: return; 181: } 182: /* 183: * Unread input from INS. 184: * An escape will be generated at end of string. 185: * Hold off n^^2 type update on dumb terminals. 186: */ 187: vglobp = INS; 188: hold |= HOLDQIK; 189: } else if (vglobp == 0) 190: /* 191: * Not a repeated command, get 192: * a new inserted text for repeat. 193: */ 194: INS[0] = 0; 195: 196: /* 197: * For wrapmargin to hack away second space after a '.' 198: * when the first space caused a line break we keep 199: * track that this happened in gobblebl, which says 200: * to gobble up a blank silently. 201: */ 202: gobblebl = 0; 203: 204: /* 205: * Text gathering loop. 206: * New text goes into genbuf starting at gcursor. 207: * cursor preserves place in linebuf where text will eventually go. 208: */ 209: if (*cursor == 0 210: #ifdef OPENCODE 211: || state == CRTOPEN 212: #endif 213: ) 214: hold |= HOLDROL; 215: for (;;) { 216: if (ch == 'r' && repcnt == 0) 217: escape = 0; 218: else { 219: gcursor = vgetline(repcnt, gcursor, &escape); 220: 221: /* 222: * After an append, stick information 223: * about the ^D's and ^^D's and 0^D's in 224: * the repeated text buffer so repeated 225: * inserts of stuff indented with ^D as backtab's 226: * can work. 227: */ 228: if (HADUP) 229: addtext("^"); 230: else if (HADZERO) 231: addtext("0"); 232: while (CDCNT > 0) 233: addtext("\204"), CDCNT--; 234: if (gobbled) 235: addtext(" "); 236: addtext(ogcursor); 237: } 238: repcnt = 0; 239: 240: /* 241: * Smash the generated and preexisting indents together 242: * and generate one cleanly made out of tabs and spaces 243: * if we are using autoindent. 244: */ 245: if (!vaifirst && value(AUTOINDENT)) { 246: i = fixindent(indent); 247: if (!HADUP) 248: indent = i; 249: gcursor = strend(genbuf); 250: } 251: 252: /* 253: * Limit the repetition count based on maximum 254: * possible line length; do output implied 255: * by further count (> 1) and cons up the new line 256: * in linebuf. 257: */ 258: cnt = vmaxrep(ch, cnt); 259: CP(gcursor + 1, cursor); 260: do { 261: CP(cursor, genbuf); 262: if (cnt > 1) { 263: int oldhold = hold; 264: 265: Outchar = vinschar; 266: hold |= HOLDQIK; 267: printf("%s", genbuf); 268: hold = oldhold; 269: Outchar = vputchar; 270: } 271: cursor += gcursor - genbuf; 272: } while (--cnt > 0); 273: endim(); 274: vUA2 = cursor; 275: if (escape != '\n') 276: CP(cursor, gcursor + 1); 277: 278: /* 279: * If doomed characters remain, clobber them, 280: * and reopen the line to get the display exact. 281: */ 282: #ifdef OPENCODE 283: if (state != HARDOPEN) { 284: #endif 285: DEPTH(vcline) = 0; 286: if (doomed > 0) { 287: register int cind = cindent(); 288: 289: physdc(cind, cind + doomed); 290: doomed = 0; 291: } 292: i = vreopen(LINE(vcline), lineDOT(), vcline); 293: #ifdef OPENCODE 294: } 295: #endif 296: 297: /* 298: * All done unless we are continuing on to another line. 299: */ 300: if (escape != '\n') 301: break; 302: 303: /* 304: * Set up for the new line. 305: * First save the current line, then construct a new 306: * first image for the continuation line consisting 307: * of any new autoindent plus the pushed ahead text. 308: */ 309: killU(); 310: addtext(gobblebl ? " " : "\n"); 311: vsave(); 312: cnt = 1; 313: if (value(AUTOINDENT)) { 314: #ifdef LISPCODE 315: if (value(LISP)) 316: indent = lindent(dot + 1); 317: else 318: #endif 319: if (!HADUP && vaifirst) 320: indent = whitecnt(linebuf); 321: vaifirst = 0; 322: strcLIN(vpastwh(gcursor + 1)); 323: gcursor = genindent(indent); 324: *gcursor = 0; 325: if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2]) 326: gcursor = genbuf; 327: CP(gcursor, linebuf); 328: } else { 329: CP(genbuf, gcursor + 1); 330: gcursor = genbuf; 331: } 332: 333: /* 334: * If we started out as a single line operation and are now 335: * turning into a multi-line change, then we had better yank 336: * out dot before it changes so that undo will work 337: * correctly later. 338: */ 339: if (vundkind == VCHNG) { 340: vremote(1, yank, 0); 341: undap1--; 342: } 343: 344: /* 345: * Now do the append of the new line in the buffer, 346: * and update the display. If slowopen 347: * we don't do very much. 348: */ 349: vdoappend(genbuf); 350: vundkind = VMANYINS; 351: vcline++; 352: #ifdef OPENCODE 353: if (state != VISUAL) 354: vshow(dot, NOLINE); 355: else { 356: #endif 357: i += LINE(vcline - 1); 358: vopen(dot, i); 359: if (value(SLOWOPEN)) 360: vscrap(); 361: else 362: vsync1(LINE(vcline)); 363: #ifdef OPENCODE 364: } 365: #endif 366: strcLIN(gcursor); 367: *gcursor = 0; 368: cursor = linebuf; 369: vgotoCL(qcolumn(cursor - 1, genbuf)); 370: } 371: 372: /* 373: * All done with insertion, position the cursor 374: * and sync the screen. 375: */ 376: hold = oldhold; 377: if (cursor > linebuf) 378: cursor--; 379: #ifdef OPENCODE 380: if (state != HARDOPEN) 381: #endif 382: vsyncCL(); 383: #ifdef OPENCODE 384: else if (cursor > linebuf) 385: back1(); 386: #endif 387: doomed = 0; 388: wcursor = cursor; 389: vmove(); 390: } 391: 392: /* 393: * Subroutine for vgetline to back up a single character position, 394: * backwards around end of lines (vgoto can't hack columns which are 395: * less than 0 in general). 396: */ 397: back1() 398: { 399: 400: vgoto(destline - 1, WCOLS + destcol - 1); 401: } 402: 403: /* 404: * Get a line into genbuf after gcursor. 405: * Cnt limits the number of input characters 406: * accepted and is used for handling the replace 407: * single character command. Aescaped is the location 408: * where we stick a termination indicator (whether we 409: * ended with an ESCAPE or a newline/return. 410: * 411: * We do erase-kill type processing here and also 412: * are careful about the way we do this so that it is 413: * repeatable. (I.e. so that your kill doesn't happen, 414: * when you repeat an insert if it was escaped with \ the 415: * first time you did it. 416: */ 417: char * 418: vgetline(cnt, gcursor, aescaped) 419: int cnt; 420: register char *gcursor; 421: bool *aescaped; 422: { 423: register int c, ch; 424: register char *cp; 425: int x, y, iwhite; 426: char *iglobp; 427: int (*OO)() = Outchar; 428: 429: /* 430: * Clear the output state and counters 431: * for autoindent backwards motion (counts of ^D, etc.) 432: * Remember how much white space at beginning of line so 433: * as not to allow backspace over autoindent. 434: */ 435: *aescaped = 0; 436: ogcursor = gcursor; 437: flusho(); 438: CDCNT = 0; 439: HADUP = 0; 440: HADZERO = 0; 441: gobbled = 0; 442: iwhite = whitecnt(genbuf); 443: iglobp = vglobp; 444: 445: /* 446: * Carefully avoid using vinschar in the echo area. 447: */ 448: if (splitw) 449: Outchar = vputchar; 450: else { 451: Outchar = vinschar; 452: vprepins(); 453: } 454: for (;;) { 455: if (gobblebl) 456: gobblebl--; 457: if (cnt != 0) { 458: cnt--; 459: if (cnt == 0) 460: goto vadone; 461: } 462: c = getkey(); 463: if (c != ATTN) 464: c &= (QUOTE|TRIM); 465: ch = c; 466: if (!iglobp) { 467: 468: /* 469: * Erase-kill type processing. 470: * Only happens if we were not reading 471: * from untyped input when we started. 472: * Map users erase to ^H, kill to -1 for switch. 473: */ 474: #ifndef USG3TTY 475: if (c == tty.sg_erase) 476: c = CTRL(h); 477: else if (c == tty.sg_kill) 478: c = -1; 479: #else 480: if (c == tty.c_cc[VERASE]) 481: c = CTRL(h); 482: else if (c == tty.c_cc[VKILL]) 483: c = -1; 484: #endif 485: switch (c) { 486: 487: /* 488: * ^? Interrupt drops you back to visual 489: * command mode with an unread interrupt 490: * still in the input buffer. 491: * 492: * ^\ Quit does the same as interrupt. 493: * If you are a ex command rather than 494: * a vi command this will drop you 495: * back to command mode for sure. 496: */ 497: case ATTN: 498: case QUIT: 499: ungetkey(c); 500: goto vadone; 501: 502: /* 503: * ^H Backs up a character in the input. 504: * 505: * BUG: Can't back around line boundaries. 506: * This is hard because stuff has 507: * already been saved for repeat. 508: */ 509: case CTRL(h): 510: bakchar: 511: cp = gcursor - 1; 512: if (cp < ogcursor) { 513: if (splitw) { 514: /* 515: * Backspacing over readecho 516: * prompt. Pretend delete but 517: * don't beep. 518: */ 519: ungetkey(c); 520: goto vadone; 521: } 522: beep(); 523: continue; 524: } 525: goto vbackup; 526: 527: /* 528: * ^W Back up a white/non-white word. 529: */ 530: case CTRL(w): 531: wdkind = 1; 532: for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--) 533: continue; 534: for (c = wordch(cp - 1); 535: cp > ogcursor && wordof(c, cp - 1); cp--) 536: continue; 537: goto vbackup; 538: 539: /* 540: * users kill Kill input on this line, back to 541: * the autoindent. 542: */ 543: case -1: 544: cp = ogcursor; 545: vbackup: 546: if (cp == gcursor) { 547: beep(); 548: continue; 549: } 550: endim(); 551: *cp = 0; 552: c = cindent(); 553: vgotoCL(qcolumn(cursor - 1, genbuf)); 554: if (doomed >= 0) 555: doomed += c - cindent(); 556: gcursor = cp; 557: continue; 558: 559: /* 560: * \ Followed by erase or kill 561: * maps to just the erase or kill. 562: */ 563: case '\\': 564: x = destcol, y = destline; 565: putchar('\\'); 566: vcsync(); 567: c = getkey(); 568: #ifndef USG3TTY 569: if (c == tty.sg_erase || c == tty.sg_kill) { 570: #else 571: if (c == tty.c_cc[VERASE] 572: || c == tty.c_cc[VKILL]) { 573: #endif 574: vgoto(y, x); 575: if (doomed >= 0) 576: doomed++; 577: goto def; 578: } 579: ungetkey(c), c = '\\'; 580: goto noput; 581: 582: /* 583: * ^Q Super quote following character 584: * Only ^@ is verboten (trapped at 585: * a lower level) and \n forces a line 586: * split so doesn't really go in. 587: * 588: * ^V Synonym for ^Q 589: */ 590: case CTRL(q): 591: case CTRL(v): 592: x = destcol, y = destline; 593: putchar('^'); 594: vgoto(y, x); 595: c = getkey(); 596: #ifdef TIOCSETC 597: if (c == ATTN) 598: c = nttyc.t_intrc; 599: #endif 600: if (c != NL) { 601: if (doomed >= 0) 602: doomed++; 603: goto def; 604: } 605: break; 606: } 607: } 608: 609: #ifdef WRAPMGN 610: /* 611: * If we get a blank not in the echo area 612: * consider splitting the window in the wrapmargin. 613: */ 614: if (c == ' ' && !splitw) { 615: if (gobblebl) { 616: gobbled = 1; 617: continue; 618: } 619: if (value(WRAPMARGIN) && outcol >= OCOLUMNS - value(WRAPMARGIN)) { 620: c = NL; 621: gobblebl = 2; 622: } 623: } 624: #endif 625: switch (c) { 626: 627: /* 628: * ^M Except in repeat maps to \n. 629: */ 630: case CR: 631: if (vglobp) 632: goto def; 633: c = '\n'; 634: /* presto chango ... */ 635: 636: /* 637: * \n Start new line. 638: */ 639: case NL: 640: *aescaped = c; 641: goto vadone; 642: 643: /* 644: * escape End insert unless repeat and more to repeat. 645: */ 646: case ESCAPE: 647: if (lastvgk) 648: goto def; 649: goto vadone; 650: 651: /* 652: * ^D Backtab. 653: * ^T Software forward tab. 654: * 655: * Unless in repeat where this means these 656: * were superquoted in. 657: */ 658: case CTRL(d): 659: #ifdef SOFTTABS 660: case CTRL(t): 661: if (vglobp) 662: goto def; 663: /* fall into ... */ 664: #endif 665: 666: /* 667: * ^D|QUOTE Is a backtab (in a repeated command). 668: */ 669: case CTRL(d) | QUOTE: 670: *gcursor = 0; 671: cp = vpastwh(genbuf); 672: c = whitecnt(genbuf); 673: #ifdef SOFTTABS 674: if (ch == CTRL(t)) { 675: /* 676: * ^t just generates new indent replacing 677: * current white space rounded up to soft 678: * tab stop increment. 679: */ 680: if (cp != gcursor) 681: /* 682: * BUG: Don't hack ^T except right 683: * after right after initial white 684: * space. This doesn't work right 685: * anyway, but is not being fixed 686: * since the official GOOD THING TO DO 687: * is to set TABSTOP and SHIFTWIDTH 688: * and use ctrl I, not ctrl T. 689: */ 690: continue; 691: cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1)); 692: ogcursor = cp; 693: goto vbackup; 694: } 695: #endif 696: /* 697: * ^D works only if we are at the (end of) the 698: * generated autoindent. We count the ^D for repeat 699: * purposes. 700: */ 701: if (c == iwhite && c != 0) 702: if (cp == gcursor) { 703: iwhite = backtab(c); 704: CDCNT++; 705: ogcursor = cp = genindent(iwhite); 706: goto vbackup; 707: } else if (&cp[1] == gcursor && 708: (*cp == '^' || *cp == '0')) { 709: /* 710: * ^^D moves to margin, then back 711: * to current indent on next line. 712: * 713: * 0^D moves to margin and then 714: * stays there. 715: */ 716: HADZERO = *cp == '0'; 717: ogcursor = cp = genbuf; 718: HADUP = 1 - HADZERO; 719: CDCNT = 1; 720: endim(); 721: back1(); 722: vputchar(' '); 723: goto vbackup; 724: } 725: if (vglobp && vglobp - iglobp >= 2 && 726: (vglobp[-2] == '^' || vglobp[-2] == '0') 727: && gcursor == ogcursor + 1) 728: goto bakchar; 729: continue; 730: 731: default: 732: /* 733: * Possibly discard control inputs. 734: */ 735: if (!vglobp && junk(c)) { 736: beep(); 737: continue; 738: } 739: def: 740: putchar(c); 741: noput: 742: if (gcursor > &genbuf[LBSIZE - 2]) 743: error("Line too long"); 744: *gcursor++ = c & TRIM; 745: vcsync(); 746: if (value(SHOWMATCH) && !iglobp) 747: if (c == ')' || c == '}') 748: lsmatch(gcursor); 749: continue; 750: } 751: } 752: vadone: 753: *gcursor = 0; 754: Outchar = OO; 755: endim(); 756: return (gcursor); 757: } 758: 759: int vgetsplit(); 760: char *vsplitpt; 761: 762: /* 763: * Append the line in buffer at lp 764: * to the buffer after dot. 765: */ 766: vdoappend(lp) 767: char *lp; 768: { 769: register int oing = inglobal; 770: 771: vsplitpt = lp; 772: inglobal = 1; 773: ignore(append(vgetsplit, dot)); 774: inglobal = oing; 775: } 776: 777: /* 778: * Subroutine for vdoappend to pass to append. 779: */ 780: vgetsplit() 781: { 782: 783: if (vsplitpt == 0) 784: return (EOF); 785: strcLIN(vsplitpt); 786: vsplitpt = 0; 787: return (0); 788: } 789: 790: /* 791: * Vmaxrep determines the maximum repetitition factor 792: * allowed that will yield total line length less than 793: * 512 characters and also does hacks for the R command. 794: */ 795: vmaxrep(ch, cnt) 796: char ch; 797: register int cnt; 798: { 799: register int len, replen; 800: 801: if (cnt > LBSIZE - 2) 802: cnt = LBSIZE - 2; 803: replen = strlen(genbuf); 804: if (ch == 'R') { 805: len = strlen(cursor); 806: if (replen < len) 807: len = replen; 808: CP(cursor, cursor + len); 809: vUD2 += len; 810: } 811: len = strlen(linebuf); 812: if (len + cnt * replen <= LBSIZE - 2) 813: return (cnt); 814: cnt = (LBSIZE - 2 - len) / replen; 815: if (cnt == 0) { 816: vsave(); 817: error("Line too long"); 818: } 819: return (cnt); 820: }