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_vput.c	7.4.1 (2.11BSD GTE) 12/9/94";
   9: #endif
  10: 
  11: #include "ex.h"
  12: #include "ex_tty.h"
  13: #include "ex_vis.h"
  14: 
  15: /*
  16:  * Deal with the screen, clearing, cursor positioning, putting characters
  17:  * into the screen image, and deleting characters.
  18:  * Really hard stuff here is utilizing insert character operations
  19:  * on intelligent terminals which differs widely from terminal to terminal.
  20:  */
  21: vclear()
  22: {
  23: 
  24: #ifdef ADEBUG
  25:     if (trace)
  26:         tfixnl(), fprintf(trace, "------\nvclear\n");
  27: #endif
  28:     tputs(CL, LINES, putch);
  29:     destcol = 0;
  30:     outcol = 0;
  31:     destline = 0;
  32:     outline = 0;
  33:     if (inopen)
  34:         vclrbyte(vtube0, WCOLS * (WECHO - ZERO + 1));
  35: }
  36: 
  37: /*
  38:  * Clear memory.
  39:  */
  40: vclrbyte(cp, i)
  41:     register char *cp;
  42:     register int i;
  43: {
  44: 
  45:     if (i > 0)
  46:         do
  47:             *cp++ = 0;
  48:         while (--i != 0);
  49: }
  50: 
  51: /*
  52:  * Clear a physical display line, high level.
  53:  */
  54: vclrlin(l, tp)
  55:     int l;
  56:     line *tp;
  57: {
  58: 
  59:     vigoto(l, 0);
  60:     if ((hold & HOLDAT) == 0)
  61:         putchar(tp > dol ? '~' : '@');
  62:     if (state == HARDOPEN)
  63:         sethard();
  64:     vclreol();
  65: }
  66: 
  67: /*
  68:  * Clear to the end of the current physical line
  69:  */
  70: vclreol()
  71: {
  72:     register int i, j;
  73:     register char *tp;
  74: 
  75:     if (destcol == WCOLS)
  76:         return;
  77:     destline += destcol / WCOLS;
  78:     destcol %= WCOLS;
  79:     if (destline < 0 || destline > WECHO)
  80:         error("Internal error: vclreol");
  81:     i = WCOLS - destcol;
  82:     tp = vtube[destline] + destcol;
  83:     if (CE) {
  84:         if (IN && *tp || !ateopr()) {
  85:             vcsync();
  86:             vputp(CE, 1);
  87:         }
  88:         vclrbyte(tp, i);
  89:         return;
  90:     }
  91:     if (*tp == 0)
  92:         return;
  93:     while (i > 0 && (j = *tp & (QUOTE|TRIM))) {
  94:         if (j != ' ' && (j & QUOTE) == 0) {
  95:             destcol = WCOLS - i;
  96:             vputchar(' ');
  97:         }
  98:         --i, *tp++ = 0;
  99:     }
 100: }
 101: 
 102: /*
 103:  * Clear the echo line.
 104:  * If didphys then its been cleared physically (as
 105:  * a side effect of a clear to end of display, e.g.)
 106:  * so just do it logically.
 107:  * If work here is being held off, just remember, in
 108:  * heldech, if work needs to be done, don't do anything.
 109:  */
 110: vclrech(didphys)
 111:     bool didphys;
 112: {
 113: 
 114:     if (Peekkey == ATTN)
 115:         return;
 116:     if (hold & HOLDECH) {
 117:         heldech = !didphys;
 118:         return;
 119:     }
 120:     if (!didphys && (CD || CE)) {
 121:         splitw++;
 122:         /*
 123: 		 * If display is retained below, then MUST use CD or CE
 124: 		 * since we don't really know whats out there.
 125: 		 * Vigoto might decide (incorrectly) to do nothing.
 126: 		 */
 127:         if (DB) {
 128:             vgoto(WECHO, 0);
 129:             vputp(CD ? CD : CE, 1);
 130:         } else {
 131:             if (XT) {
 132:                 /*
 133: 				 * This code basically handles the t1061
 134: 				 * where positioning at (0, 0) won't work
 135: 				 * because the terminal won't let you put
 136: 				 * the cursor on it's magic cookie.
 137: 				 *
 138: 				 * Should probably be XS above, or even a
 139: 				 * new X? glitch, but right now t1061 is the
 140: 				 * only terminal with XT.
 141: 				 */
 142:                 vgoto(WECHO, 0);
 143:                 vputp(DL, 1);
 144:             } else {
 145:                 vigoto(WECHO, 0);
 146:                 vclreol();
 147:             }
 148:         }
 149:         splitw = 0;
 150:         didphys = 1;
 151:     }
 152:     if (didphys)
 153:         vclrbyte(vtube[WECHO], WCOLS);
 154:     heldech = 0;
 155: }
 156: 
 157: /*
 158:  * Fix the echo area for use, setting
 159:  * the state variable splitw so we wont rollup
 160:  * when we move the cursor there.
 161:  */
 162: fixech()
 163: {
 164: 
 165:     splitw++;
 166:     if (state != VISUAL && state != CRTOPEN) {
 167:         vclean();
 168:         vcnt = 0;
 169:     }
 170:     vgoto(WECHO, 0); flusho();
 171: }
 172: 
 173: /*
 174:  * Put the cursor ``before'' cp.
 175:  */
 176: vcursbef(cp)
 177:     register char *cp;
 178: {
 179: 
 180:     if (cp <= linebuf)
 181:         vgotoCL(value(NUMBER) << 3);
 182:     else
 183:         vgotoCL(column(cp - 1) - 1);
 184: }
 185: 
 186: /*
 187:  * Put the cursor ``at'' cp.
 188:  */
 189: vcursat(cp)
 190:     register char *cp;
 191: {
 192: 
 193:     if (cp <= linebuf && linebuf[0] == 0)
 194:         vgotoCL(value(NUMBER) << 3);
 195:     else
 196:         vgotoCL(column(cp - 1));
 197: }
 198: 
 199: /*
 200:  * Put the cursor ``after'' cp.
 201:  */
 202: vcursaft(cp)
 203:     register char *cp;
 204: {
 205: 
 206:     vgotoCL(column(cp));
 207: }
 208: 
 209: /*
 210:  * Fix the cursor to be positioned in the correct place
 211:  * to accept a command.
 212:  */
 213: vfixcurs()
 214: {
 215: 
 216:     vsetcurs(cursor);
 217: }
 218: 
 219: /*
 220:  * Compute the column position implied by the cursor at ``nc'',
 221:  * and move the cursor there.
 222:  */
 223: vsetcurs(nc)
 224:     register char *nc;
 225: {
 226:     register int col;
 227: 
 228:     col = column(nc);
 229:     if (linebuf[0])
 230:         col--;
 231:     vgotoCL(col);
 232:     cursor = nc;
 233: }
 234: 
 235: /*
 236:  * Move the cursor invisibly, i.e. only remember to do it.
 237:  */
 238: vigoto(y, x)
 239:     int y, x;
 240: {
 241: 
 242:     destline = y;
 243:     destcol = x;
 244: }
 245: 
 246: /*
 247:  * Move the cursor to the position implied by any previous
 248:  * vigoto (or low level hacking with destcol/destline as in readecho).
 249:  */
 250: vcsync()
 251: {
 252: 
 253:     vgoto(destline, destcol);
 254: }
 255: 
 256: /*
 257:  * Goto column x of the current line.
 258:  */
 259: vgotoCL(x)
 260:     register int x;
 261: {
 262: 
 263:     if (splitw)
 264:         vgoto(WECHO, x);
 265:     else
 266:         vgoto(LINE(vcline), x);
 267: }
 268: 
 269: /*
 270:  * Invisible goto column x of current line.
 271:  */
 272: vigotoCL(x)
 273:     register int x;
 274: {
 275: 
 276:     if (splitw)
 277:         vigoto(WECHO, x);
 278:     else
 279:         vigoto(LINE(vcline), x);
 280: }
 281: 
 282: /*
 283:  * Move cursor to line y, column x, handling wraparound and scrolling.
 284:  */
 285: vgoto(y, x)
 286:     register int y, x;
 287: {
 288:     register char *tp;
 289:     register int c;
 290: 
 291:     /*
 292: 	 * Fold the possibly too large value of x.
 293: 	 */
 294:     if (x >= WCOLS) {
 295:         y += x / WCOLS;
 296:         x %= WCOLS;
 297:     }
 298:     if (y < 0)
 299:         error("Internal error: vgoto");
 300:     if (outcol >= WCOLS) {
 301:         if (AM) {
 302:             outline += outcol / WCOLS;
 303:             outcol %= WCOLS;
 304:         } else
 305:             outcol = WCOLS - 1;
 306:     }
 307: 
 308:     /*
 309: 	 * In a hardcopy or glass crt open, print the stuff
 310: 	 * implied by a motion, or backspace.
 311: 	 */
 312:     if (state == HARDOPEN || state == ONEOPEN) {
 313:         if (y != outline)
 314:             error("Line too long for open");
 315:         if (x + 1 < outcol - x || (outcol > x && !BS))
 316:             destcol = 0, fgoto();
 317:         tp = vtube[WBOT] + outcol;
 318:         while (outcol != x)
 319:             if (outcol < x) {
 320:                 if (*tp == 0)
 321:                     *tp = ' ';
 322:                 c = *tp++ & TRIM;
 323:                 vputc(c && (!OS || EO) ? c : ' '), outcol++;
 324:             } else {
 325:                 if (BC)
 326:                     vputp(BC, 0);
 327:                 else
 328:                     vputc('\b');
 329:                 outcol--;
 330:             }
 331:         destcol = outcol = x;
 332:         destline = outline;
 333:         return;
 334:     }
 335: 
 336:     /*
 337: 	 * If the destination position implies a scroll, do it.
 338: 	 */
 339:     destline = y;
 340:     if (destline > WBOT && (!splitw || destline > WECHO)) {
 341:         endim();
 342:         vrollup(destline);
 343:     }
 344: 
 345:     /*
 346: 	 * If there really is a motion involved, do it.
 347: 	 * The check here is an optimization based on profiling.
 348: 	 */
 349:     destcol = x;
 350:     if ((destline - outline) * WCOLS != destcol - outcol) {
 351:         if (!MI)
 352:             endim();
 353:         fgoto();
 354:     }
 355: }
 356: 
 357: /*
 358:  * This is the hardest code in the editor, and deals with insert modes
 359:  * on different kinds of intelligent terminals.  The complexity is due
 360:  * to the cross product of three factors:
 361:  *
 362:  *	1. Lines may display as more than one segment on the screen.
 363:  *	2. There are 2 kinds of intelligent terminal insert modes.
 364:  *	3. Tabs squash when you insert characters in front of them,
 365:  *	   in a way in which current intelligent terminals don't handle.
 366:  *
 367:  * The two kinds of terminals are typified by the DM2500 or HP2645 for
 368:  * one and the CONCEPT-100 or the FOX for the other.
 369:  *
 370:  * The first (HP2645) kind has an insert mode where the characters
 371:  * fall off the end of the line and the screen is shifted rigidly
 372:  * no matter how the display came about.
 373:  *
 374:  * The second (CONCEPT-100) kind comes from terminals which are designed
 375:  * for forms editing and which distinguish between blanks and ``spaces''
 376:  * on the screen, spaces being like blank, but never having had
 377:  * and data typed into that screen position (since, e.g. a clear operation
 378:  * like clear screen).  On these terminals, when you insert a character,
 379:  * the characters from where you are to the end of the screen shift
 380:  * over till a ``space'' is found, and the null character there gets
 381:  * eaten up.
 382:  *
 383:  *
 384:  * The code here considers the line as consisting of several parts
 385:  * the first part is the ``doomed'' part, i.e. a part of the line
 386:  * which is being typed over.  Next comes some text up to the first
 387:  * following tab.  The tab is the next segment of the line, and finally
 388:  * text after the tab.
 389:  *
 390:  * We have to consider each of these segments and the effect of the
 391:  * insertion of a character on them.  On terminals like HP2645's we
 392:  * must simulate a multi-line insert mode using the primitive one
 393:  * line insert mode.  If we are inserting in front of a tab, we have
 394:  * to either delete characters from the tab or insert white space
 395:  * (when the tab reaches a new spot where it gets larger) before we
 396:  * insert the new character.
 397:  *
 398:  * On a terminal like a CONCEPT our strategy is to make all
 399:  * blanks be displayed, while trying to keep the screen having ``spaces''
 400:  * for portions of tabs.  In this way the terminal hardward does some
 401:  * of the hacking for compression of tabs, although this tends to
 402:  * disappear as you work on the line and spaces change into blanks.
 403:  *
 404:  * There are a number of boundary conditions (like typing just before
 405:  * the first following tab) where we can avoid a lot of work.  Most
 406:  * of them have to be dealt with explicitly because performance is
 407:  * much, much worse if we don't.
 408:  *
 409:  * A final thing which is hacked here is two flavors of insert mode.
 410:  * Datamedia's do this by an insert mode which you enter and leave
 411:  * and by having normal motion character operate differently in this
 412:  * mode, notably by having a newline insert a line on the screen in
 413:  * this mode.  This generally means it is unsafe to move around
 414:  * the screen ignoring the fact that we are in this mode.
 415:  * This is possible on some terminals, and wins big (e.g. HP), so
 416:  * we encode this as a ``can move in insert capability'' mi,
 417:  * and terminals which have it can do insert mode with much less
 418:  * work when tabs are present following the cursor on the current line.
 419:  */
 420: 
 421: /*
 422:  * Routine to expand a tab, calling the normal Outchar routine
 423:  * to put out each implied character.  Note that we call outchar
 424:  * with a QUOTE.  We use QUOTE internally to represent a position
 425:  * which is part of the expansion of a tab.
 426:  */
 427: vgotab()
 428: {
 429:     register int i = tabcol(destcol, value(TABSTOP)) - destcol;
 430: 
 431:     do
 432:         (*Outchar)(QUOTE);
 433:     while (--i);
 434: }
 435: 
 436: /*
 437:  * Variables for insert mode.
 438:  */
 439: int linend;         /* The column position of end of line */
 440: int tabstart;       /* Column of start of first following tab */
 441: int tabend;         /* Column of end of following tabs */
 442: int tabsize;        /* Size of the following tabs */
 443: int tabslack;       /* Number of ``spaces'' in following tabs */
 444: int inssiz;         /* Number of characters to be inserted */
 445: int inscol;         /* Column where insertion is taking place */
 446: int shft;           /* Amount tab expansion shifted rest of line */
 447: int slakused;       /* This much of tabslack will be used up */
 448: 
 449: /*
 450:  * This routine MUST be called before insert mode is run,
 451:  * and brings all segments of the current line to the top
 452:  * of the screen image buffer so it is easier for us to
 453:  * maniuplate them.
 454:  */
 455: vprepins()
 456: {
 457:     register int i;
 458:     register char *cp = vtube0;
 459: 
 460:     for (i = 0; i < DEPTH(vcline); i++) {
 461:         vmaktop(LINE(vcline) + i, cp);
 462:         cp += WCOLS;
 463:     }
 464: }
 465: 
 466: vmaktop(p, cp)
 467:     register int p;
 468:     char *cp;
 469: {
 470:     register int i;
 471:     char temp[TUBECOLS];
 472: 
 473:     if (p < 0 || vtube[p] == cp)
 474:         return;
 475:     for (i = ZERO; i <= WECHO; i++)
 476:         if (vtube[i] == cp) {
 477:             copy(temp, vtube[i], WCOLS);
 478:             copy(vtube[i], vtube[p], WCOLS);
 479:             copy(vtube[p], temp, WCOLS);
 480:             vtube[i] = vtube[p];
 481:             vtube[p] = cp;
 482:             return;
 483:         }
 484:     error("Line too long");
 485: }
 486: 
 487: /*
 488:  * Insert character c at current cursor position.
 489:  * Multi-character inserts occur only as a result
 490:  * of expansion of tabs (i.e. inssize == 1 except
 491:  * for tabs) and code assumes this in several place
 492:  * to make life simpler.
 493:  */
 494: vinschar(c)
 495:     int c;      /* mjm: char --> int */
 496: {
 497:     register int i;
 498:     register char *tp;
 499: 
 500:     if ((!IM || !EI) && ((hold & HOLDQIK) || !value(REDRAW) || value(SLOWOPEN))) {
 501:         /*
 502: 		 * Don't want to try to use terminal
 503: 		 * insert mode, or to try to fake it.
 504: 		 * Just put the character out; the screen
 505: 		 * will probably be wrong but we will fix it later.
 506: 		 */
 507:         if (c == '\t') {
 508:             vgotab();
 509:             return;
 510:         }
 511:         vputchar(c);
 512:         if (DEPTH(vcline) * WCOLS + !value(REDRAW) >
 513:             (destline - LINE(vcline)) * WCOLS + destcol)
 514:             return;
 515:         /*
 516: 		 * The next line is about to be clobbered
 517: 		 * make space for another segment of this line
 518: 		 * (on an intelligent terminal) or just remember
 519: 		 * that next line was clobbered (on a dumb one
 520: 		 * if we don't care to redraw the tail.
 521: 		 */
 522:         if (AL) {
 523:             vnpins(0);
 524:         } else {
 525:             c = LINE(vcline) + DEPTH(vcline);
 526:             if (c < LINE(vcline + 1) || c > WBOT)
 527:                 return;
 528:             i = destcol;
 529:             vinslin(c, 1, vcline);
 530:             DEPTH(vcline)++;
 531:             vigoto(c, i);
 532:             vprepins();
 533:         }
 534:         return;
 535:     }
 536:     /*
 537: 	 * Compute the number of positions in the line image of the
 538: 	 * current line.  This is done from the physical image
 539: 	 * since that is faster.  Note that we have no memory
 540: 	 * from insertion to insertion so that routines which use
 541: 	 * us don't have to worry about moving the cursor around.
 542: 	 */
 543:     if (*vtube0 == 0)
 544:         linend = 0;
 545:     else {
 546:         /*
 547: 		 * Search backwards for a non-null character
 548: 		 * from the end of the displayed line.
 549: 		 */
 550:         i = WCOLS * DEPTH(vcline);
 551:         if (i == 0)
 552:             i = WCOLS;
 553:         tp = vtube0 + i;
 554:         while (*--tp == 0)
 555:             if (--i == 0)
 556:                 break;
 557:         linend = i;
 558:     }
 559: 
 560:     /*
 561: 	 * We insert at a position based on the physical location
 562: 	 * of the output cursor.
 563: 	 */
 564:     inscol = destcol + (destline - LINE(vcline)) * WCOLS;
 565:     if (c == '\t') {
 566:         /*
 567: 		 * Characters inserted from a tab must be
 568: 		 * remembered as being part of a tab, but we can't
 569: 		 * use QUOTE here since we really need to print blanks.
 570: 		 * QUOTE|' ' is the representation of this.
 571: 		 */
 572:         inssiz = tabcol(inscol, value(TABSTOP)) - inscol;
 573:         c = ' ' | QUOTE;
 574:     } else
 575:         inssiz = 1;
 576: 
 577:     /*
 578: 	 * If the text to be inserted is less than the number
 579: 	 * of doomed positions, then we don't need insert mode,
 580: 	 * rather we can just typeover.
 581: 	 */
 582:     if (inssiz <= doomed) {
 583:         endim();
 584:         if (inscol != linend)
 585:             doomed -= inssiz;
 586:         do
 587:             vputchar(c);
 588:         while (--inssiz);
 589:         return;
 590:     }
 591: 
 592:     /*
 593: 	 * Have to really do some insertion, thus
 594: 	 * stake out the bounds of the first following
 595: 	 * group of tabs, computing starting position,
 596: 	 * ending position, and the number of ``spaces'' therein
 597: 	 * so we can tell how much it will squish.
 598: 	 */
 599:     tp = vtube0 + inscol;
 600:     for (i = inscol; i < linend; i++)
 601:         if (*tp++ & QUOTE) {
 602:             --tp;
 603:             break;
 604:         }
 605:     tabstart = tabend = i;
 606:     tabslack = 0;
 607:     while (tabend < linend) {
 608:         i = *tp++;
 609:         if ((i & QUOTE) == 0)
 610:             break;
 611:         if ((i & TRIM) == 0)
 612:             tabslack++;
 613:         tabsize++;
 614:         tabend++;
 615:     }
 616:     tabsize = tabend - tabstart;
 617: 
 618:     /*
 619: 	 * For HP's and DM's, e.g. tabslack has no meaning.
 620: 	 */
 621:     if (!IN)
 622:         tabslack = 0;
 623: #ifdef IDEBUG
 624:     if (trace) {
 625:         fprintf(trace, "inscol %d, inssiz %d, tabstart %d, ",
 626:             inscol, inssiz, tabstart);
 627:         fprintf(trace, "tabend %d, tabslack %d, linend %d\n",
 628:             tabend, tabslack, linend);
 629:     }
 630: #endif
 631: 
 632:     /*
 633: 	 * The real work begins.
 634: 	 */
 635:     slakused = 0;
 636:     shft = 0;
 637:     if (tabsize) {
 638:         /*
 639: 		 * There are tabs on this line.
 640: 		 * If they need to expand, then the rest of the line
 641: 		 * will have to be shifted over.  In this case,
 642: 		 * we will need to make sure there are no ``spaces''
 643: 		 * in the rest of the line (on e.g. CONCEPT-100)
 644: 		 * and then grab another segment on the screen if this
 645: 		 * line is now deeper.  We then do the shift
 646: 		 * implied by the insertion.
 647: 		 */
 648:         if (inssiz >= doomed + tabcol(tabstart, value(TABSTOP)) - tabstart) {
 649:             if (IN)
 650:                 vrigid();
 651:             vneedpos(value(TABSTOP));
 652:             vishft();
 653:         }
 654:     } else if (inssiz > doomed)
 655:         /*
 656: 		 * No tabs, but line may still get deeper.
 657: 		 */
 658:         vneedpos(inssiz - doomed);
 659:     /*
 660: 	 * Now put in the inserted characters.
 661: 	 */
 662:     viin(c);
 663: 
 664:     /*
 665: 	 * Now put the cursor in its final resting place.
 666: 	 */
 667:     destline = LINE(vcline);
 668:     destcol = inscol + inssiz;
 669:     vcsync();
 670: }
 671: 
 672: /*
 673:  * Rigidify the rest of the line after the first
 674:  * group of following tabs, typing blanks over ``spaces''.
 675:  */
 676: vrigid()
 677: {
 678:     register int col;
 679:     register char *tp = vtube0 + tabend;
 680: 
 681:     for (col = tabend; col < linend; col++)
 682:         if ((*tp++ & TRIM) == 0) {
 683:             endim();
 684:             vgotoCL(col);
 685:             vputchar(' ' | QUOTE);
 686:         }
 687: }
 688: 
 689: /*
 690:  * We need cnt more positions on this line.
 691:  * Open up new space on the screen (this may in fact be a
 692:  * screen rollup).
 693:  *
 694:  * On a dumb terminal we may infact redisplay the rest of the
 695:  * screen here brute force to keep it pretty.
 696:  */
 697: vneedpos(cnt)
 698:     int cnt;
 699: {
 700:     register int d = DEPTH(vcline);
 701:     register int rmdr = d * WCOLS - linend;
 702: 
 703:     if (cnt <= rmdr - IN)
 704:         return;
 705:     endim();
 706:     vnpins(1);
 707: }
 708: 
 709: vnpins(dosync)
 710:     int dosync;
 711: {
 712:     register int d = DEPTH(vcline);
 713:     register int e;
 714: 
 715:     e = LINE(vcline) + DEPTH(vcline);
 716:     if (e < LINE(vcline + 1)) {
 717:         vigoto(e, 0);
 718:         vclreol();
 719:         return;
 720:     }
 721:     DEPTH(vcline)++;
 722:     if (e < WECHO) {
 723:         e = vglitchup(vcline, d);
 724:         vigoto(e, 0); vclreol();
 725:         if (dosync) {
 726:             int (*Ooutchar)() = Outchar;
 727:             Outchar = vputchar;
 728:             vsync(e + 1);
 729:             Outchar = Ooutchar;
 730:         }
 731:     } else {
 732:         vup1();
 733:         vigoto(WBOT, 0);
 734:         vclreol();
 735:     }
 736:     vprepins();
 737: }
 738: 
 739: /*
 740:  * Do the shift of the next tabstop implied by
 741:  * insertion so it expands.
 742:  */
 743: vishft()
 744: {
 745:     int tshft = 0;
 746:     int j;
 747:     register int i;
 748:     register char *tp = vtube0;
 749:     register char *up;
 750:     short oldhold = hold;
 751: 
 752:     shft = value(TABSTOP);
 753:     hold |= HOLDPUPD;
 754:     if (!IM && !EI) {
 755:         /*
 756: 		 * Dumb terminals are easy, we just have
 757: 		 * to retype the text.
 758: 		 */
 759:         vigotoCL(tabend + shft);
 760:         up = tp + tabend;
 761:         for (i = tabend; i < linend; i++)
 762:             vputchar(*up++);
 763:     } else if (IN) {
 764:         /*
 765: 		 * CONCEPT-like terminals do most of the work for us,
 766: 		 * we don't have to muck with simulation of multi-line
 767: 		 * insert mode.  Some of the shifting may come for free
 768: 		 * also if the tabs don't have enough slack to take up
 769: 		 * all the inserted characters.
 770: 		 */
 771:         i = shft;
 772:         slakused = inssiz - doomed;
 773:         if (slakused > tabslack) {
 774:             i -= slakused - tabslack;
 775:             slakused -= tabslack;
 776:         }
 777:         if (i > 0 && tabend != linend) {
 778:             tshft = i;
 779:             vgotoCL(tabend);
 780:             goim();
 781:             do
 782:                 vputchar(' ' | QUOTE);
 783:             while (--i);
 784:         }
 785:     } else {
 786:         /*
 787: 		 * HP and Datamedia type terminals have to have multi-line
 788: 		 * insert faked.  Hack each segment after where we are
 789: 		 * (going backwards to where we are.)  We then can
 790: 		 * hack the segment where the end of the first following
 791: 		 * tab group is.
 792: 		 */
 793:         for (j = DEPTH(vcline) - 1; j > (tabend + shft) / WCOLS; j--) {
 794:             vgotoCL(j * WCOLS);
 795:             goim();
 796:             up = tp + j * WCOLS - shft;
 797:             i = shft;
 798:             do {
 799:                 if (*up)
 800:                     vputchar(*up++);
 801:                 else
 802:                     break;
 803:             } while (--i);
 804:         }
 805:         vigotoCL(tabstart);
 806:         i = shft - (inssiz - doomed);
 807:         if (i > 0) {
 808:             tabslack = inssiz - doomed;
 809:             vcsync();
 810:             goim();
 811:             do
 812:                 vputchar(' ');
 813:             while (--i);
 814:         }
 815:     }
 816:     /*
 817: 	 * Now do the data moving in the internal screen
 818: 	 * image which is common to all three cases.
 819: 	 */
 820:     tp += linend;
 821:     up = tp + shft;
 822:     i = linend - tabend;
 823:     if (i > 0)
 824:         do
 825:             *--up = *--tp;
 826:         while (--i);
 827:     if (IN && tshft) {
 828:         i = tshft;
 829:         do
 830:             *--up = ' ' | QUOTE;
 831:         while (--i);
 832:     }
 833:     hold = oldhold;
 834: }
 835: 
 836: /*
 837:  * Now do the insert of the characters (finally).
 838:  */
 839: viin(c)
 840:     int c;      /* mjm: char --> int */
 841: {
 842:     register char *tp, *up;
 843:     register int i, j;
 844:     register bool noim = 0;
 845:     int remdoom;
 846:     short oldhold = hold;
 847: 
 848:     hold |= HOLDPUPD;
 849:     if (tabsize && (IM && EI) && inssiz - doomed > tabslack)
 850:         /*
 851: 		 * There is a tab out there which will be affected
 852: 		 * by the insertion since there aren't enough doomed
 853: 		 * characters to take up all the insertion and we do
 854: 		 * have insert mode capability.
 855: 		 */
 856:         if (inscol + doomed == tabstart) {
 857:             /*
 858: 			 * The end of the doomed characters sits right at the
 859: 			 * start of the tabs, then we don't need to use insert
 860: 			 * mode; unless the tab has already been expanded
 861: 			 * in which case we MUST use insert mode.
 862: 			 */
 863:             slakused = 0;
 864:             noim = !shft;
 865:         } else {
 866:             /*
 867: 			 * The last really special case to handle is case
 868: 			 * where the tab is just sitting there and doesn't
 869: 			 * have enough slack to let the insertion take
 870: 			 * place without shifting the rest of the line
 871: 			 * over.  In this case we have to go out and
 872: 			 * delete some characters of the tab before we start
 873: 			 * or the answer will be wrong, as the rest of the
 874: 			 * line will have been shifted.  This code means
 875: 			 * that terminals with only insert chracter (no
 876: 			 * delete character) won't work correctly.
 877: 			 */
 878:             i = inssiz - doomed - tabslack - slakused;
 879:             i %= value(TABSTOP);
 880:             if (i > 0) {
 881:                 vgotoCL(tabstart);
 882:                 godm();
 883:                 for (i = inssiz - doomed - tabslack; i > 0; i--)
 884:                     vputp(DC, DEPTH(vcline));
 885:                 enddm();
 886:             }
 887:         }
 888: 
 889:     /*
 890: 	 * Now put out the characters of the actual insertion.
 891: 	 */
 892:     vigotoCL(inscol);
 893:     remdoom = doomed;
 894:     for (i = inssiz; i > 0; i--) {
 895:         if (remdoom > 0) {
 896:             remdoom--;
 897:             endim();
 898:         } else if (noim)
 899:             endim();
 900:         else if (IM && EI) {
 901:             vcsync();
 902:             goim();
 903:         }
 904:         vputchar(c);
 905:     }
 906: 
 907:     if (!IM || !EI) {
 908:         /*
 909: 		 * We are a dumb terminal; brute force update
 910: 		 * the rest of the line; this is very much an n^^2 process,
 911: 		 * and totally unreasonable at low speed.
 912: 		 *
 913: 		 * You asked for it, you get it.
 914: 		 */
 915:         tp = vtube0 + inscol + doomed;
 916:         for (i = inscol + doomed; i < tabstart; i++)
 917:             vputchar(*tp++);
 918:         hold = oldhold;
 919:         vigotoCL(tabstart + inssiz - doomed);
 920:         for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
 921:             vputchar(' ' | QUOTE);
 922:     } else {
 923:         if (!IN) {
 924:             /*
 925: 			 * On terminals without multi-line
 926: 			 * insert in the hardware, we must go fix the segments
 927: 			 * between the inserted text and the following
 928: 			 * tabs, if they are on different lines.
 929: 			 *
 930: 			 * Aaargh.
 931: 			 */
 932:             tp = vtube0;
 933:             for (j = (inscol + inssiz - 1) / WCOLS + 1;
 934:                 j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) {
 935:                 vgotoCL(j * WCOLS);
 936:                 i = inssiz - doomed;
 937:                 up = tp + j * WCOLS - i;
 938:                 goim();
 939:                 do
 940:                     vputchar(*up++);
 941:                 while (--i && *up);
 942:             }
 943:         } else {
 944:             /*
 945: 			 * On terminals with multi line inserts,
 946: 			 * life is simpler, just reflect eating of
 947: 			 * the slack.
 948: 			 */
 949:             tp = vtube0 + tabend;
 950:             for (i = tabsize - (inssiz - doomed); i >= 0; i--) {
 951:                 if ((*--tp & (QUOTE|TRIM)) == QUOTE) {
 952:                     --tabslack;
 953:                     if (tabslack >= slakused)
 954:                         continue;
 955:                 }
 956:                 *tp = ' ' | QUOTE;
 957:             }
 958:         }
 959:         /*
 960: 		 * Blank out the shifted positions to be tab positions.
 961: 		 */
 962:         if (shft) {
 963:             tp = vtube0 + tabend + shft;
 964:             for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
 965:                 if ((*--tp & QUOTE) == 0)
 966:                     *tp = ' ' | QUOTE;
 967:         }
 968:     }
 969: 
 970:     /*
 971: 	 * Finally, complete the screen image update
 972: 	 * to reflect the insertion.
 973: 	 */
 974:     hold = oldhold;
 975:     tp = vtube0 + tabstart; up = tp + inssiz - doomed;
 976:     for (i = tabstart; i > inscol + doomed; i--)
 977:         *--up = *--tp;
 978:     for (i = inssiz; i > 0; i--)
 979:         *--up = c;
 980:     doomed = 0;
 981: }
 982: 
 983: /*
 984:  * Go into ``delete mode''.  If the
 985:  * sequence which goes into delete mode
 986:  * is the same as that which goes into insert
 987:  * mode, then we are in delete mode already.
 988:  */
 989: godm()
 990: {
 991: 
 992:     if (insmode) {
 993:         if (eq(DM, IM))
 994:             return;
 995:         endim();
 996:     }
 997:     vputp(DM, 0);
 998: }
 999: 
