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

Defined functions

grabtag defined in line 1122; used 1 times
prepapp defined in line 1146; used 5 times
vmain defined in line 25; used 2 times
vremote defined in line 1159; used 11 times
vzop defined in line 1220; used 3 times

Defined variables

sccsid defined in line 8; never used

Defined macros

forbid defined in line 1214; used 22 times
Last modified: 1991-09-08
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4965
Valid CSS Valid XHTML 1.0 Strict