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

Defined functions

grabtag defined in line 1114; used 1 times
prepapp defined in line 1134; used 5 times
vmain defined in line 17; used 2 times
vremote defined in line 1147; used 11 times
vzop defined in line 1208; used 3 times

Defined variables

sccsid defined in line 2; never used

Defined macros

forbid defined in line 1202; used 22 times
Last modified: 1982-01-25
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2441
Valid CSS Valid XHTML 1.0 Strict