1000: /*
1001:  * If we are coming out of delete mode, but
1002:  * delete and insert mode end with the same sequence,
1003:  * it wins to pretend we are now in insert mode,
1004:  * since we will likely want to be there again soon
1005:  * if we just moved over to delete space from part of
1006:  * a tab (above).
1007:  */
1008: enddm()
1009: {
1010: 
1011:     if (eq(DM, IM)) {
1012:         insmode = 1;
1013:         return;
1014:     }
1015:     vputp(ED, 0);
1016: }
1017: 
1018: /*
1019:  * In and out of insert mode.
1020:  * Note that the code here demands that there be
1021:  * a string for insert mode (the null string) even
1022:  * if the terminal does all insertions a single character
1023:  * at a time, since it branches based on whether IM is null.
1024:  */
1025: goim()
1026: {
1027: 
1028:     if (!insmode)
1029:         vputp(IM, 0);
1030:     insmode = 1;
1031: }
1032: 
1033: endim()
1034: {
1035: 
1036:     if (insmode) {
1037:         vputp(EI, 0);
1038:         insmode = 0;
1039:     }
1040: }
1041: 
1042: /*
1043:  * Put the character c on the screen at the current cursor position.
1044:  * This routine handles wraparound and scrolling and understands not
1045:  * to roll when splitw is set, i.e. we are working in the echo area.
1046:  * There is a bunch of hacking here dealing with the difference between
1047:  * QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also
1048:  * code to deal with terminals which overstrike, including CRT's where
1049:  * you can erase overstrikes with some work.  CRT's which do underlining
1050:  * implicitly which has to be erased (like CONCEPTS) are also handled.
1051:  */
1052: vputchar(c)
1053:     register int c;
1054: {
1055:     register char *tp;
1056:     register int d;
1057: 
1058:     c &= (QUOTE|TRIM);
1059: #ifdef TRACE
1060:     if (trace)
1061:         tracec(c);
1062: #endif
1063:     /* Fix problem of >79 chars on echo line. */
1064:     if (destcol >= WCOLS-1 && splitw && destline == WECHO)
1065:         pofix();
1066:     if (destcol >= WCOLS) {
1067:         destline += destcol / WCOLS;
1068:         destcol %= WCOLS;
1069:     }
1070:     if (destline > WBOT && (!splitw || destline > WECHO))
1071:         vrollup(destline);
1072:     tp = vtube[destline] + destcol;
1073:     switch (c) {
1074: 
1075:     case '\t':
1076:         vgotab();
1077:         return;
1078: 
1079:     case ' ':
1080:         /*
1081: 		 * We can get away without printing a space in a number
1082: 		 * of cases, but not always.  We get away with doing nothing
1083: 		 * if we are not in insert mode, and not on a CONCEPT-100
1084: 		 * like terminal, and either not in hardcopy open or in hardcopy
1085: 		 * open on a terminal with no overstriking, provided,
1086: 		 * in all cases, that nothing has ever been displayed
1087: 		 * at this position.  Ugh.
1088: 		 */
1089:         if (!insmode && !IN && (state != HARDOPEN || OS) && (*tp&TRIM) == 0) {
1090:             *tp = ' ';
1091:             destcol++;
1092:             return;
1093:         }
1094:         goto def;
1095: 
1096:     case QUOTE:
1097:         if (insmode) {
1098:             /*
1099: 			 * When in insert mode, tabs have to expand
1100: 			 * to real, printed blanks.
1101: 			 */
1102:             c = ' ' | QUOTE;
1103:             goto def;
1104:         }
1105:         if (*tp == 0) {
1106:             /*
1107: 			 * A ``space''.
1108: 			 */
1109:             if ((hold & HOLDPUPD) == 0)
1110:                 *tp = QUOTE;
1111:             destcol++;
1112:             return;
1113:         }
1114:         /*
1115: 		 * A ``space'' ontop of a part of a tab.
1116: 		 */
1117:         if (*tp & QUOTE) {
1118:             destcol++;
1119:             return;
1120:         }
1121:         c = ' ' | QUOTE;
1122:         /* fall into ... */
1123: 
1124: def:
1125:     default:
1126:         d = *tp & TRIM;
1127:         /*
1128: 		 * Now get away with doing nothing if the characters
1129: 		 * are the same, provided we are not in insert mode
1130: 		 * and if we are in hardopen, that the terminal has overstrike.
1131: 		 */
1132:         if (d == (c & TRIM) && !insmode && (state != HARDOPEN || OS)) {
1133:             if ((hold & HOLDPUPD) == 0)
1134:                 *tp = c;
1135:             destcol++;
1136:             return;
1137:         }
1138:         /*
1139: 		 * Backwards looking optimization.
1140: 		 * The low level cursor motion routines will use
1141: 		 * a cursor motion right sequence to step 1 character
1142: 		 * right.  On, e.g., a DM3025A this is 2 characters
1143: 		 * and printing is noticeably slower at 300 baud.
1144: 		 * Since the low level routines are not allowed to use
1145: 		 * spaces for positioning, we discover the common
1146: 		 * case of a single space here and force a space
1147: 		 * to be printed.
1148: 		 */
1149:         if (destcol == outcol + 1 && tp[-1] == ' ' && outline == destline) {
1150:             vputc(' ');
1151:             outcol++;
1152:         }
1153: 
1154:         /*
1155: 		 * This is an inline expansion a call to vcsync() dictated
1156: 		 * by high frequency in a profile.
1157: 		 */
1158:         if (outcol != destcol || outline != destline)
1159:             vgoto(destline, destcol);
1160: 
1161:         /*
1162: 		 * Deal with terminals which have overstrike.
1163: 		 * We handle erasing general overstrikes, erasing
1164: 		 * underlines on terminals (such as CONCEPTS) which
1165: 		 * do underlining correctly automatically (e.g. on nroff
1166: 		 * output), and remembering, in hardcopy mode,
1167: 		 * that we have overstruct something.
1168: 		 */
1169:         if (!insmode && d && d != ' ' && d != (c & TRIM)) {
1170:             if (EO && (OS || UL && (c == '_' || d == '_'))) {
1171:                 vputc(' ');
1172:                 outcol++, destcol++;
1173:                 back1();
1174:             } else
1175:                 rubble = 1;
1176:         }
1177: 
1178:         /*
1179: 		 * Unless we are just bashing characters around for
1180: 		 * inner working of insert mode, update the display.
1181: 		 */
1182:         if ((hold & HOLDPUPD) == 0)
1183:             *tp = c;
1184: 
1185:         /*
1186: 		 * In insert mode, put out the IC sequence, padded
1187: 		 * based on the depth of the current line.
1188: 		 * A terminal which had no real insert mode, rather
1189: 		 * opening a character position at a time could do this.
1190: 		 * Actually should use depth to end of current line
1191: 		 * but this rarely matters.
1192: 		 */
1193:         if (insmode)
1194:             vputp(IC, DEPTH(vcline));
1195:         vputc(c & TRIM);
1196: 
1197:         /*
1198: 		 * In insert mode, IP is a post insert pad.
1199: 		 */
1200:         if (insmode)
1201:             vputp(IP, DEPTH(vcline));
1202:         destcol++, outcol++;
1203: 
1204:         /*
1205: 		 * CONCEPT braindamage in early models:  after a wraparound
1206: 		 * the next newline is eaten.  It's hungry so we just
1207: 		 * feed it now rather than worrying about it.
1208: 		 * Fixed to use	return linefeed to work right
1209: 		 * on vt100/tab132 as well as concept.
1210: 		 */
1211:         if (XN && outcol % WCOLS == 0) {
1212:             vputc('\r');
1213:             vputc('\n');
1214:         }
1215:     }
1216: }
1217: 
1218: /*
1219:  * Delete display positions stcol through endcol.
1220:  * Amount of use of special terminal features here is limited.
1221:  */
1222: physdc(stcol, endcol)
1223:     int stcol, endcol;
1224: {
1225:     register char *tp, *up;
1226:     char *tpe;
1227:     register int i;
1228:     register int nc = endcol - stcol;
1229: 
1230: #ifdef IDEBUG
1231:     if (trace)
1232:         tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol);
1233: #endif
1234:     if (!DC || nc <= 0)
1235:         return;
1236:     if (IN) {
1237:         /*
1238: 		 * CONCEPT-100 like terminal.
1239: 		 * If there are any ``spaces'' in the material to be
1240: 		 * deleted, then this is too hard, just retype.
1241: 		 */
1242:         vprepins();
1243:         up = vtube0 + stcol;
1244:         i = nc;
1245:         do
1246:             if ((*up++ & (QUOTE|TRIM)) == QUOTE)
1247:                 return;
1248:         while (--i);
1249:         i = 2 * nc;
1250:         do
1251:             if (*up == 0 || (*up++ & QUOTE) == QUOTE)
1252:                 return;
1253:         while (--i);
1254:         vgotoCL(stcol);
1255:     } else {
1256:         /*
1257: 		 * HP like delete mode.
1258: 		 * Compute how much text we are moving over by deleting.
1259: 		 * If it appears to be faster to just retype
1260: 		 * the line, do nothing and that will be done later.
1261: 		 * We are assuming 2 output characters per deleted
1262: 		 * characters and that clear to end of line is available.
1263: 		 */
1264:         i = stcol / WCOLS;
1265:         if (i != endcol / WCOLS)
1266:             return;
1267:         i += LINE(vcline);
1268:         stcol %= WCOLS;
1269:         endcol %= WCOLS;
1270:         up = vtube[i]; tp = up + endcol; tpe = up + WCOLS;
1271:         while (tp < tpe && *tp)
1272:             tp++;
1273:         if (tp - (up + stcol) < 2 * nc)
1274:             return;
1275:         vgoto(i, stcol);
1276:     }
1277: 
1278:     /*
1279: 	 * Go into delete mode and do the actual delete.
1280: 	 * Padding is on DC itself.
1281: 	 */
1282:     godm();
1283:     for (i = nc; i > 0; i--)
1284:         vputp(DC, DEPTH(vcline));
1285:     vputp(ED, 0);
1286: 
1287:     /*
1288: 	 * Straighten up.
1289: 	 * With CONCEPT like terminals, characters are pulled left
1290: 	 * from first following null.  HP like terminals shift rest of
1291: 	 * this (single physical) line rigidly.
1292: 	 */
1293:     if (IN) {
1294:         up = vtube0 + stcol;
1295:         tp = vtube0 + endcol;
1296:         while (i = *tp++) {
1297:             if ((i & (QUOTE|TRIM)) == QUOTE)
1298:                 break;
1299:             *up++ = i;
1300:         }
1301:         do
1302:             *up++ = i;
1303:         while (--nc);
1304:     } else {
1305:         copy(up + stcol, up + endcol, WCOLS - endcol);
1306:         vclrbyte(tpe - nc, nc);
1307:     }
1308: }
1309: 
1310: #ifdef TRACE
1311: tfixnl()
1312: {
1313: 
1314:     if (trubble || techoin)
1315:         fprintf(trace, "\n");
1316:     trubble = 0, techoin = 0;
1317: }
1318: 
1319: tvliny()
1320: {
1321:     register int i;
1322: 
1323:     if (!trace)
1324:         return;
1325:     tfixnl();
1326:     fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline);
1327:     for (i = 0; i <= vcnt; i++) {
1328:         fprintf(trace, "%d", LINE(i));
1329:         if (FLAGS(i) & VDIRT)
1330:             fprintf(trace, "*");
1331:         if (DEPTH(i) != 1)
1332:             fprintf(trace, "<%d>", DEPTH(i));
1333:         if (i < vcnt)
1334:             fprintf(trace, " ");
1335:     }
1336:     fprintf(trace, "\n");
1337: }
1338: 
1339: tracec(c)
1340:     int c;      /* mjm: char --> int */
1341: {
1342: 
1343:     if (!techoin)
1344:         trubble = 1;
1345:     if (c == ESCAPE)
1346:         fprintf(trace, "$");
1347:     else if (c & QUOTE) /* mjm: for 3B (no sign extension) */
1348:         fprintf(trace, "~%c", ctlof(c&TRIM));
1349:     else if (c < ' ' || c == DELETE)
1350:         fprintf(trace, "^%c", ctlof(c));
1351:     else
1352:         fprintf(trace, "%c", c);
1353: }
1354: #endif
1355: 
1356: /*
1357:  * Put a character with possible tracing.
1358:  */
1359: vputch(c)
1360:     int c;
1361: {
1362: 
1363: #ifdef TRACE
1364:     if (trace)
1365:         tracec(c);
1366: #endif
1367:     vputc(c);
1368: }

