1: /* Copyright (c) 1979 Regents of the University of California */ 2: #include "ex.h" 3: #include "ex_tty.h" 4: #include "ex_vis.h" 5: 6: /* 7: * Terminal driving and line formatting routines. 8: * Basic motion optimizations are done here as well 9: * as formatting of lines (printing of control characters, 10: * line numbering and the like). 11: */ 12: 13: /* 14: * The routines outchar, putchar and pline are actually 15: * variables, and these variables point at the current definitions 16: * of the routines. See the routine setflav. 17: * We sometimes make outchar be routines which catch the characters 18: * to be printed, e.g. if we want to see how long a line is. 19: * During open/visual, outchar and putchar will be set to 20: * routines in the file ex_vput.c (vputchar, vinschar, etc.). 21: */ 22: int (*Outchar)() = termchar; 23: int (*Putchar)() = normchar; 24: int (*Pline)() = normline; 25: 26: int (* 27: setlist(t))() 28: bool t; 29: { 30: register int (*P)(); 31: 32: listf = t; 33: P = Putchar; 34: Putchar = t ? listchar : normchar; 35: return (P); 36: } 37: 38: int (* 39: setnumb(t))() 40: bool t; 41: { 42: register int (*P)(); 43: 44: numberf = t; 45: P = Pline; 46: Pline = t ? numbline : normline; 47: return (P); 48: } 49: 50: /* 51: * Format c for list mode; leave things in common 52: * with normal print mode to be done by normchar. 53: */ 54: listchar(c) 55: register short c; 56: { 57: 58: c &= (TRIM|QUOTE); 59: switch (c) { 60: 61: case '\t': 62: case '\b': 63: outchar('^'); 64: c = ctlof(c); 65: break; 66: 67: case '\n': 68: break; 69: 70: case '\n' | QUOTE: 71: outchar('$'); 72: break; 73: 74: default: 75: if (c & QUOTE) 76: break; 77: if (c < ' ' && c != '\n' || c == DELETE) 78: outchar('^'), c = ctlof(c); 79: break; 80: } 81: normchar(c); 82: } 83: 84: /* 85: * Format c for printing. Handle funnies of upper case terminals 86: * and crocky hazeltines which don't have ~. 87: */ 88: normchar(c) 89: register short c; 90: { 91: register char *colp; 92: 93: c &= (TRIM|QUOTE); 94: if (c == '~' && HZ) { 95: normchar('\\'); 96: c = '^'; 97: } 98: if (c & QUOTE) 99: switch (c) { 100: 101: case ' ' | QUOTE: 102: case '\b' | QUOTE: 103: break; 104: 105: case QUOTE: 106: return; 107: 108: default: 109: c &= TRIM; 110: } 111: else if (c < ' ' && (c != '\b' || !OS) && c != '\n' && c != '\t' || c == DELETE) 112: putchar('^'), c = ctlof(c); 113: else if (UPPERCASE) 114: if (isupper(c)) { 115: outchar('\\'); 116: c = tolower(c); 117: } else { 118: colp = "({)}!|^~'`"; 119: while (*colp++) 120: if (c == *colp++) { 121: outchar('\\'); 122: c = colp[-2]; 123: break; 124: } 125: } 126: outchar(c); 127: } 128: 129: /* 130: * Print a line with a number. 131: */ 132: numbline(i) 133: int i; 134: { 135: 136: if (shudclob) 137: slobber(' '); 138: printf("%6d ", i); 139: normline(); 140: } 141: 142: /* 143: * Normal line output, no numbering. 144: */ 145: normline() 146: { 147: register char *cp; 148: 149: if (shudclob) 150: slobber(linebuf[0]); 151: /* pdp-11 doprnt is not reentrant so can't use "printf" here 152: in case we are tracing */ 153: for (cp = linebuf; *cp;) 154: putchar(*cp++); 155: if (!inopen) 156: putchar('\n' | QUOTE); 157: } 158: 159: /* 160: * Given c at the beginning of a line, determine whether 161: * the printing of the line will erase or otherwise obliterate 162: * the prompt which was printed before. If it won't, do it now. 163: */ 164: slobber(c) 165: int c; 166: { 167: 168: shudclob = 0; 169: switch (c) { 170: 171: case '\t': 172: if (Putchar == listchar) 173: return; 174: break; 175: 176: default: 177: return; 178: 179: case ' ': 180: case 0: 181: break; 182: } 183: if (OS) 184: return; 185: flush(); 186: putch(' '); 187: if (BC) 188: tputs(BC, 0, putch); 189: else 190: putch('\b'); 191: } 192: 193: /* 194: * The output buffer is initialized with a useful error 195: * message so we don't have to keep it in data space. 196: */ 197: static char linb[66] = { 198: 'E', 'r', 'r', 'o', 'r', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', ' ', 199: 'f', 'i', 'l', 'e', ' ', 'n', 'o', 't', ' ', 200: 'a', 'v', 'a', 'i', 'l', 'a', 'b', 'l', 'e', '\n', 0 201: }; 202: static char *linp = linb + 33; 203: 204: /* 205: * Phadnl records when we have already had a complete line ending with \n. 206: * If another line starts without a flush, and the terminal suggests it, 207: * we switch into -nl mode so that we can send lineffeeds to avoid 208: * a lot of spacing. 209: */ 210: static bool phadnl; 211: 212: /* 213: * Indirect to current definition of putchar. 214: */ 215: putchar(c) 216: int c; 217: { 218: 219: (*Putchar)(c); 220: } 221: 222: /* 223: * Termchar routine for command mode. 224: * Watch for possible switching to -nl mode. 225: * Otherwise flush into next level of buffering when 226: * small buffer fills or at a newline. 227: */ 228: termchar(c) 229: int c; 230: { 231: 232: if (pfast == 0 && phadnl) 233: pstart(); 234: if (c == '\n') 235: phadnl = 1; 236: else if (linp >= &linb[63]) 237: flush1(); 238: *linp++ = c; 239: if (linp >= &linb[63]) { 240: fgoto(); 241: flush1(); 242: } 243: } 244: 245: flush() 246: { 247: 248: flush1(); 249: flush2(); 250: } 251: 252: /* 253: * Flush from small line buffer into output buffer. 254: * Work here is destroying motion into positions, and then 255: * letting fgoto do the optimized motion. 256: */ 257: flush1() 258: { 259: register char *lp; 260: register short c; 261: 262: *linp = 0; 263: lp = linb; 264: while (*lp) 265: switch (c = *lp++) { 266: 267: case '\r': 268: destline += destcol / COLUMNS; 269: destcol = 0; 270: continue; 271: 272: case '\b': 273: if (destcol) 274: destcol--; 275: continue; 276: 277: case ' ': 278: destcol++; 279: continue; 280: 281: case '\t': 282: destcol += value(TABSTOP) - destcol % value(TABSTOP); 283: continue; 284: 285: case '\n': 286: destline += destcol / COLUMNS + 1; 287: if (destcol != 0 && destcol % COLUMNS == 0) 288: destline--; 289: destcol = 0; 290: continue; 291: 292: default: 293: fgoto(); 294: for (;;) { 295: if (AM == 0 && outcol == COLUMNS) 296: fgoto(); 297: c &= TRIM; 298: putch(c); 299: if (c == '\b') { 300: outcol--; 301: destcol--; 302: } else if (c >= ' ' && c != DELETE) { 303: outcol++; 304: destcol++; 305: if (XN && outcol % COLUMNS == 0) 306: putch('\n'); 307: } 308: c = *lp++; 309: if (c <= ' ') 310: break; 311: } 312: --lp; 313: continue; 314: } 315: linp = linb; 316: } 317: 318: flush2() 319: { 320: 321: fgoto(); 322: flusho(); 323: pstop(); 324: } 325: 326: /* 327: * Sync the position of the output cursor. 328: * Most work here is rounding for terminal boundaries getting the 329: * column position implied by wraparound or the lack thereof and 330: * rolling up the screen to get destline on the screen. 331: */ 332: fgoto() 333: { 334: register int l, c; 335: 336: if (destcol > COLUMNS - 1) { 337: destline += destcol / COLUMNS; 338: destcol %= COLUMNS; 339: } 340: if (outcol > COLUMNS - 1) { 341: l = (outcol + 1) / COLUMNS; 342: outline += l; 343: outcol %= COLUMNS; 344: if (AM == 0) { 345: while (l > 0) { 346: if (pfast) 347: #ifdef CRNL 348: if (xCR) 349: tputs(xCR, 0, putch); 350: else 351: #endif 352: putch('\r'); 353: #ifdef CRNL 354: if (xNL) 355: tputs(xNL, 0, putch); 356: else 357: #endif 358: putch('\n'); 359: l--; 360: } 361: outcol = 0; 362: } 363: if (outline > LINES - 1) { 364: destline -= outline - (LINES - 1); 365: outline = LINES - 1; 366: } 367: } 368: if (destline > LINES - 1) { 369: l = destline; 370: destline = LINES - 1; 371: if (outline < LINES - 1) { 372: c = destcol; 373: if (pfast == 0 && (!CA || holdcm)) 374: destcol = 0; 375: fgoto(); 376: destcol = c; 377: } 378: while (l > LINES - 1) { 379: /* 380: * The following linefeed (or simulation thereof) 381: * is supposed to scroll up the screen, since we 382: * are on the bottom line. We make the assumption 383: * that linefeed will scroll. If ns is in the 384: * capability list this won't work. We should 385: * probably have an sc capability but sf will 386: * generally take the place if it works. 387: */ 388: #ifdef CRNL 389: if (xNL /* && !XB */ && pfast) 390: tputs(xNL, 0, putch); 391: else 392: #endif 393: putch('\n'); 394: l--; 395: if (pfast == 0) 396: outcol = 0; 397: } 398: } 399: if (destline < outline && !(CA && !holdcm || UP != NOSTR)) 400: destline = outline; 401: if (CA && !holdcm) 402: if (plod(costCM) > 0) 403: plod(0); 404: else 405: tputs(tgoto(CM, destcol, destline), 0, putch); 406: else 407: plod(0); 408: outline = destline; 409: outcol = destcol; 410: } 411: 412: /* 413: * Tab to column col by flushing and then setting destcol. 414: * Used by "set all". 415: */ 416: tab(col) 417: int col; 418: { 419: 420: flush1(); 421: destcol = col; 422: } 423: 424: /* 425: * Move (slowly) to destination. 426: * Hard thing here is using home cursor on really deficient terminals. 427: * Otherwise just use cursor motions, hacking use of tabs and overtabbing 428: * and backspace. 429: */ 430: 431: static int plodcnt, plodflg; 432: 433: plodput(c) 434: { 435: 436: if (plodflg) 437: plodcnt--; 438: else 439: putch(c); 440: } 441: 442: plod(cnt) 443: { 444: register int i, j, k; 445: register int soutcol, soutline; 446: 447: plodcnt = plodflg = cnt; 448: soutcol = outcol; 449: soutline = outline; 450: if (HO) { 451: if (GT) 452: i = (destcol / value(HARDTABS)) + (destcol % value(HARDTABS)); 453: else 454: i = destcol; 455: if (destcol >= outcol) { 456: j = destcol / value(HARDTABS) - outcol / value(HARDTABS); 457: if (GT && j) 458: j += destcol % value(HARDTABS); 459: else 460: j = destcol - outcol; 461: } else 462: if (outcol - destcol <= i && (BS || BC)) 463: i = j = outcol - destcol; 464: else 465: j = i + 1; 466: k = outline - destline; 467: if (k < 0) 468: k = -k; 469: j += k; 470: if (i + destline < j) { 471: tputs(HO, 0, plodput); 472: outcol = outline = 0; 473: } else if (LL) { 474: k = (LINES - 1) - destline; 475: if (i + k + 2 < j) { 476: tputs(LL, 0, plodput); 477: outcol = 0; 478: outline = LINES - 1; 479: } 480: } 481: } 482: if (GT) 483: i = destcol % value(HARDTABS) + destcol / value(HARDTABS); 484: else 485: i = destcol; 486: /* 487: if (BT && outcol > destcol && (j = (((outcol+7) & ~7) - destcol - 1) >> 3)) { 488: j *= (k = strlen(BT)); 489: if ((k += (destcol&7)) > 4) 490: j += 8 - (destcol&7); 491: else 492: j += k; 493: } else 494: */ 495: j = outcol - destcol; 496: /* 497: * If we will later need a \n which will turn into a \r\n by 498: * the system or the terminal, then don't bother to try to \r. 499: */ 500: if ((NONL || !pfast) && outline < destline) 501: goto dontcr; 502: /* 503: * If the terminal will do a \r\n and there isn't room for it, 504: * then we can't afford a \r. 505: */ 506: if (NC && outline >= destline) 507: goto dontcr; 508: /* 509: * If it will be cheaper, or if we can't back up, then send 510: * a return preliminarily. 511: */ 512: if (j > i + 1 || outcol > destcol && !BS && !BC) { 513: #ifdef CRNL 514: if (xCR) 515: tputs(xCR, 0, plodput); 516: else 517: #endif 518: plodput('\r'); 519: if (NC) { 520: #ifdef CRNL 521: if (xNL) 522: tputs(xNL, 0, plodput); 523: else 524: #endif 525: plodput('\n'); 526: outline++; 527: } 528: outcol = 0; 529: } 530: dontcr: 531: while (outline < destline) { 532: outline++; 533: #ifdef CRNL 534: if (xNL) 535: tputs(xNL, 0, plodput); 536: else 537: #endif 538: plodput('\n'); 539: if (plodcnt < 0) 540: goto out; 541: if (NONL || pfast == 0) 542: outcol = 0; 543: } 544: if (BT) 545: k = strlen(BT); 546: while (outcol > destcol) { 547: if (plodcnt < 0) 548: goto out; 549: /* 550: if (BT && !insmode && outcol - destcol > 4+k) { 551: tputs(BT, 0, plodput); 552: outcol--; 553: outcol &= ~7; 554: continue; 555: } 556: */ 557: outcol--; 558: if (BC) 559: tputs(BC, 0, plodput); 560: else 561: plodput('\b'); 562: } 563: while (outline > destline) { 564: outline--; 565: tputs(UP, 0, plodput); 566: if (plodcnt < 0) 567: goto out; 568: } 569: if (GT && !insmode && destcol - outcol > 1) { 570: for (;;) { 571: i = (outcol / value(HARDTABS) + 1) * value(HARDTABS); 572: if (i > destcol) 573: break; 574: if (TA) 575: tputs(TA, 0, plodput); 576: else 577: plodput('\t'); 578: outcol = i; 579: } 580: if (destcol - outcol > 4 && i < COLUMNS && (BC || BS)) { 581: if (TA) 582: tputs(TA, 0, plodput); 583: else 584: plodput('\t'); 585: outcol = i; 586: while (outcol > destcol) { 587: outcol--; 588: if (BC) 589: tputs(BC, 0, plodput); 590: else 591: plodput('\b'); 592: } 593: } 594: } 595: while (outcol < destcol) { 596: /* 597: * move one char to the right. We don't use ND space 598: * because it's better to just print the char we are 599: * moving over. There are various exceptions, however. 600: * If !inopen, vtube contains garbage. If the char is 601: * a null or a tab we want to print a space. Other random 602: * chars we use space for instead, too. 603: */ 604: if (!inopen || vtube[outline]==NULL || 605: (i=vtube[outline][outcol]) < ' ') 606: i = ' '; 607: if (insmode && ND) 608: tputs(ND, 0, plodput); 609: else 610: plodput(i); 611: outcol++; 612: if (plodcnt < 0) 613: goto out; 614: } 615: out: 616: if (plodflg) { 617: outcol = soutcol; 618: outline = soutline; 619: } 620: return(plodcnt); 621: } 622: 623: /* 624: * An input line arrived. 625: * Calculate new (approximate) screen line position. 626: * Approximate because kill character echoes newline with 627: * no feedback and also because of long input lines. 628: */ 629: noteinp() 630: { 631: 632: outline++; 633: if (outline > LINES - 1) 634: outline = LINES - 1; 635: destline = outline; 636: destcol = outcol = 0; 637: } 638: 639: /* 640: * Something weird just happened and we 641: * lost track of whats happening out there. 642: * Since we cant, in general, read where we are 643: * we just reset to some known state. 644: * On cursor addressible terminals setting to unknown 645: * will force a cursor address soon. 646: */ 647: termreset() 648: { 649: 650: endim(); 651: if (TI) /* otherwise it flushes anyway, and 'set tty=dumb' vomits */ 652: putpad(TI); /*adb change -- emit terminal initial sequence */ 653: destcol = 0; 654: destline = LINES - 1; 655: if (CA) { 656: outcol = UKCOL; 657: outline = UKCOL; 658: } else { 659: outcol = destcol; 660: outline = destline; 661: } 662: } 663: 664: /* 665: * Low level buffering, with the ability to drain 666: * buffered output without printing it. 667: */ 668: char *obp = obuf; 669: 670: draino() 671: { 672: 673: obp = obuf; 674: } 675: 676: flusho() 677: { 678: 679: if (obp != obuf) { 680: write(1, obuf, obp - obuf); 681: obp = obuf; 682: } 683: } 684: 685: putnl() 686: { 687: 688: putchar('\n'); 689: } 690: 691: putS(cp) 692: char *cp; 693: { 694: 695: if (cp == NULL) 696: return; 697: while (*cp) 698: putch(*cp++); 699: } 700: 701: 702: putch(c) 703: int c; 704: { 705: 706: *obp++ = c & 0177; 707: if (obp >= &obuf[sizeof obuf]) 708: flusho(); 709: } 710: 711: /* 712: * Miscellaneous routines related to output. 713: */ 714: 715: /* 716: * Put with padding 717: */ 718: putpad(cp) 719: char *cp; 720: { 721: 722: flush(); 723: tputs(cp, 0, putch); 724: } 725: 726: /* 727: * Set output through normal command mode routine. 728: */ 729: setoutt() 730: { 731: 732: Outchar = termchar; 733: } 734: 735: /* 736: * Printf (temporarily) in list mode. 737: */ 738: /*VARARGS2*/ 739: lprintf(cp, dp) 740: char *cp, *dp; 741: { 742: register int (*P)(); 743: 744: P = setlist(1); 745: printf(cp, dp); 746: Putchar = P; 747: } 748: 749: /* 750: * Newline + flush. 751: */ 752: putNFL() 753: { 754: 755: putnl(); 756: flush(); 757: } 758: 759: /* 760: * Try to start -nl mode. 761: */ 762: pstart() 763: { 764: 765: if (NONL) 766: return; 767: if (!value(OPTIMIZE)) 768: return; 769: if (ruptible == 0 || pfast) 770: return; 771: fgoto(); 772: flusho(); 773: pfast = 1; 774: normtty++; 775: #ifndef USG3TTY 776: tty.sg_flags = normf & ~(ECHO|XTABS|CRMOD); 777: #else 778: tty = normf; 779: tty.c_oflag &= ~(ONLCR|TAB3); 780: tty.c_lflag &= ~ECHO; 781: #endif 782: sTTY(1); 783: } 784: 785: /* 786: * Stop -nl mode. 787: */ 788: pstop() 789: { 790: 791: if (inopen) 792: return; 793: phadnl = 0; 794: linp = linb; 795: draino(); 796: normal(normf); 797: pfast &= ~1; 798: } 799: 800: /* 801: * Prep tty for open mode. 802: */ 803: ttymode 804: ostart() 805: { 806: ttymode f; 807: 808: if (!intty) 809: error("Open and visual must be used interactively"); 810: gTTY(1); 811: normtty++; 812: #ifndef USG3TTY 813: f = tty.sg_flags; 814: tty.sg_flags = (normf &~ (ECHO|XTABS|CRMOD)) | 815: # ifdef CBREAK 816: CBREAK; 817: # else 818: RAW; 819: # endif 820: # ifdef TIOCGETC 821: ttcharoff(); 822: # endif 823: #else 824: f = tty; 825: tty = normf; 826: tty.c_iflag &= ~ICRNL; 827: tty.c_lflag &= ~(ECHO|ICANON); 828: tty.c_oflag &= ~TAB3; 829: tty.c_cc[VMIN] = 1; 830: tty.c_cc[VTIME] = 1; 831: ttcharoff(); 832: #endif 833: sTTY(1); 834: putpad(VS); 835: pfast |= 2; 836: return (f); 837: } 838: 839: #ifdef TIOCGETC 840: /* 841: * Turn off start/stop chars if they aren't the default ^S/^Q. 842: * This is so idiots who make esc their start/stop don't lose. 843: * We always turn off quit since datamedias send ^\ for their 844: * right arrow key. 845: */ 846: ttcharoff() 847: { 848: nttyc.t_quitc = '\377'; 849: if (nttyc.t_startc != CTRL(q)) 850: nttyc.t_startc = '\377'; 851: if (nttyc.t_stopc != CTRL(s)) 852: nttyc.t_stopc = '\377'; 853: } 854: #endif 855: 856: #ifdef USG3TTY 857: ttcharoff() 858: { 859: tty.c_cc[VQUIT] = '\377'; 860: # ifdef VSTART 861: /* 862: * The following is sample code if USG ever lets people change 863: * their start/stop chars. As long as they can't we can't get 864: * into trouble so we just leave them alone. 865: */ 866: if (tty.c_cc[VSTART] != CTRL(q)) 867: tty.c_cc[VSTART] = '\377'; 868: if (tty.c_cc[VSTOP] != CTRL(s)) 869: tty.c_cc[VSTOP] = '\377'; 870: # endif 871: } 872: #endif 873: 874: /* 875: * Stop open, restoring tty modes. 876: */ 877: ostop(f) 878: ttymode f; 879: { 880: 881: #ifndef USG3TTY 882: pfast = (f & CRMOD) == 0; 883: #else 884: pfast = (f.c_oflag & OCRNL) == 0; 885: #endif 886: termreset(), fgoto(), flusho(); 887: normal(f); 888: putpad(VE); 889: } 890: 891: #ifndef CBREAK 892: /* 893: * Into cooked mode for interruptibility. 894: */ 895: vcook() 896: { 897: 898: tty.sg_flags &= ~RAW; 899: sTTY(1); 900: } 901: 902: /* 903: * Back into raw mode. 904: */ 905: vraw() 906: { 907: 908: tty.sg_flags |= RAW; 909: sTTY(1); 910: } 911: #endif 912: 913: /* 914: * Restore flags to normal state f. 915: */ 916: normal(f) 917: ttymode f; 918: { 919: 920: if (normtty > 0) { 921: setty(f); 922: normtty--; 923: } 924: } 925: 926: /* 927: * Straight set of flags to state f. 928: */ 929: ttymode 930: setty(f) 931: ttymode f; 932: { 933: #ifndef USG3TTY 934: register int ot = tty.sg_flags; 935: #else 936: ttymode ot; 937: ot = tty; 938: #endif 939: 940: #ifndef USG3TTY 941: # ifdef TIOCGETC 942: if (f == normf) 943: nttyc = ottyc; 944: else 945: ttcharoff(); 946: # endif 947: tty.sg_flags = f; 948: #else 949: if (tty.c_lflag & ICANON) 950: ttcharoff(); 951: tty = f; 952: #endif 953: sTTY(1); 954: return (ot); 955: } 956: 957: gTTY(i) 958: int i; 959: { 960: 961: #ifndef USG3TTY 962: ignore(gtty(i, &tty)); 963: # ifdef TIOCGETC 964: ioctl(i, TIOCGETC, &ottyc); 965: # endif 966: #else 967: ioctl(i, TCGETA, &tty); 968: #endif 969: #ifdef TIOCGETC 970: nttyc = ottyc; 971: #endif 972: } 973: 974: /* 975: * sTTY: set the tty modes on file descriptor i to be what's currently 976: * in global "tty". (Also use nttyc if needed.) 977: */ 978: sTTY(i) 979: int i; 980: { 981: 982: #ifndef USG3TTY 983: /* 984: * Bug in USG tty driver, put out a DEL as a patch. 985: */ 986: # ifdef USG 987: if (tty.sg_ospeed >= B1200) 988: write(1, "\377", 1); 989: # endif 990: # ifdef TIOCSETN 991: /* Don't flush typeahead if we don't have to. */ 992: ioctl(i, TIOCSETN, &tty); 993: # else 994: /* We have to. Too bad. */ 995: stty(i, &tty); 996: # endif 997: # ifdef TIOCGETC 998: /* 999: * Update the other random chars while we're at it. 1000: * Heaven help us if TIOCGETC is defined but TIOCSETC isn't! 1001: */ 1002: ioctl(i, TIOCSETC, &nttyc); 1003: # endif 1004: #else 1005: /* USG 3 very simple: just set everything */ 1006: ioctl(i, TCSETAW, &tty); 1007: #endif 1008: } 1009: 1010: /* 1011: * Print newline, or blank if in open/visual 1012: */ 1013: noonl() 1014: { 1015: 1016: putchar(Outchar != termchar ? ' ' : '\n'); 1017: }