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:  * This is the main routine for visual.
   8:  * We here decode the count and possible named buffer specification
   9:  * preceding a command and interpret a few of the commands.
  10:  * Commands which involve a target (i.e. an operator) are decoded
  11:  * in the routine operate in ex_voperate.c.
  12:  */
  13: 
  14: #define forbid(a)   { if (a) goto fonfon; }
  15: 
  16: vmain()
  17: {
  18:     register int c, cnt, i;
  19:     char esave[TUBECOLS];
  20:     char *oglobp;
  21:     char d;
  22:     line *addr;
  23:     int ind;
  24:     int onumber, olist, (*OPline)(), (*OPutchar)();
  25: 
  26:     /*
  27: 	 * If we started as a vi command (on the command line)
  28: 	 * then go process initial commands (recover, next or tag).
  29: 	 */
  30:     if (initev) {
  31:         oglobp = globp;
  32:         globp = initev;
  33:         hadcnt = cnt = 0;
  34:         i = tchng;
  35:         addr = dot;
  36:         goto doinit;
  37:     }
  38: 
  39:     /*
  40: 	 * NB:
  41: 	 *
  42: 	 * The current line is always in the line buffer linebuf,
  43: 	 * and the cursor at the position cursor.  You should do
  44: 	 * a vsave() before moving off the line to make sure the disk
  45: 	 * copy is updated if it has changed, and a getDOT() to get
  46: 	 * the line back if you mung linebuf.  The motion
  47: 	 * routines in ex_vwind.c handle most of this.
  48: 	 */
  49:     for (;;) {
  50:         /*
  51: 		 * Decode a visual command.
  52: 		 * First sync the temp file if there has been a reasonable
  53: 		 * amount of change.  Clear state for decoding of next
  54: 		 * command.
  55: 		 */
  56:         TSYNC();
  57:         vglobp = 0;
  58:         vreg = 0;
  59:         hold = 0;
  60:         wcursor = 0;
  61:         Xhadcnt = hadcnt = 0;
  62:         Xcnt = cnt = 1;
  63:         splitw = 0;
  64:         if (i = holdupd) {
  65: #ifdef OPENCODE
  66:             if (state == VISUAL)
  67: #endif
  68:                 ignore(peekkey());
  69:             holdupd = 0;
  70: /*
  71: 			if (LINE(0) < ZERO) {
  72: 				vclear();
  73: 				vcnt = 0;
  74: 				i = 3;
  75: 			}
  76: */
  77: #ifdef OPENCODE
  78:             if (state != VISUAL) {
  79:                 vcnt = 0;
  80:                 vsave();
  81:                 vrepaint(cursor);
  82:             } else
  83: #endif
  84:             if (i == 3)
  85:                 vredraw(WTOP);
  86:             else
  87:                 vsync(WTOP);
  88:             vfixcurs();
  89:         }
  90: 
  91:         /*
  92: 		 * Gobble up counts and named buffer specifications.
  93: 		 */
  94:         for (;;) {
  95:             if (isdigit(peekkey()) && peekkey() != '0') {
  96:                 hadcnt = 1;
  97:                 cnt = vgetcnt();
  98:                 forbid (cnt <= 0);
  99:             }
 100:             if (peekkey() != '"')
 101:                 break;
 102:             ignore(getkey()), c = getkey();
 103:             /*
 104: 			 * Buffer names be letters or digits.
 105: 			 * But not '0' as that is the source of
 106: 			 * an 'empty' named buffer spec in the routine
 107: 			 * kshift (see ex_temp.c).
 108: 			 */
 109:             forbid (c == '0' || !isalpha(c) && !isdigit(c));
 110:             vreg = c;
 111:         }
 112: reread:
 113:         /*
 114: 		 * Come to reread from below after some macro expansions.
 115: 		 * The call to map allows use of function key pads
 116: 		 * by performing a terminal dependent mapping of inputs,
 117: 		 * but notably not of two character inputs starting with
 118: 		 * an escape (as from a VT52 keypad).  This is useful for
 119: 		 * e.g. DM1520's however.
 120: 		 */
 121:         op = c = map(getkey());
 122: 
 123:         /*
 124: 		 * Begin to build an image of this command for possible
 125: 		 * later repeat in the buffer workcmd.  It will be copied
 126: 		 * to lastcmd by the routine setLAST
 127: 		 * if/when completely specified.
 128: 		 */
 129:         lastcp = workcmd;
 130:         if (!vglobp)
 131:             *lastcp++ = c;
 132: 
 133:         /*
 134: 		 * First level command decode.
 135: 		 */
 136:         switch (c) {
 137: 
 138:         /*
 139: 		 * ^L		Clear screen e.g. after transmission error.
 140: 		 */
 141:         case CTRL(l):
 142:             vclear();
 143:             vdirty(0, vcnt);
 144:             /* fall into... */
 145: 
 146:         /*
 147: 		 * ^R		Retype screen, getting rid of @ lines.
 148: 		 *		If in open, equivalent to ^L.
 149: 		 */
 150:         case CTRL(r):
 151: #ifdef OPENCODE
 152:             if (state != VISUAL) {
 153:                 /*
 154: 				 * Get a clean line, throw away the
 155: 				 * memory of what is displayed now,
 156: 				 * and move back onto the current line.
 157: 				 */
 158:                 vclean();
 159:                 vcnt = 0;
 160:                 vmoveto(dot, cursor, 0);
 161:                 continue;
 162:             }
 163: #endif
 164:             vredraw(WTOP);
 165:             /*
 166: 			 * Weird glitch -- when we enter visual
 167: 			 * in a very small window we may end up with
 168: 			 * no lines on the screen because the line
 169: 			 * at the top is too long.  This forces the screen
 170: 			 * to be expanded to make room for it (after
 171: 			 * we have printed @'s ick showing we goofed).
 172: 			 */
 173:             if (vcnt == 0)
 174:                 vrepaint(cursor);
 175:             vfixcurs();
 176:             continue;
 177: 
 178:         /*
 179: 		 * $		Escape just cancels the current command
 180: 		 *		with a little feedback.  It would be
 181: 		 *		nice to handle function pads sending ESC-X;
 182: 		 *		here, using something else to replace ESC.
 183: 		 */
 184:         case ESCAPE:
 185:             beep();
 186:             continue;
 187: 
 188:         /*
 189: 		 * .		Repeat the last (modifying) open/visual command.
 190: 		 */
 191:         case '.':
 192:             /*
 193: 			 * Check that there was a last command, and
 194: 			 * take its count and named buffer unless they
 195: 			 * were given anew.  Special case if last command
 196: 			 * referenced a numeric named buffer -- increment
 197: 			 * the number and go to a named buffer again.
 198: 			 * This allows a sequence like "1pu.u.u...
 199: 			 * to successively look for stuff in the kill chain
 200: 			 * much as one does in EMACS with C-Y and M-Y.
 201: 			 */
 202:             forbid (lastcmd[0] == 0);
 203:             if (hadcnt)
 204:                 lastcnt = cnt;
 205:             if (vreg)
 206:                 lastreg = vreg;
 207:             else if (isdigit(lastreg) && lastreg < '9')
 208:                 lastreg++;
 209:             vreg = lastreg;
 210:             cnt = lastcnt;
 211:             hadcnt = lasthad;
 212:             vglobp = lastcmd;
 213:             goto reread;
 214: 
 215:         /*
 216: 		 * ^U		Scroll up.  A count sticks around for
 217: 		 *		future scrolls as the scroll amount.
 218: 		 *		Attempt to hold the indentation from the
 219: 		 *		top of the screen (in logical lines).
 220: 		 *
 221: 		 * BUG:		A ^U near the bottom of the screen
 222: 		 *		on a dumb terminal (which can't roll back)
 223: 		 *		causes the screen to be cleared and then
 224: 		 *		redrawn almost as it was.  In this case
 225: 		 *		one should simply move the cursor.
 226: 		 */
 227:         case CTRL(u):
 228:             if (hadcnt)
 229:                 vSCROLL = cnt;
 230:             cnt = vSCROLL;
 231: #ifdef OPENCODE
 232:             if (state == VISUAL)
 233: #endif
 234:                 ind = vcline, cnt += ind;
 235: #ifdef OPENCODE
 236:             else
 237:                 ind = 0;
 238: #endif
 239:             vmoving = 0;
 240:             vup(cnt, ind, 1);
 241:             vnline(NOSTR);
 242:             continue;
 243: 
 244:         /*
 245: 		 * ^D		Scroll down.  Like scroll up.
 246: 		 */
 247:         case CTRL(d):
 248:             if (hadcnt)
 249:                 vSCROLL = cnt;
 250:             cnt = vSCROLL;
 251: #ifdef OPENCODE
 252:             if (state == VISUAL)
 253: #endif
 254:                 ind = vcnt - vcline - 1, cnt += ind;
 255: #ifdef OPENCODE
 256:             else
 257:                 ind = 0;
 258: #endif
 259:             vmoving = 0;
 260:             vdown(cnt, ind, 1);
 261:             vnline(NOSTR);
 262:             continue;
 263: 
 264:         /*
 265: 		 * m		Mark position in mark register given
 266: 		 *		by following letter.  Return is
 267: 		 *		accomplished via ' or `; former
 268: 		 *		to beginning of line where mark
 269: 		 *		was set, latter to column where marked.
 270: 		 */
 271:         case 'm':
 272:             /*
 273: 			 * Getesc is generally used when a character
 274: 			 * is read as a latter part of a command
 275: 			 * to allow one to hit rubout/escape to cancel
 276: 			 * what you have typed so far.  These characters
 277: 			 * are mapped to 0 by the subroutine.
 278: 			 */
 279:             c = getesc();
 280:             if (c == 0)
 281:                 continue;
 282: 
 283:             /*
 284: 			 * Markreg checks that argument is a letter
 285: 			 * and also maps ' and ` to the end of the range
 286: 			 * to allow '' or `` to reference the previous
 287: 			 * context mark.
 288: 			 */
 289:             c = markreg(c);
 290:             forbid (c == 0);
 291:             vsave();
 292:             names[c - 'a'] = (*dot &~ 01);
 293:             ncols[c - 'a'] = cursor;
 294:             anymarks = 1;
 295:             continue;
 296: 
 297: #ifdef ZCMD
 298:         /*
 299: 		 * ^F		Window forwards, with 2 lines of continuity.
 300: 		 *		Count gives new screen size.
 301: 		 */
 302:         case CTRL(f):
 303:             vsave();
 304:             if (vcnt > 2) {
 305:                 dot += (vcnt - vcline) - 2 + (cnt-1)*basWLINES;
 306:                 vcnt = vcline = 0;
 307:             }
 308:             vzop(0, 0, '+');
 309:             continue;
 310: 
 311:         /*
 312: 		 * ^B		Window backwards, with 2 lines of continuity.
 313: 		 *		Inverse of ^F.
 314: 		 */
 315:         case CTRL(b):
 316:             vsave();
 317:             if (one + vcline != dot && vcnt > 2) {
 318:                 dot -= vcline - 2 + (cnt-1)*basWLINES;
 319:                 vcnt = vcline = 0;
 320:             }
 321:             vzop(0, 0, '^');
 322:             continue;
 323: #endif
 324: 
 325:         /*
 326: 		 * z		Screen adjustment, taking a following character:
 327: 		 *			z<CR>		current line to top
 328: 		 *			z<NL>		like z<CR>
 329: 		 *			z-		current line to bottom
 330: 		 *		also z+, z^ like ^F and ^B.
 331: 		 *		A preceding count is line to use rather
 332: 		 *		than current line.  A count between z and
 333: 		 *		specifier character changes the screen size
 334: 		 *		for the redraw.
 335: 		 *
 336: 		 */
 337:         case 'z':
 338: #ifdef OPENCODE
 339:             if (state == VISUAL) {
 340: #endif
 341:                 i = vgetcnt();
 342:                 if (i > 0)
 343:                     vsetsiz(i);
 344:                 c = getesc();
 345:                 if (c == 0)
 346:                     continue;
 347: #ifdef OPENCODE
 348:             }
 349: #endif
 350:             vsave();
 351:             vzop(hadcnt, cnt, c);
 352:             continue;
 353: 
 354:         /*
 355: 		 * Y		Yank lines, abbreviation for y_ or yy.
 356: 		 *		Yanked lines can be put later if no
 357: 		 *		changes intervene, or can be put in named
 358: 		 *		buffers and put anytime in this session.
 359: 		 */
 360:         case 'Y':
 361:             ungetkey('_');
 362:             c = 'y';
 363:             break;
 364: 
 365:         /*
 366: 		 * J		Join lines, 2 by default.  Count is number
 367: 		 *		of lines to join (no join operator sorry.)
 368: 		 */
 369:         case 'J':
 370:             forbid (dot == dol);
 371:             if (cnt == 1)
 372:                 cnt = 2;
 373:             if (cnt > (i = dol - dot + 1))
 374:                 cnt = i;
 375:             vsave();
 376:             setLAST();
 377:             cursor = strend(linebuf);
 378:             vremote(cnt, join, 0);
 379:             notenam = "join";
 380:             vmoving = 0;
 381:             killU();
 382:             vreplace(vcline, cnt, 1);
 383:             if (!*cursor && cursor > linebuf)
 384:                 cursor--;
 385:             if (notecnt == 2)
 386:                 notecnt = 0;
 387:             vrepaint(cursor);
 388:             continue;
 389: 
 390:         /*
 391: 		 * S		Substitute text for whole lines, abbrev for c_.
 392: 		 *		Count is number of lines to change.
 393: 		 */
 394:         case 'S':
 395:             ungetkey('_');
 396:             c = 'c';
 397:             break;
 398: 
 399:         /*
 400: 		 * O		Create a new line above current and accept new
 401: 		 *		input text, to an escape, there.
 402: 		 *		A count specifies, for dumb terminals when
 403: 		 *		slowopen is not set, the number of physical
 404: 		 *		line space to open on the screen.
 405: 		 *
 406: 		 * o		Like O, but opens lines below.
 407: 		 */
 408:         case 'O':
 409:         case 'o':
 410:             voOpen(c, cnt);
 411:             continue;
 412: 
 413:         /*
 414: 		 * C		Change text to end of line, short for c$.
 415: 		 */
 416:         case 'C':
 417:             if (*cursor) {
 418:                 ungetkey('$'), c = 'c';
 419:                 break;
 420:             }
 421:             goto appnd;
 422: 
 423:         /*
 424: 		 * A		Append at end of line, short for $a.
 425: 		 */
 426:         case 'A':
 427:             operate('$', 1);
 428: appnd:
 429:             c = 'a';
 430:             /* fall into ... */
 431: 
 432:         /*
 433: 		 * a		Appends text after cursor.  Text can continue
 434: 		 *		through arbitrary number of lines.
 435: 		 */
 436:         case 'a':
 437:             if (*cursor) {
 438: #ifdef OPENCODE
 439:                 if (state == HARDOPEN)
 440:                     putchar(*cursor);
 441: #endif
 442:                 cursor++;
 443:             }
 444:             goto insrt;
 445: 
 446:         /*
 447: 		 * I		Insert at beginning of whitespace of line,
 448: 		 *		short for ^i.
 449: 		 */
 450:         case 'I':
 451:             operate('^', 1);
 452:             c = 'i';
 453:             /* fall into ... */
 454: 
 455:         /*
 456: 		 * R		Replace characters, one for one, by input
 457: 		 *		(logically), like repeated r commands.
 458: 		 *
 459: 		 * BUG:		This is like the typeover mode of many other
 460: 		 *		editors, and is only rarely useful.  Its
 461: 		 *		implementation is a hack in a low level
 462: 		 *		routine and it doesn't work very well, e.g.
 463: 		 *		you can't move around within a R, etc.
 464: 		 */
 465:         case 'R':
 466:             /* fall into... */
 467: 
 468:         /*
 469: 		 * i		Insert text to an escape in the buffer.
 470: 		 *		Text is arbitrary.  This command reminds of
 471: 		 *		the i command in bare teco.
 472: 		 */
 473:         case 'i':
 474: insrt:
 475:             /*
 476: 			 * Common code for all the insertion commands.
 477: 			 * Save for redo, position cursor, prepare for append
 478: 			 * at command and in visual undo.  Note that nothing
 479: 			 * is doomed, unless R when all is, and save the
 480: 			 * current line in a the undo temporary buffer.
 481: 			 */
 482:             setLAST();
 483:             vcursat(cursor);
 484:             prepapp();
 485:             vnoapp();
 486:             doomed = c == 'R' ? 10000 : 0;
 487:             vundkind = VCHNG;
 488:             CP(vutmp, linebuf);
 489: 
 490:             /*
 491: 			 * If this is a repeated command, then suppress
 492: 			 * fake insert mode on dumb terminals which looks
 493: 			 * ridiculous and wastes lots of time even at 9600B.
 494: 			 */
 495:             if (vglobp)
 496:                 hold = HOLDQIK;
 497:             vappend(c, cnt, 0);
 498:             continue;
 499: 
 500:         /*
 501: 		 * ^?		An attention, normally a ^?, just beeps.
 502: 		 *		If you are a vi command within ex, then
 503: 		 *		two ATTN's will drop you back to command mode.
 504: 		 */
 505:         case ATTN:
 506:             beep();
 507:             if (initev || peekkey() != ATTN)
 508:                 continue;
 509:             /* fall into... */
 510: 
 511:         /*
 512: 		 * ^\		A quit always gets command mode.
 513: 		 */
 514:         case QUIT:
 515:             /*
 516: 			 * Have to be careful if we were called
 517: 			 *	g/xxx/vi
 518: 			 * since a return will just start up again.
 519: 			 * So we simulate an interrupt.
 520: 			 */
 521:             if (inglobal)
 522:                 onintr();
 523:             /* fall into... */
 524: 
 525: #ifdef notdef
 526:         /*
 527: 		 * q		Quit back to command mode, unless called as
 528: 		 *		vi on command line in which case dont do it
 529: 		 */
 530:         case 'q':   /* quit */
 531:             if (initev) {
 532:                 vsave();
 533:                 CATCH
 534:                     error("Q gets ex command mode, :q leaves vi");
 535:                 ENDCATCH
 536:                 splitw = 0;
 537:                 getDOT();
 538:                 vrepaint(cursor);
 539:                 continue;
 540:             }
 541:             /* fall into... */
 542: #endif
 543: 
 544:         /*
 545: 		 * Q		Is like q, but always gets to command mode
 546: 		 *		even if command line invocation was as vi.
 547: 		 */
 548:         case 'Q':
 549:             vsave();
 550:             return;
 551: 
 552: #ifdef ZZCMD
 553:         /*
 554: 		 *		Z: like :x
 555: 		 */
 556:         case 'Z':
 557:             forbid(getkey() != 'Z');
 558:             oglobp = globp;
 559:             globp = "x";
 560:             vclrech(0);
 561:             goto gogo;
 562: #endif
 563: 
 564:         /*
 565: 		 * P		Put back text before cursor or before current
 566: 		 *		line.  If text was whole lines goes back
 567: 		 *		as whole lines.  If part of a single line
 568: 		 *		or parts of whole lines splits up current
 569: 		 *		line to form many new lines.
 570: 		 *		May specify a named buffer, or the delete
 571: 		 *		saving buffers 1-9.
 572: 		 *
 573: 		 * p		Like P but after rather than before.
 574: 		 */
 575:         case 'P':
 576:         case 'p':
 577:             vmoving = 0;
 578:             /*
 579: 			 * If previous delete was partial line, use an
 580: 			 * append or insert to put it back so as to
 581: 			 * use insert mode on intelligent terminals.
 582: 			 */
 583:             if (!vreg && DEL[0]) {
 584:                 forbid ((DEL[0] & (QUOTE|TRIM)) == OVERBUF);
 585:                 vglobp = DEL;
 586:                 ungetkey(c == 'p' ? 'a' : 'i');
 587:                 goto reread;
 588:             }
 589: 
 590:             /*
 591: 			 * If a register wasn't specified, then make
 592: 			 * sure there is something to put back.
 593: 			 */
 594:             forbid (!vreg && unddol == dol);
 595:             vsave();
 596:             setLAST();
 597:             i = 0;
 598:             if (vreg && partreg(vreg) || !vreg && pkill[0]) {
 599:                 /*
 600: 				 * Restoring multiple lines which were partial
 601: 				 * lines; will leave cursor in middle
 602: 				 * of line after shoving restored text in to
 603: 				 * split the current line.
 604: 				 */
 605:                 i++;
 606:                 if (c == 'p' && *cursor)
 607:                     cursor++;
 608:             } else {
 609:                 /*
 610: 				 * In whole line case, have to back up dot
 611: 				 * for P; also want to clear cursor so
 612: 				 * cursor will eventually be positioned
 613: 				 * at the beginning of the first put line.
 614: 				 */
 615:                 cursor = 0;
 616:                 if (c == 'P') {
 617:                     dot--, vcline--;
 618:                     c = 'p';
 619:                 }
 620:             }
 621:             killU();
 622: 
 623:             /*
 624: 			 * The call to putreg can potentially
 625: 			 * bomb since there may be nothing in a named buffer.
 626: 			 * We thus put a catch in here.  If we didn't and
 627: 			 * there was an error we would end up in command mode.
 628: 			 */
 629:             CATCH
 630:                 vremote(1, vreg ? putreg : put, vreg);
 631:             ONERR
 632:                 if (vreg == -1) {
 633:                     splitw = 0;
 634:                     if (op == 'P')
 635:                         dot++, vcline++;
 636:                     goto pfixup;
 637:                 }
 638:             ENDCATCH
 639:             splitw = 0;
 640:             if (!i) {
 641:                 /*
 642: 				 * Increment undap1, undap2 to make up
 643: 				 * for their incorrect initialization in the
 644: 				 * routine vremote before calling put/putreg.
 645: 				 */
 646:                 undap1++, undap2++;
 647:                 vcline++;
 648:             }
 649: 
 650:             /*
 651: 			 * After a put want current line first line,
 652: 			 * and dot was made the last line put in code run
 653: 			 * so far.  This is why we increment vcline above,
 654: 			 * and decrease (usually) dot here.
 655: 			 */
 656:             dot = undap1;
 657:             vreplace(vcline, i, undap2 - undap1);
 658: #ifdef OPENCODE
 659:             if (state != VISUAL) {
 660:                 /*
 661: 				 * Special case in open mode.
 662: 				 * Force action on the screen when a single
 663: 				 * line is put even if it is identical to
 664: 				 * the current line, e.g. on YP; otherwise
 665: 				 * you can't tell anything happened.
 666: 				 */
 667:                 vjumpto(dot, cursor, '.');
 668:                 continue;
 669:             }
 670: #endif
 671: pfixup:
 672:             vrepaint(cursor);
 673:             vfixcurs();
 674:             continue;
 675: 
 676:         /*
 677: 		 * ^^		Return to previous file.
 678: 		 *		Like a :e #, and thus can be used after a
 679: 		 *		"No Write" diagnostic.
 680: 		 */
 681:         case CTRL(^):
 682:             vsave();
 683:             ckaw();
 684:             oglobp = globp;
 685:             if (value(AUTOWRITE))
 686:                 globp = "e! #";
 687:             else
 688:                 globp = "e #";
 689:             goto gogo;
 690: 
 691: #ifdef TAGSCODE
 692:         /*
 693: 		 * ^]		Takes word after cursor as tag, and then does
 694: 		 *		tag command.  Read ``go right to''.
 695: 		 */
 696:         case CTRL(]):
 697:             grabtag();
 698:             oglobp = globp;
 699:             globp = "tag";
 700:             goto gogo;
 701: #endif
 702: 
 703:         /*
 704: 		 * ^G		Bring up a status line at the bottom of
 705: 		 *		the screen, like a :file command.
 706: 		 *
 707: 		 * BUG:		Was ^S but doesn't work in cbreak mode
 708: 		 */
 709:         case CTRL(g):
 710:             oglobp = globp;
 711:             globp = "file";
 712: gogo:
 713:             addr = dot;
 714:             vsave();
 715:             goto doinit;
 716: 
 717:         /*
 718: 		 * :		Read a command from the echo area and
 719: 		 *		execute it in command mode.
 720: 		 */
 721:         case ':':
 722:             vsave();
 723:             i = tchng;
 724:             addr = dot;
 725:             if (readecho(c)) {
 726:                 esave[0] = 0;
 727:                 goto fixup;
 728:             }
 729:             /*
 730: 			 * Use the visual undo buffer to store the global
 731: 			 * string for command mode, since it is idle right now.
 732: 			 */
 733:             oglobp = globp; strcpy(vutmp, genbuf+1); globp = vutmp;
 734: doinit:
 735:             esave[0] = 0;
 736:             fixech();
 737: 
 738:             /*
 739: 			 * Have to finagle around not to lose last
 740: 			 * character after this command (when run from ex
 741: 			 * command mode).  This is clumsy.
 742: 			 */
 743:             d = peekc; ungetchar(0);
 744:             CATCH
 745:                 /*
 746: 				 * Save old values of options so we can
 747: 				 * notice when they change; switch into
 748: 				 * cooked mode so we are interruptible.
 749: 				 */
 750:                 onumber = value(NUMBER);
 751:                 olist = value(LIST);
 752:                 OPline = Pline;
 753:                 OPutchar = Putchar;
 754: #ifndef CBREAK
 755:                 vcook();
 756: #endif
 757:                 commands(1, 1);
 758:                 if (dot == zero && dol > zero)
 759:                     dot = one;
 760: #ifndef CBREAK
 761:                 vraw();
 762: #endif
 763:             ONERR
 764: #ifndef CBREAK
 765:                 vraw();
 766: #endif
 767:                 copy(esave, vtube[WECHO], TUBECOLS);
 768:             ENDCATCH
 769:             fixol();
 770:             Pline = OPline;
 771:             Putchar = OPutchar;
 772:             ungetchar(d);
 773:             globp = oglobp;
 774: 
 775:             /*
 776: 			 * If we ended up with no lines in the buffer, make
 777: 			 * a line, and don't consider the buffer changed.
 778: 			 */
 779:             if (dot == zero) {
 780:                 fixzero();
 781:                 sync();
 782:             }
 783:             splitw = 0;
 784: 
 785:             /*
 786: 			 * Special case: did list/number options change?
 787: 			 */
 788:             if (onumber != value(NUMBER))
 789:                 setnumb(value(NUMBER));
 790:             if (olist != value(LIST))
 791:                 setlist(value(LIST));
 792: 
 793: fixup:
 794:             /*
 795: 			 * If a change occurred, other than
 796: 			 * a write which clears changes, then
 797: 			 * we should allow an undo even if .
 798: 			 * didn't move.
 799: 			 *
 800: 			 * BUG: You can make this wrong by
 801: 			 * tricking around with multiple commands
 802: 			 * on one line of : escape, and including
 803: 			 * a write command there, but its not
 804: 			 * worth worrying about.
 805: 			 */
 806:             if (tchng && tchng != i)
 807:                 vundkind = VMANY, cursor = 0;
 808: 
 809:             /*
 810: 			 * If we are about to do another :, hold off
 811: 			 * updating of screen.
 812: 			 */
 813:             if (vcnt < 0 && Peekkey == ':') {
 814:                 getDOT();
 815:                 continue;
 816:             }
 817: 
 818:             /*
 819: 			 * In the case where the file being edited is
 820: 			 * new; e.g. if the initial state hasn't been
 821: 			 * saved yet, then do so now.
 822: 			 */
 823:             if (unddol == truedol) {
 824:                 vundkind = VNONE;
 825:                 Vlines = lineDOL();
 826:                 if (!inglobal)
 827:                     savevis();
 828:                 addr = zero;
 829:                 vcnt = 0;
 830:                 if (esave[0] == 0)
 831:                     copy(esave, vtube[WECHO], TUBECOLS);
 832:             }
 833: 
 834:             /*
 835: 			 * If the current line moved reset the cursor position.
 836: 			 */
 837:             if (dot != addr) {
 838:                 vmoving = 0;
 839:                 cursor = 0;
 840:             }
 841: 
 842:             /*
 843: 			 * If current line is not on screen or if we are
 844: 			 * in open mode and . moved, then redraw.
 845: 			 */
 846:             i = vcline + (dot - addr);
 847:             if (i < 0 || i >= vcnt && i >= -vcnt
 848: #ifdef OPENCODE
 849:                                  || state != VISUAL && dot != addr
 850: #endif
 851:                                  ) {
 852: #ifdef OPENCODE
 853:                 if (state == CRTOPEN)
 854:                     vup1();
 855: #endif
 856:                 if (vcnt > 0)
 857:                     vcnt = 0;
 858:                 vjumpto(dot, (char *) 0, '.');
 859:             } else {
 860:                 /*
 861: 				 * Current line IS on screen.
 862: 				 * If we did a [Hit return...] then
 863: 				 * restore vcnt and clear screen if in visual
 864: 				 */
 865:                 vcline = i;
 866:                 if (vcnt < 0) {
 867:                     vcnt = -vcnt;
 868: #ifdef OPENCODE
 869:                     if (state == VISUAL)
 870: #endif
 871:                         vclear();
 872: #ifdef OPENCODE
 873:                     else if (state == CRTOPEN)
 874:                         vcnt = 0;
 875: #endif
 876:                 }
 877: 
 878:                 /*
 879: 				 * Limit max value of vcnt based on $
 880: 				 */
 881:                 i = vcline + lineDOL() - lineDOT() + 1;
 882:                 if (i < vcnt)
 883:                     vcnt = i;
 884: 
 885:                 /*
 886: 				 * Dirty and repaint.
 887: 				 */
 888:                 vdirty(0, LINES);
 889:                 vrepaint(cursor);
 890:             }
 891: 
 892:             /*
 893: 			 * If in visual, put back the echo area
 894: 			 * if it was clobberred.
 895: 			 */
 896: #ifdef OPENCODE
 897:             if (state == VISUAL)
 898: #endif
 899:                         {
 900:                 int sdc = destcol, sdl = destline;
 901: 
 902:                 splitw++;
 903:                 vigoto(WECHO, 0);
 904:                 for (i = 0; i < TUBECOLS - 1; i++) {
 905:                     if (esave[i] == 0)
 906:                         break;
 907:                     vputchar(esave[i]);
 908:                 }
 909:                 splitw = 0;
 910:                 vgoto(sdl, sdc);
 911:             }
 912:             continue;
 913: 
 914:         /*
 915: 		 * u		undo the last changing command.
 916: 		 */
 917:         case 'u':
 918:             vundo();
 919:             continue;
 920: 
 921:         /*
 922: 		 * U		restore current line to initial state.
 923: 		 */
 924:         case 'U':
 925:             vUndo();
 926:             continue;
 927: 
 928: fonfon:
 929:             beep();
 930:             continue;
 931:         }
 932: 
 933:         /*
 934: 		 * Rest of commands are decoded by the operate
 935: 		 * routine.
 936: 		 */
 937:         operate(c, cnt);
 938:     }
 939: }
 940: 
 941: /*
 942:  * Grab the word after the cursor so we can look for it as a tag.
 943:  */
 944: grabtag()
 945: {
 946:     register char *cp, *dp;
 947: 
 948:     cp = vpastwh(cursor);
 949:     if (*cp) {
 950:         dp = lasttag;
 951:         do {
 952:             if (dp < &lasttag[sizeof lasttag - 2])
 953:                 *dp++ = *cp;
 954:             cp++;
 955:         } while (isalpha(*cp) || isdigit(*cp) || *cp == '_');
 956:         *dp++ = 0;
 957:     }
 958: }
 959: 
 960: /*
 961:  * Before appending lines, set up addr1 and
 962:  * the command mode undo information.
 963:  */
 964: prepapp()
 965: {
 966: 
 967:     addr1 = dot;
 968:     deletenone();
 969:     addr1++;
 970:     appendnone();
 971: }
 972: 
 973: /*
 974:  * Execute function f with the address bounds addr1
 975:  * and addr2 surrounding cnt lines starting at dot.
 976:  */
 977: vremote(cnt, f, arg)
 978:     int cnt, (*f)(), arg;
 979: {
 980:     register int oing = inglobal;
 981: 
 982:     addr1 = dot;
 983:     addr2 = dot + cnt - 1;
 984:     undap1 = undap2 = dot;
 985:     inglobal = 0;
 986:     (*f)(arg);
 987:     inglobal = oing;
 988:     vundkind = VMANY;
 989:     vmcurs = 0;
 990: }
 991: 
 992: /*
 993:  * Save the current contents of linebuf, if it has changed.
 994:  */
 995: vsave()
 996: {
 997:     char temp[LBSIZE];
 998: 
 999:     CP(temp, linebuf);
1000:     if (vundkind == VCHNG || vundkind == VCAPU) {
1001:         /*
1002: 		 * If the undo state is saved in the temporary buffer
1003: 		 * vutmp, then we sync this into the temp file so that
1004: 		 * we will be able to undo even after we have moved off
1005: 		 * the line.  It would be possible to associate a line
1006: 		 * with vutmp but we assume that vutmp is only associated
1007: 		 * with line dot (e.g. in case ':') above, so beware.
1008: 		 */
1009:         prepapp();
1010:         strcLIN(vutmp);
1011:         putmark(dot);
1012:         vremote(1, yank, 0);
1013:         vundkind = VMCHNG;
1014:         notecnt = 0;
1015:         undkind = UNDCHANGE;
1016:     }
1017:     /*
1018: 	 * Get the line out of the temp file and do nothing if it hasn't
1019: 	 * changed.  This may seem like a loss, but the line will
1020: 	 * almost always be in a read buffer so this may well avoid disk i/o.
1021: 	 */
1022:     getDOT();
1023:     if (strcmp(linebuf, temp) == 0)
1024:         return;
1025:     strcLIN(temp);
1026:     putmark(dot);
1027: }
1028: 
1029: #undef  forbid
1030: #define forbid(a)   if (a) { beep(); return; }
1031: 
1032: /*
1033:  * Do a z operation.
1034:  * Code here is rather long, and very uninteresting.
1035:  */
1036: vzop(hadcnt, cnt, c)
1037:     bool hadcnt;
1038:     int cnt;
1039:     register int c;
1040: {
1041:     register line *addr;
1042: 
1043: #ifdef OPENCODE
1044:     if (state != VISUAL) {
1045:         /*
1046: 		 * Z from open; always like a z=.
1047: 		 * This code is a mess and should be cleaned up.
1048: 		 */
1049:         vmoveitup(1);
1050:         vgoto(outline, 0);
1051:         ostop(normf);
1052:         setoutt();
1053:         addr2 = dot;
1054:         vclear();
1055:         destline = WECHO;
1056:         zop2(Xhadcnt ? Xcnt : value(WINDOW) - 1, '=');
1057:         if (state == CRTOPEN)
1058:             putnl();
1059:         putNFL();
1060:         termreset();
1061:         Outchar = vputchar;
1062:         ignore(ostart());
1063:         vcnt = 0;
1064:         outline = destline = 0;
1065:         vjumpto(dot, cursor, 0);
1066:         return;
1067:     }
1068: #endif
1069:     if (hadcnt) {
1070:         addr = zero + cnt;
1071:         if (addr < one)
1072:             addr = one;
1073:         if (addr > dol)
1074:             addr = dol;
1075:         markit(addr);
1076:     } else
1077: #ifdef ZCMD
1078:         switch (c) {
1079: 
1080:         case '+':
1081:             addr = dot + vcnt - vcline;
1082:             break;
1083: 
1084:         case '^':
1085:             addr = dot - vcline - 1;
1086:             forbid (addr < one);
1087:             c = '-';
1088:             break;
1089: 
1090:         default:
1091: #endif
1092:             addr = dot;
1093: #ifdef ZCMD
1094:             break;
1095:         }
1096: #endif
1097:     switch (c) {
1098: 
1099: #ifdef ZCMD
1100:     case '.':
1101:     case '-':
1102:         break;
1103: 
1104:     case '^':
1105:         forbid (addr <= one);
1106:         break;
1107: 
1108:     case '+':
1109:         forbid (addr >= dol);
1110:         /* fall into ... */
1111: #endif
1112: 
1113:     case CR:
1114:     case NL:
1115:         c = CR;
1116:         break;
1117: 
1118:     default:
1119:         beep();
1120:         return;
1121:     }
1122:     vmoving = 0;
1123:     vjumpto(addr, NOSTR, c);
1124: }

Defined functions

grabtag defined in line 944; used 1 times
prepapp defined in line 964; used 5 times
vmain defined in line 16; used 2 times
vremote defined in line 977; used 11 times
vzop defined in line 1036; used 3 times

Defined macros

forbid defined in line 1030; used 12 times
Last modified: 1980-09-13
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1842
Valid CSS Valid XHTML 1.0 Strict