1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 /* Copyright (c) 1981 Regents of the University of California */ 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 #include "ex.h" 35 #include "ex_tty.h" 36 #include "ex_vis.h" 37 #ifndef PRESUNEUC 38 #include <wctype.h> 39 /* Undef putchar/getchar if they're defined. */ 40 #ifdef putchar 41 # undef putchar 42 #endif 43 #ifdef getchar 44 # undef getchar 45 #endif 46 #endif /* PRESUNEUC */ 47 48 /* 49 * Terminal driving and line formatting routines. 50 * Basic motion optimizations are done here as well 51 * as formatting of lines (printing of control characters, 52 * line numbering and the like). 53 */ 54 55 /* 56 * The routines outchar, putchar and pline are actually 57 * variables, and these variables point at the current definitions 58 * of the routines. See the routine setflav. 59 * We sometimes make outchar be routines which catch the characters 60 * to be printed, e.g. if we want to see how long a line is. 61 * During open/visual, outchar and putchar will be set to 62 * routines in the file ex_vput.c (vputchar, vinschar, etc.). 63 */ 64 int (*Outchar)() = termchar; 65 int (*Putchar)() = normchar; 66 int (*Pline)() = normline; 67 static unsigned char multic[MULTI_BYTE_MAX]; 68 bool putoctal; /* flag to say if byte should be printed as octal */ 69 int termiosflag = -1; /* flag for using termios ioctl 70 * structure */ 71 72 int (* 73 setlist(t))() 74 bool t; 75 { 76 int (*P)(); 77 78 listf = t; 79 P = Putchar; 80 Putchar = t ? listchar : normchar; 81 return (P); 82 } 83 84 int (* 85 setnumb(t))() 86 bool t; 87 { 88 int (*P)(); 89 90 numberf = t; 91 P = Pline; 92 Pline = t ? (int (*)())numbline : normline; 93 return (P); 94 } 95 96 /* 97 * Format c for list mode; leave things in common 98 * with normal print mode to be done by normchar. 99 */ 100 int 101 listchar(wchar_t c) 102 { 103 104 c &= (int)(TRIM|QUOTE); 105 switch (c) { 106 107 case '\t': 108 case '\b': 109 outchar('^'); 110 c = ctlof(c); 111 break; 112 113 case '\n': 114 break; 115 116 case (int)('\n' | QUOTE): 117 outchar('$'); 118 break; 119 120 default: 121 if((int)(c & QUOTE)) 122 break; 123 if (c < ' ' && c != '\n' || c == DELETE) 124 outchar('^'), c = ctlof(c); 125 } 126 (void) normchar(c); 127 return (0); 128 } 129 130 /* 131 * Format c for printing. Handle funnies of upper case terminals 132 * and hazeltines which don't have ~. 133 */ 134 int 135 normchar(wchar_t c) 136 { 137 char *colp; 138 139 c &= (int)(TRIM|QUOTE); 140 if (c == '~' && tilde_glitch) { 141 (void) normchar('\\'); 142 c = '^'; 143 } 144 if ((int)(c & QUOTE)) 145 switch (c) { 146 147 case (int)(' ' | QUOTE): 148 case (int)('\b' | QUOTE): 149 break; 150 151 case (int)QUOTE: 152 return (0); 153 154 default: 155 c &= (int)TRIM; 156 } 157 else if (c < ' ' && (c != '\b' || !over_strike) && c != '\n' && c != '\t' || c == DELETE) 158 putchar('^'), c = ctlof(c); 159 else if (c >= 0200 && (putoctal || !iswprint(c))) { 160 outchar('\\'); 161 outchar(((c >> 6) & 07) + '0'); 162 outchar(((c >> 3) & 07) + '0'); 163 outchar((c & 07) + '0'); 164 return (0); 165 } else if (UPPERCASE) 166 if (isupper(c)) { 167 outchar('\\'); 168 c = tolower(c); 169 } else { 170 colp = "({)}!|^~'`"; 171 while (*colp++) 172 if (c == *colp++) { 173 outchar('\\'); 174 c = colp[-2]; 175 break; 176 } 177 } 178 outchar(c); 179 return (0); 180 } 181 182 /* 183 * Print a line with a number. 184 */ 185 int 186 numbline(int i) 187 { 188 189 if (shudclob) 190 slobber(' '); 191 viprintf("%6d ", i); 192 (void) normline(); 193 return (0); 194 } 195 196 /* 197 * Normal line output, no numbering. 198 */ 199 int 200 normline(void) 201 { 202 unsigned char *cp; 203 int n; 204 wchar_t wchar; 205 if (shudclob) 206 slobber(linebuf[0]); 207 /* pdp-11 doprnt is not reentrant so can't use "printf" here 208 in case we are tracing */ 209 for (cp = linebuf; *cp;) 210 if((n = mbtowc(&wchar, (char *)cp, MULTI_BYTE_MAX)) < 0) { 211 putoctal = 1; 212 putchar(*cp++); 213 putoctal = 0; 214 } else { 215 cp += n; 216 putchar(wchar); 217 } 218 if (!inopen) 219 putchar((int)('\n' | QUOTE)); 220 return (0); 221 } 222 223 /* 224 * Given c at the beginning of a line, determine whether 225 * the printing of the line will erase or otherwise obliterate 226 * the prompt which was printed before. If it won't, do it now. 227 */ 228 void 229 slobber(int c) 230 { 231 232 shudclob = 0; 233 switch (c) { 234 235 case '\t': 236 if (Putchar == listchar) 237 return; 238 break; 239 240 default: 241 return; 242 243 case ' ': 244 case 0: 245 break; 246 } 247 if (over_strike) 248 return; 249 flush(); 250 (void) putch(' '); 251 tputs(cursor_left, 0, putch); 252 } 253 254 /* 255 * The output buffer is initialized with a useful error 256 * message so we don't have to keep it in data space. 257 */ 258 static wchar_t linb[66]; 259 wchar_t *linp = linb; 260 261 /* 262 * Phadnl records when we have already had a complete line ending with \n. 263 * If another line starts without a flush, and the terminal suggests it, 264 * we switch into -nl mode so that we can send linefeeds to avoid 265 * a lot of spacing. 266 */ 267 static bool phadnl; 268 269 /* 270 * Indirect to current definition of putchar. 271 */ 272 int 273 putchar(int c) 274 { 275 return ((*Putchar)((wchar_t)c)); 276 } 277 278 /* 279 * Termchar routine for command mode. 280 * Watch for possible switching to -nl mode. 281 * Otherwise flush into next level of buffering when 282 * small buffer fills or at a newline. 283 */ 284 int 285 termchar(wchar_t c) 286 { 287 288 if (pfast == 0 && phadnl) 289 pstart(); 290 if (c == '\n') 291 phadnl = 1; 292 else if (linp >= &linb[63]) 293 flush1(); 294 *linp++ = c; 295 if (linp >= &linb[63]) { 296 fgoto(); 297 flush1(); 298 } 299 return (0); 300 } 301 302 void 303 flush(void) 304 { 305 306 flush1(); 307 flush2(); 308 } 309 310 /* 311 * Flush from small line buffer into output buffer. 312 * Work here is destroying motion into positions, and then 313 * letting fgoto do the optimized motion. 314 */ 315 void 316 flush1(void) 317 { 318 wchar_t *lp; 319 wchar_t c; 320 #ifdef PRESUNEUC 321 /* used for multibyte characters split between lines */ 322 int splitcnt = 0; 323 #else 324 /* used for multicolumn character substitution and padding */ 325 int fillercnt = 0; 326 #endif /* PRESUNEUC */ 327 *linp = 0; 328 lp = linb; 329 while (*lp) 330 switch (c = *lp++) { 331 332 case '\r': 333 destline += destcol / columns; 334 destcol = 0; 335 continue; 336 337 case '\b': 338 if (destcol) 339 destcol--; 340 continue; 341 342 case ' ': 343 destcol++; 344 continue; 345 346 case '\t': 347 destcol += value(vi_TABSTOP) - destcol % value(vi_TABSTOP); 348 continue; 349 350 case '\n': 351 destline += destcol / columns + 1; 352 if (destcol != 0 && destcol % columns == 0) 353 destline--; 354 destcol = 0; 355 continue; 356 357 default: 358 fgoto(); 359 for (;;) { 360 int length, length2; 361 unsigned char *p; 362 c &= TRIM; 363 if ((length = wcwidth(c)) < 0) 364 length = 0; 365 if (auto_right_margin == 0 && outcol >= columns) 366 fgoto(); 367 if((destcol % columns) + length - 1 >= columns) { 368 #ifdef PRESUNEUC 369 /* represent split chars by '>' */ 370 splitcnt = length - 1; 371 c = '>'; 372 #else 373 /* substitute/wrap multicolumn char */ 374 if(mc_wrap) { 375 fillercnt = columns - 376 (destcol % columns); 377 while(fillercnt) { 378 (void) putch(mc_filler); 379 outcol++; 380 destcol++; 381 fillercnt--; 382 } 383 } else { 384 fillercnt = length - 1; 385 c = mc_filler; 386 } 387 #endif /* PRESUNEUC */ 388 continue; 389 } 390 length2 = wctomb((char *)multic, c); 391 p = multic; 392 while(length2--) 393 (void) putch(*p++); 394 if (c == '\b') { 395 outcol--; 396 destcol--; 397 } else if (c >= ' ' && c != DELETE) { 398 outcol += length; 399 destcol += length; 400 if (eat_newline_glitch && outcol % columns == 0) 401 (void) putch('\r'), 402 (void) putch('\n'); 403 } 404 #ifdef PRESUNEUC 405 if(splitcnt) { 406 splitcnt--; 407 c = '>'; 408 } else 409 c = *lp++; 410 #else 411 if(fillercnt) { 412 fillercnt--; 413 c = mc_filler; 414 if(c == ' ') 415 continue; 416 } else 417 c = *lp++; 418 #endif /* PRESUNEUC */ 419 if (c <= ' ') 420 break; 421 } 422 --lp; 423 continue; 424 } 425 linp = linb; 426 } 427 428 void 429 flush2(void) 430 { 431 432 fgoto(); 433 flusho(); 434 pstop(); 435 } 436 437 /* 438 * Sync the position of the output cursor. 439 * Most work here is rounding for terminal boundaries getting the 440 * column position implied by wraparound or the lack thereof and 441 * rolling up the screen to get destline on the screen. 442 */ 443 void 444 fgoto(void) 445 { 446 int l, c; 447 448 if (destcol > columns - 1) { 449 destline += destcol / columns; 450 destcol %= columns; 451 } 452 if (outcol > columns - 1) { 453 l = (outcol + 1) / columns; 454 outline += l; 455 outcol %= columns; 456 if (auto_right_margin == 0) { 457 while (l > 0) { 458 if (pfast) 459 tputs(carriage_return, 0, putch); 460 tputs(cursor_down, 0, putch); 461 l--; 462 } 463 outcol = 0; 464 } 465 if (outline > lines - 1) { 466 destline -= outline - (lines - 1); 467 outline = lines - 1; 468 } 469 } 470 if (destline > lines - 1) { 471 l = destline; 472 destline = lines - 1; 473 if (outline < lines - 1) { 474 c = destcol; 475 if (pfast == 0 && (!cursor_address || holdcm)) 476 destcol = 0; 477 fgoto(); 478 destcol = c; 479 } 480 while (l > lines - 1) { 481 /* 482 * The following linefeed (or simulation thereof) 483 * is supposed to scroll up the screen, since we 484 * are on the bottom line. 485 * 486 * Superbee glitch: in the middle of the screen we 487 * have to use esc B (down) because linefeed messes up 488 * in "Efficient Paging" mode (which is essential in 489 * some SB's because CRLF mode puts garbage 490 * in at end of memory), but you must use linefeed to 491 * scroll since down arrow won't go past memory end. 492 * I turned this off after receiving Paul Eggert's 493 * Superbee description which wins better. 494 */ 495 if (scroll_forward /* && !beehive_glitch */ && pfast) 496 tputs(scroll_forward, 0, putch); 497 else 498 (void) putch('\n'); 499 l--; 500 if (pfast == 0) 501 outcol = 0; 502 } 503 } 504 if (destline < outline && !(cursor_address && !holdcm || cursor_up || cursor_home)) 505 destline = outline; 506 if (cursor_address && !holdcm) 507 if (plod(costCM) > 0) 508 plod(0); 509 else 510 tputs(tparm(cursor_address, destline, destcol), 0, putch); 511 else 512 plod(0); 513 outline = destline; 514 outcol = destcol; 515 } 516 517 /* 518 * Tab to column col by flushing and then setting destcol. 519 * Used by "set all". 520 */ 521 void 522 gotab(int col) 523 { 524 525 flush1(); 526 destcol = col; 527 } 528 529 /* 530 * Move (slowly) to destination. 531 * Hard thing here is using home cursor on really deficient terminals. 532 * Otherwise just use cursor motions, hacking use of tabs and overtabbing 533 * and backspace. 534 */ 535 536 static int plodcnt, plodflg; 537 538 int 539 #ifdef __STDC__ 540 plodput(char c) 541 #else 542 plodput(c) 543 char c; 544 #endif 545 { 546 547 if (plodflg) 548 plodcnt--; 549 else 550 (void) putch(c); 551 return (0); 552 } 553 554 int 555 plod(int cnt) 556 { 557 int i, j, k; 558 int soutcol, soutline; 559 560 plodcnt = plodflg = cnt; 561 soutcol = outcol; 562 soutline = outline; 563 /* 564 * Consider homing and moving down/right from there, vs moving 565 * directly with local motions to the right spot. 566 */ 567 if (cursor_home) { 568 /* 569 * i is the cost to home and tab/space to the right to 570 * get to the proper column. This assumes cursor_right costs 571 * 1 char. So i+destcol is cost of motion with home. 572 */ 573 if (tab && value(vi_HARDTABS)) 574 i = (destcol / value(vi_HARDTABS)) + (destcol % value(vi_HARDTABS)); 575 else 576 i = destcol; 577 /* 578 * j is cost to move locally without homing 579 */ 580 if (destcol >= outcol) { /* if motion is to the right */ 581 if (value(vi_HARDTABS)) { 582 j = destcol / value(vi_HARDTABS) - outcol / value(vi_HARDTABS); 583 if (tab && j) 584 j += destcol % value(vi_HARDTABS); 585 else 586 j = destcol - outcol; 587 } else 588 j = destcol - outcol; 589 } else 590 /* leftward motion only works if we can backspace. */ 591 if (outcol - destcol <= i && (cursor_left)) 592 i = j = outcol - destcol; /* cheaper to backspace */ 593 else 594 j = i + 1; /* impossibly expensive */ 595 596 /* k is the absolute value of vertical distance */ 597 k = outline - destline; 598 if (k < 0) 599 k = -k; 600 j += k; 601 602 /* 603 * Decision. We may not have a choice if no cursor_up. 604 */ 605 if (i + destline < j || (!cursor_up && destline < outline)) { 606 /* 607 * Cheaper to home. Do it now and pretend it's a 608 * regular local motion. 609 */ 610 tputs(cursor_home, 0, plodput); 611 outcol = outline = 0; 612 } else if (cursor_to_ll) { 613 /* 614 * Quickly consider homing down and moving from there. 615 * Assume cost of cursor_to_ll is 2. 616 */ 617 k = (lines - 1) - destline; 618 if (i + k + 2 < j && (k<=0 || cursor_up)) { 619 tputs(cursor_to_ll, 0, plodput); 620 outcol = 0; 621 outline = lines - 1; 622 } 623 } 624 } else 625 /* 626 * No home and no up means it's impossible, so we return an 627 * incredibly big number to make cursor motion win out. 628 */ 629 if (!cursor_up && destline < outline) 630 return (500); 631 if (tab && value(vi_HARDTABS)) 632 i = destcol % value(vi_HARDTABS) 633 + destcol / value(vi_HARDTABS); 634 else 635 i = destcol; 636 /* 637 if (back_tab && outcol > destcol && (j = (((outcol+7) & ~7) - destcol - 1) >> 3)) { 638 j *= (k = strlen(back_tab)); 639 if ((k += (destcol&7)) > 4) 640 j += 8 - (destcol&7); 641 else 642 j += k; 643 } else 644 */ 645 j = outcol - destcol; 646 /* 647 * If we will later need a \n which will turn into a \r\n by 648 * the system or the terminal, then don't bother to try to \r. 649 */ 650 if ((NONL || !pfast) && outline < destline) 651 goto dontcr; 652 /* 653 * If the terminal will do a \r\n and there isn't room for it, 654 * then we can't afford a \r. 655 */ 656 if (!carriage_return && outline >= destline) 657 goto dontcr; 658 /* 659 * If it will be cheaper, or if we can't back up, then send 660 * a return preliminarily. 661 */ 662 if (j > i + 1 || outcol > destcol && !cursor_left) { 663 /* 664 * BUG: this doesn't take the (possibly long) length 665 * of carriage_return into account. 666 */ 667 if (carriage_return) { 668 tputs(carriage_return, 0, plodput); 669 outcol = 0; 670 } else if (newline) { 671 tputs(newline, 0, plodput); 672 outline++; 673 outcol = 0; 674 } 675 } 676 dontcr: 677 /* Move down, if necessary, until we are at the desired line */ 678 while (outline < destline) { 679 j = destline - outline; 680 if (j > costDP && parm_down_cursor) { 681 /* Win big on Tek 4025 */ 682 tputs(tparm(parm_down_cursor, j), j, plodput); 683 outline += j; 684 } 685 else { 686 outline++; 687 if (cursor_down && pfast) 688 tputs(cursor_down, 0, plodput); 689 else 690 (void) plodput('\n'); 691 } 692 if (plodcnt < 0) 693 goto out; 694 if (NONL || pfast == 0) 695 outcol = 0; 696 } 697 if (back_tab) 698 k = strlen(back_tab); /* should probably be cost(back_tab) and moved out */ 699 /* Move left, if necessary, to desired column */ 700 while (outcol > destcol) { 701 if (plodcnt < 0) 702 goto out; 703 if (back_tab && !insmode && outcol - destcol > 4+k) { 704 tputs(back_tab, 0, plodput); 705 outcol--; 706 if (value(vi_HARDTABS)) 707 outcol -= outcol % value(vi_HARDTABS); /* outcol &= ~7; */ 708 continue; 709 } 710 j = outcol - destcol; 711 if (j > costLP && parm_left_cursor) { 712 tputs(tparm(parm_left_cursor, j), j, plodput); 713 outcol -= j; 714 } 715 else { 716 outcol--; 717 tputs(cursor_left, 0, plodput); 718 } 719 } 720 /* Move up, if necessary, to desired row */ 721 while (outline > destline) { 722 j = outline - destline; 723 if (parm_up_cursor && j > 1) { 724 /* Win big on Tek 4025 */ 725 tputs(tparm(parm_up_cursor, j), j, plodput); 726 outline -= j; 727 } 728 else { 729 outline--; 730 tputs(cursor_up, 0, plodput); 731 } 732 if (plodcnt < 0) 733 goto out; 734 } 735 /* 736 * Now move to the right, if necessary. We first tab to 737 * as close as we can get. 738 */ 739 if (value(vi_HARDTABS) && tab && !insmode && destcol - outcol > 1) { 740 /* tab to right as far as possible without passing col */ 741 for (;;) { 742 i = tabcol(outcol, value(vi_HARDTABS)); 743 if (i > destcol) 744 break; 745 if (tab) 746 tputs(tab, 0, plodput); 747 else 748 (void) plodput('\t'); 749 outcol = i; 750 } 751 /* consider another tab and then some backspaces */ 752 if (destcol - outcol > 4 && i < columns && cursor_left) { 753 tputs(tab, 0, plodput); 754 outcol = i; 755 /* 756 * Back up. Don't worry about parm_left_cursor because 757 * it's never more than 4 spaces anyway. 758 */ 759 while (outcol > destcol) { 760 outcol--; 761 tputs(cursor_left, 0, plodput); 762 } 763 } 764 } 765 /* 766 * We've tabbed as much as possible. If we still need to go 767 * further (not exact or can't tab) space over. This is a 768 * very common case when moving to the right with space. 769 */ 770 while (outcol < destcol) { 771 j = destcol - outcol; 772 if (j > costRP && parm_right_cursor) { 773 /* 774 * This probably happens rarely, if at all. 775 * It seems mainly useful for ANSI terminals 776 * with no hardware tabs, and I don't know 777 * of any such terminal at the moment. 778 */ 779 tputs(tparm(parm_right_cursor, j), j, plodput); 780 outcol += j; 781 } 782 else { 783 /* 784 * move one char to the right. We don't use right 785 * because it's better to just print the char we are 786 * moving over. There are various exceptions, however. 787 * If !inopen, vtube contains garbage. If the char is 788 * a null or a tab we want to print a space. Other 789 * random chars we use space for instead, too. 790 */ 791 wchar_t wchar; 792 int length, scrlength; 793 unsigned char multic[MB_LEN_MAX]; 794 795 if (!inopen || vtube[outline]==NULL || 796 (wchar=vtube[outline][outcol]) < ' ') 797 wchar = ' '; 798 if((int)(wchar & QUOTE)) /* no sign extension on 3B */ 799 wchar = ' '; 800 length = wctomb((char *)multic, wchar); 801 if ((scrlength = wcwidth(wchar)) < 0) 802 scrlength = 0; 803 /* assume multibyte terminals have cursor_right */ 804 if (insmode && cursor_right || length > 1 || wchar == FILLER) { 805 int diff = destcol - outcol; 806 j = (wchar == FILLER ? 1 : scrlength > diff ? diff : scrlength); 807 while(j--) { 808 outcol++; 809 tputs(cursor_right, 0, plodput); 810 } 811 } else { 812 (void) plodput((char)multic[0]); 813 outcol++; 814 } 815 } 816 if (plodcnt < 0) 817 goto out; 818 } 819 out: 820 if(plodflg) { 821 outcol = soutcol; 822 outline = soutline; 823 } 824 return(plodcnt); 825 } 826 827 /* 828 * An input line arrived. 829 * Calculate new (approximate) screen line position. 830 * Approximate because kill character echoes newline with 831 * no feedback and also because of long input lines. 832 */ 833 void 834 noteinp(void) 835 { 836 837 outline++; 838 if (outline > lines - 1) 839 outline = lines - 1; 840 destline = outline; 841 destcol = outcol = 0; 842 } 843 844 /* 845 * Something weird just happened and we 846 * lost track of what's happening out there. 847 * Since we can't, in general, read where we are 848 * we just reset to some known state. 849 * On cursor addressable terminals setting to unknown 850 * will force a cursor address soon. 851 */ 852 void 853 termreset(void) 854 { 855 856 endim(); 857 if (enter_ca_mode) 858 putpad((unsigned char *)enter_ca_mode); 859 destcol = 0; 860 destline = lines - 1; 861 if (cursor_address) { 862 outcol = UKCOL; 863 outline = UKCOL; 864 } else { 865 outcol = destcol; 866 outline = destline; 867 } 868 } 869 870 /* 871 * Low level buffering, with the ability to drain 872 * buffered output without printing it. 873 */ 874 unsigned char *obp = obuf; 875 876 void 877 draino(void) 878 { 879 880 obp = obuf; 881 } 882 883 void 884 flusho(void) 885 { 886 if (obp != obuf) { 887 write(1, obuf, obp - obuf); 888 #ifdef TRACE 889 if (trace) 890 fwrite(obuf, 1, obp-obuf, trace); 891 #endif 892 obp = obuf; 893 } 894 } 895 896 void 897 putnl(void) 898 { 899 900 putchar('\n'); 901 } 902 903 void 904 putS(unsigned char *cp) 905 { 906 907 if (cp == NULL) 908 return; 909 while (*cp) 910 (void) putch(*cp++); 911 } 912 913 int 914 putch(char c) 915 { 916 917 #ifdef OLD3BTTY 918 if(c == '\n') /* Fake "\n\r" for '\n' til fix in 3B firmware */ 919 (void) putch('\r'); /* vi does "stty -icanon" => -onlcr !! */ 920 #endif 921 *obp++ = c; 922 if (obp >= &obuf[sizeof obuf]) 923 flusho(); 924 return (0); 925 } 926 927 /* 928 * Miscellaneous routines related to output. 929 */ 930 931 /* 932 * Put with padding 933 */ 934 void 935 putpad(unsigned char *cp) 936 { 937 938 flush(); 939 tputs((char *)cp, 0, putch); 940 } 941 942 /* 943 * Set output through normal command mode routine. 944 */ 945 void 946 setoutt(void) 947 { 948 949 Outchar = termchar; 950 } 951 952 /* 953 * Printf (temporarily) in list mode. 954 */ 955 /*VARARGS2*/ 956 void 957 lprintf(unsigned char *cp, unsigned char *dp, ...) 958 { 959 int (*P)(); 960 961 P = setlist(1); 962 #ifdef PRESUNEUC 963 viprintf(cp, dp); 964 #else 965 viprintf((char *)cp, (char *)dp); 966 #endif /* PRESUNEUC */ 967 Putchar = P; 968 } 969 970 /* 971 * Newline + flush. 972 */ 973 void 974 putNFL() 975 { 976 977 putnl(); 978 flush(); 979 } 980 981 /* 982 * Try to start -nl mode. 983 */ 984 void 985 pstart(void) 986 { 987 988 if (NONL) 989 return; 990 if (!value(vi_OPTIMIZE)) 991 return; 992 if (ruptible == 0 || pfast) 993 return; 994 fgoto(); 995 flusho(); 996 pfast = 1; 997 normtty++; 998 tty = normf; 999 tty.c_oflag &= ~(ONLCR|TAB3); 1000 tty.c_lflag &= ~ECHO; 1001 saveterm(); 1002 sTTY(2); 1003 } 1004 1005 /* 1006 * Stop -nl mode. 1007 */ 1008 void 1009 pstop(void) 1010 { 1011 1012 if (inopen) 1013 return; 1014 phadnl = 0; 1015 linp = linb; 1016 draino(); 1017 normal(normf); 1018 pfast &= ~1; 1019 } 1020 1021 /* 1022 * Prep tty for open mode. 1023 */ 1024 ttymode 1025 ostart() 1026 { 1027 ttymode f; 1028 1029 /* 1030 if (!intty) 1031 error("Open and visual must be used interactively"); 1032 */ 1033 (void) gTTY(2); 1034 normtty++; 1035 f = tty; 1036 tty = normf; 1037 tty.c_iflag &= ~ICRNL; 1038 tty.c_lflag &= ~(ECHO|ICANON); 1039 tty.c_oflag &= ~(TAB3|ONLCR); 1040 tty.c_cc[VMIN] = 1; 1041 tty.c_cc[VTIME] = 1; 1042 ttcharoff(); 1043 sTTY(2); 1044 tostart(); 1045 pfast |= 2; 1046 saveterm(); 1047 return (f); 1048 } 1049 1050 /* actions associated with putting the terminal in open mode */ 1051 void 1052 tostart(void) 1053 { 1054 putpad((unsigned char *)cursor_visible); 1055 putpad((unsigned char *)keypad_xmit); 1056 if (!value(vi_MESG)) { 1057 if (ttynbuf[0] == 0) { 1058 char *tn; 1059 if ((tn=ttyname(2)) == NULL && 1060 (tn=ttyname(1)) == NULL && 1061 (tn=ttyname(0)) == NULL) 1062 ttynbuf[0] = 1; 1063 else 1064 strcpy(ttynbuf, tn); 1065 } 1066 if (ttynbuf[0] != 1) { 1067 struct stat64 sbuf; 1068 stat64((char *)ttynbuf, &sbuf); 1069 ttymesg = FMODE(sbuf) & 0777; 1070 chmod((char *)ttynbuf, 0600); 1071 } 1072 } 1073 } 1074 1075 /* 1076 * Turn off start/stop chars if they aren't the default ^S/^Q. 1077 * This is so people who make esc their start/stop don't lose. 1078 * We always turn off quit since datamedias send ^\ for their 1079 * right arrow key. 1080 */ 1081 1082 void 1083 ttcharoff(void) 1084 { 1085 /* 1086 * use 200 instead of 377 because 377 is y-umlaut 1087 * in ISO 8859/1 1088 */ 1089 tty.c_cc[VQUIT] = termiosflag ? _POSIX_VDISABLE : '\200'; 1090 if (tty.c_cc[VSTART] != CTRL('q')) 1091 tty.c_cc[VSTART] = _POSIX_VDISABLE; 1092 if (tty.c_cc[VSTOP] != CTRL('s')) 1093 tty.c_cc[VSTOP] = _POSIX_VDISABLE; 1094 /* We will read ^z and suspend ourselves via kill */ 1095 tty.c_cc[VSUSP] = _POSIX_VDISABLE; 1096 tty.c_cc[VDSUSP] = _POSIX_VDISABLE; 1097 tty.c_cc[VREPRINT] = _POSIX_VDISABLE; 1098 tty.c_cc[VDISCARD] = _POSIX_VDISABLE; 1099 tty.c_cc[VWERASE] = _POSIX_VDISABLE; 1100 tty.c_cc[VLNEXT] = _POSIX_VDISABLE; 1101 } 1102 1103 /* 1104 * Stop open, restoring tty modes. 1105 */ 1106 void 1107 ostop(ttymode f) 1108 { 1109 1110 pfast = (f.c_oflag & ONLCR) == 0; 1111 termreset(), fgoto(), flusho(); 1112 normal(f); 1113 tostop(); 1114 } 1115 1116 /* Actions associated with putting the terminal in the right mode. */ 1117 void 1118 tostop(void) 1119 { 1120 putpad((unsigned char *)clr_eos); 1121 putpad((unsigned char *)cursor_normal); 1122 putpad((unsigned char *)keypad_local); 1123 if (!value(vi_MESG) && ttynbuf[0]>1) 1124 chmod((char *)ttynbuf, ttymesg); 1125 } 1126 1127 #ifndef CBREAK 1128 /* 1129 * Into cooked mode for interruptibility. 1130 */ 1131 vcook() 1132 { 1133 1134 tty.sg_flags &= ~RAW; 1135 sTTY(2); 1136 } 1137 1138 /* 1139 * Back into raw mode. 1140 */ 1141 vraw() 1142 { 1143 1144 tty.sg_flags |= RAW; 1145 sTTY(2); 1146 } 1147 #endif 1148 1149 /* 1150 * Restore flags to normal state f. 1151 */ 1152 void 1153 normal(ttymode f) 1154 { 1155 1156 if (normtty > 0) { 1157 setty(f); 1158 normtty--; 1159 } 1160 } 1161 1162 /* 1163 * Straight set of flags to state f. 1164 */ 1165 ttymode 1166 setty(f) 1167 ttymode f; 1168 { 1169 int isnorm = 0; 1170 ttymode ot; 1171 ot = tty; 1172 1173 if (tty.c_lflag & ICANON) 1174 ttcharoff(); 1175 else 1176 isnorm = 1; 1177 tty = f; 1178 sTTY(2); 1179 if (!isnorm) 1180 saveterm(); 1181 return (ot); 1182 } 1183 1184 static struct termio termio; 1185 1186 int 1187 gTTY(int i) 1188 { 1189 if(termiosflag < 0) { 1190 if(ioctl(i, TCGETS, &tty) == 0) 1191 termiosflag = 1; 1192 else { 1193 termiosflag = 0; 1194 if(ioctl(i, TCGETA, &termio) < 0) 1195 return (-1); 1196 tty.c_iflag = termio.c_iflag; 1197 tty.c_oflag = termio.c_oflag; 1198 tty.c_cflag = termio.c_cflag; 1199 tty.c_lflag = termio.c_lflag; 1200 for(i = 0; i < NCC; i++) 1201 tty.c_cc[i] = termio.c_cc[i]; 1202 } 1203 return (0); 1204 } 1205 if(termiosflag) 1206 return (ioctl(i, TCGETS, &tty)); 1207 if(ioctl(i, TCGETA, &termio) < 0) 1208 return (-1); 1209 tty.c_iflag = termio.c_iflag; 1210 tty.c_oflag = termio.c_oflag; 1211 tty.c_cflag = termio.c_cflag; 1212 tty.c_lflag = termio.c_lflag; 1213 for(i = 0; i < NCC; i++) 1214 tty.c_cc[i] = termio.c_cc[i]; 1215 return (0); 1216 } 1217 1218 /* 1219 * sTTY: set the tty modes on file descriptor i to be what's 1220 * currently in global "tty". (Also use nttyc if needed.) 1221 */ 1222 void 1223 sTTY(int i) 1224 { 1225 int j; 1226 if(termiosflag) 1227 ioctl(i, TCSETSW, &tty); 1228 else { 1229 termio.c_iflag = tty.c_iflag; 1230 termio.c_oflag = tty.c_oflag; 1231 termio.c_cflag = tty.c_cflag; 1232 termio.c_lflag = tty.c_lflag; 1233 for(j = 0; j < NCC; j++) 1234 termio.c_cc[j] = tty.c_cc[j]; 1235 ioctl(i, TCSETAW, &termio); 1236 } 1237 } 1238 1239 /* 1240 * Print newline, or blank if in open/visual 1241 */ 1242 void 1243 noonl(void) 1244 { 1245 1246 putchar(Outchar != termchar ? ' ' : '\n'); 1247 } 1248