Defined functions

enddm defined in line 1008; used 1 times
endim defined in line 1033; used 15 times
godm defined in line 989; used 2 times
goim defined in line 1025; used 6 times
physdc defined in line 1222; used 3 times
tfixnl defined in line 1311; used 20 times
tracec defined in line 1339; used 3 times
tvliny defined in line 1319; used 8 times
vclrbyte defined in line 40; used 10 times
vclrlin defined in line 54; used 3 times
vcsync defined in line 250; used 7 times
vcursaft defined in line 202; used 1 times
vcursat defined in line 189; used 4 times
vcursbef defined in line 176; used 1 times
vgotab defined in line 427; used 2 times
vgotoCL defined in line 259; used 15 times
vigoto defined in line 238; used 12 times
vigotoCL defined in line 272; used 4 times
viin defined in line 839; used 1 times
vinschar defined in line 494; used 1 times
vishft defined in line 743; used 1 times
vmaktop defined in line 466; used 1 times
vneedpos defined in line 697; used 2 times
vnpins defined in line 709; used 2 times
vprepins defined in line 455; used 5 times
vputch defined in line 1359; used 1 times
vrigid defined in line 676; used 1 times
vsetcurs defined in line 223; used 3 times

Defined variables

inscol defined in line 445; used 14 times
inssiz defined in line 444; used 26 times
linend defined in line 439; used 12 times
sccsid defined in line 8; never used
shft defined in line 446; used 14 times
slakused defined in line 447; used 8 times
tabend defined in line 441; used 16 times
tabsize defined in line 442; used 7 times
tabslack defined in line 443; used 13 times
tabstart defined in line 440; used 13 times
Last modified: 1994-12-10
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 5821
Valid CSS Valid XHTML 1.0 Strict