summaryrefslogtreecommitdiff
path: root/ex_put.c
diff options
context:
space:
mode:
authorThomas Ulmer <thomasmulmer02@gmail.com>2026-02-23 16:54:28 -0800
committerThomas Ulmer <thomasmulmer02@gmail.com>2026-02-23 16:54:28 -0800
commit15bd7946cc838a3151c357e4b0bc1ab85eecda62 (patch)
tree56977cb9bfc4349f46e2c608503a298df30ca957 /ex_put.c
add musl and vi
Diffstat (limited to 'ex_put.c')
-rw-r--r--ex_put.c1337
1 files changed, 1337 insertions, 0 deletions
diff --git a/ex_put.c b/ex_put.c
new file mode 100644
index 0000000..0b77955
--- /dev/null
+++ b/ex_put.c
@@ -0,0 +1,1337 @@
+/*
+ * This code contains changes by
+ * Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * Redistributions of source code and documentation must retain the
+ * above copyright notice, this list of conditions and the following
+ * disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed or owned by Caldera
+ * International, Inc.
+ * Neither the name of Caldera International, Inc. nor the names of
+ * other contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_put.c 1.35 (gritter) 12/25/06";
+#endif
+#endif
+
+/* from ex_put.c 7.9.1 (2.11BSD GTE) 12/9/94 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Terminal driving and line formatting routines.
+ * Basic motion optimizations are done here as well
+ * as formatting of lines (printing of control characters,
+ * line numbering and the like).
+ */
+
+/*
+ * The routines outchar, putchar and pline are actually
+ * variables, and these variables point at the current definitions
+ * of the routines. See the routine setflav.
+ * We sometimes make outchar be routines which catch the characters
+ * to be printed, e.g. if we want to see how long a line is.
+ * During open/visual, outchar and putchar will be set to
+ * routines in the file ex_vput.c (vputchar, vinschar, etc.).
+ */
+int (*Outchar)(int) = termchar;
+int (*Putchar)(int) = normchar;
+int (*Pline)(int, int) = normline;
+
+int (*
+setlist(int t))(int)
+{
+ register int (*P)(int);
+
+ listf = t;
+ P = Putchar;
+ Putchar = t ? listchar : normchar;
+ return (P);
+}
+
+int (*
+setnumb(int t))(int, int)
+{
+ register int (*P)(int, int);
+
+ numberf = t;
+ P = Pline;
+ Pline = t ? numbline : normline;
+ return (P);
+}
+
+/*
+ * Format c for list mode; leave things in common
+ * with normal print mode to be done by normchar.
+ */
+int
+listchar(int c)
+{
+
+ if (c & MULTICOL) {
+ c &= ~MULTICOL;
+ if (c == 0)
+ return MULTICOL;
+ }
+ c &= (TRIM|QUOTE);
+ switch (c) {
+ case '\t':
+ case '\b':
+ c = ctlof(c);
+ outchar('^');
+ break;
+
+ case '\n':
+ break;
+
+ default:
+ if (c == ('\n' | QUOTE))
+ outchar('$');
+ if (c & QUOTE)
+ break;
+#ifndef BIT8
+ if (c < ' ' && c != '\n')
+ outchar('^'), c = ctlof(c);
+#else /* !BIT8 */
+ if (!printable(c) && c != '\n' || c == DELETE)
+ c = printof(c);
+#endif
+ break;
+ }
+ return normchar(c);
+}
+
+/*
+ * Format c for printing. Handle funnies of upper case terminals
+ * and crocky hazeltines which don't have ~.
+ */
+int
+normchar(register int c)
+{
+ int u;
+
+#ifdef UCVISUAL
+ register char *colp;
+
+ if (c == '~' && xHZ) {
+ normchar('\\');
+ c = '^';
+ }
+#endif
+
+ if (c & MULTICOL) {
+ c &= ~MULTICOL;
+ if (c == 0)
+ return MULTICOL;
+ }
+ c &= (TRIM|QUOTE);
+ u = c & TRIM;
+ if (c & QUOTE) {
+ if (c == (' ' | QUOTE) || c == ('\b' | QUOTE))
+ /*EMPTY*/;
+ else if (c == QUOTE)
+ return c;
+ else
+ c &= TRIM;
+ }
+#ifdef BIT8
+ else {
+ if (!printable(c) && (u != '\b' || !OS) &&
+ u != '\n' && u != '\t')
+ c = printof(u);
+ else {
+ c = u;
+ if (0)
+ /*EMPTY*/;
+#else /* !BIT8 */
+ else if (c < ' ' && (c != '\b' || !OS) && c != '\n' && c != '\t')
+ putchar('^'), c = ctlof(c);
+#endif /* !BIT8 */
+#ifdef UCVISUAL
+ else if (UPPERCASE)
+ if (xisupper(c)) {
+ outchar('\\');
+ c = tolower(c);
+ } else {
+ colp = "({)}!|^~'`";
+ while (*colp++)
+ if (c == *colp++) {
+ outchar('\\');
+ c = colp[-2];
+ break;
+ }
+ }
+#endif /* UCVISUAL */
+#ifdef BIT8
+ }
+ }
+#endif
+ outchar(c);
+ return c;
+}
+
+/*
+ * Given c at the beginning of a line, determine whether
+ * the printing of the line will erase or otherwise obliterate
+ * the prompt which was printed before. If it won't, do it now.
+ */
+void
+slobber(int c)
+{
+
+ shudclob = 0;
+ switch (c) {
+
+ case '\t':
+ if (Putchar == listchar)
+ return;
+ break;
+
+ default:
+ return;
+
+ case ' ':
+ case 0:
+ break;
+ }
+ if (OS)
+ return;
+ flush();
+ putch(' ');
+ if (BC)
+ tputs(BC, 0, putch);
+ else
+ putch('\b');
+}
+
+/*
+ * Print a line with a number.
+ */
+int
+numbline(int i, int max)
+{
+
+ if (shudclob)
+ slobber(' ');
+ max -= printf("%6d ", i);
+ return normline(0, max);
+}
+
+/*
+ * Normal line output, no numbering.
+ */
+int
+normline(int unused, int max)
+{
+ extern short vcntcol, lastsc;
+ short ovc = -1;
+ register char *cp;
+ int (*OO)(int);
+ int c, n;
+ int ret = 0;
+
+ if (max > 0)
+ vcntcol = 0;
+ if (shudclob)
+ slobber(linebuf[0]);
+ /* pdp-11 doprnt is not reentrant so can't use "printf" here
+ in case we are tracing */
+ cp = linebuf;
+ vcolbp = cp;
+ while (*cp && max) {
+ vcolbp = cp;
+ nextc(c, cp, n);
+ cp += n;
+ if (max > 0) {
+ if (Outchar != qcount) {
+ OO = Outchar;
+ Outchar = qcount;
+ putchar(c);
+ Outchar = OO;
+ } else
+ putchar(c);
+ if ((vcntcol-1) % WCOLS == 0 && lastsc > 1)
+ vcntcol++;
+ if (vcntcol >= max) {
+ putchar('@');
+ vcntcol = ovc + 1;
+ lastsc = 1;
+ ret = 1;
+ break;
+ }
+ ovc = vcntcol;
+ if (Outchar != qcount)
+ putchar(c);
+ } else
+ putchar(c);
+ }
+ if (!inopen) {
+ putchar('\n' | QUOTE);
+ }
+ return ret;
+}
+
+/*
+ * The output buffer is initialized with a useful error
+ * message so we don't have to keep it in data space.
+ */
+static char linb[66+MB_LEN_MAX];
+char *linp = linb;
+
+/*
+ * Phadnl records when we have already had a complete line ending with \n.
+ * If another line starts without a flush, and the terminal suggests it,
+ * we switch into -nl mode so that we can send lineffeeds to avoid
+ * a lot of spacing.
+ */
+static bool phadnl;
+
+/*
+ * Indirect to current definition of putchar.
+ */
+int
+putchar(int c)
+{
+ if (c & MULTICOL) {
+ c &= ~MULTICOL;
+ if (c == 0)
+ return MULTICOL;
+ }
+ (*Putchar)(c);
+ return c;
+}
+
+/*
+ * Termchar routine for command mode.
+ * Watch for possible switching to -nl mode.
+ * Otherwise flush into next level of buffering when
+ * small buffer fills or at a newline.
+ */
+int
+termchar(int c)
+{
+
+ if (pfast == 0 && phadnl)
+ pstart();
+ if (c == '\n')
+ phadnl = 1;
+ else if (linp >= &linb[63])
+ flush1();
+#ifdef MB
+ if (mb_cur_max > 1 && c & ~(wchar_t)0177) {
+ char mb[MB_LEN_MAX];
+ int i, n;
+ n = wctomb(mb, c&TRIM);
+ for (i = 0; i < n; i++)
+ *linp++ = mb[i];
+ } else
+#endif /* MB */
+ *linp++ = c;
+ if (linp >= &linb[63]) {
+ fgoto();
+ flush1();
+ }
+ return c;
+}
+
+void
+flush2(void)
+{
+
+ fgoto();
+ flusho();
+ pstop();
+}
+
+void
+flush(void)
+{
+
+ flush1();
+ flush2();
+}
+
+/*
+ * Flush from small line buffer into output buffer.
+ * Work here is destroying motion into positions, and then
+ * letting fgoto do the optimized motion.
+ */
+void
+flush1(void)
+{
+ register char *lp;
+ int c, n;
+
+ *linp = 0;
+ lp = linb;
+ while (*lp) {
+ nextc(c, lp, n);
+ lp += n;
+ switch (c) {
+
+ case '\r':
+ destline += destcol / TCOLUMNS;
+ destcol = 0;
+ continue;
+
+ case '\b':
+ if (destcol)
+ destcol--;
+ continue;
+
+ case ' ':
+ destcol++;
+ continue;
+
+ case '\t':
+ destcol += value(TABSTOP) - destcol % value(TABSTOP);
+ continue;
+
+ case '\n':
+ destline += destcol / TCOLUMNS + 1;
+ if (destcol != 0 && destcol % TCOLUMNS == 0)
+ destline--;
+ destcol = 0;
+ continue;
+
+ default:
+ fgoto();
+ for (;;) {
+ if (AM == 0 && outcol == TCOLUMNS)
+ fgoto();
+ c &= TRIM;
+ putch(c);
+ if (c == '\b') {
+ outcol--;
+ destcol--;
+#ifndef BIT8
+ } else if ( c >= ' ' && c != DELETE) {
+#else
+ } else if (printable(c)) {
+#endif
+#ifdef MB
+ n = colsc(c);
+ outcol += n;
+ destcol += n;
+#else /* !MB */
+ outcol++;
+ destcol++;
+#endif /* !MB */
+ if (XN && outcol % TCOLUMNS == 0)
+ putch('\r'), putch('\n');
+ }
+ nextc(c, lp, n);
+ lp += n;
+#ifndef BIT8
+ if (c <= ' ')
+#else
+ if (c == ' ' || !printable(c))
+#endif
+ break;
+ }
+ --lp;
+ continue;
+ }
+ }
+ linp = linb;
+}
+
+static int plodcnt, plodflg;
+
+/*
+ * Move (slowly) to destination.
+ * Hard thing here is using home cursor on really deficient terminals.
+ * Otherwise just use cursor motions, hacking use of tabs and overtabbing
+ * and backspace.
+ */
+
+int
+plodput(int c)
+{
+
+ if (plodflg)
+ plodcnt--;
+ else
+ putch(c);
+ return c;
+}
+
+int
+plod(int cnt)
+{
+ register int i, j, k = 0;
+ register int soutcol, soutline;
+
+ plodcnt = plodflg = cnt;
+ soutcol = outcol;
+ soutline = outline;
+ /*
+ * Consider homing and moving down/right from there, vs moving
+ * directly with local motions to the right spot.
+ */
+ if (HO) {
+ /*
+ * i is the cost to home and tab/space to the right to
+ * get to the proper column. This assumes ND space costs
+ * 1 char. So i+destcol is cost of motion with home.
+ */
+ if (GT)
+ i = (destcol / value(HARDTABS)) + (destcol % value(HARDTABS));
+ else
+ i = destcol;
+ /*
+ * j is cost to move locally without homing
+ */
+ if (destcol >= outcol) { /* if motion is to the right */
+ j = destcol / value(HARDTABS) - outcol / value(HARDTABS);
+ if (GT && j)
+ j += destcol % value(HARDTABS);
+ else
+ j = destcol - outcol;
+ } else
+ /* leftward motion only works if we can backspace. */
+ if (outcol - destcol <= i && (BS || BC))
+ i = j = outcol - destcol; /* cheaper to backspace */
+ else
+ j = i + 1; /* impossibly expensive */
+
+ /* k is the absolute value of vertical distance */
+ k = outline - destline;
+ if (k < 0)
+ k = -k;
+ j += k;
+
+ /*
+ * Decision. We may not have a choice if no UP.
+ */
+ if (i + destline < j || (!UP && destline < outline)) {
+ /*
+ * Cheaper to home. Do it now and pretend it's a
+ * regular local motion.
+ */
+ tputs(HO, 0, plodput);
+ outcol = outline = 0;
+ } else if (LL) {
+ /*
+ * Quickly consider homing down and moving from there.
+ * Assume cost of LL is 2.
+ */
+ k = (TLINES - 1) - destline;
+ if (i + k + 2 < j && (k<=0 || UP)) {
+ tputs(LL, 0, plodput);
+ outcol = 0;
+ outline = TLINES - 1;
+ }
+ }
+ } else
+ /*
+ * No home and no up means it's impossible, so we return an
+ * incredibly big number to make cursor motion win out.
+ */
+ if (!UP && destline < outline)
+ return (500);
+ if (GT)
+ i = destcol % value(HARDTABS)
+ + destcol / value(HARDTABS);
+ else
+ i = destcol;
+/*
+ if (BT && outcol > destcol && (j = (((outcol+7) & ~7) - destcol - 1) >> 3)) {
+ j *= (k = strlen(BT));
+ if ((k += (destcol&7)) > 4)
+ j += 8 - (destcol&7);
+ else
+ j += k;
+ } else
+*/
+ j = outcol - destcol;
+ /*
+ * If we will later need a \n which will turn into a \r\n by
+ * the system or the terminal, then don't bother to try to \r.
+ */
+ if ((NONL || !pfast) && outline < destline)
+ goto dontcr;
+ /*
+ * If the terminal will do a \r\n and there isn't room for it,
+ * then we can't afford a \r.
+ */
+ if (NC && outline >= destline)
+ goto dontcr;
+ /*
+ * If it will be cheaper, or if we can't back up, then send
+ * a return preliminarily.
+ */
+ if (j > i + 1 || outcol > destcol && !BS && !BC) {
+ /*
+ * BUG: this doesn't take the (possibly long) length
+ * of xCR into account.
+ */
+ if (ospeed != B0) {
+ if (xCR)
+ tputs(xCR, 0, plodput);
+ else
+ plodput('\r');
+ }
+ if (NC) {
+ if (xNL)
+ tputs(xNL, 0, plodput);
+ else
+ plodput('\n');
+ outline++;
+ }
+ outcol = 0;
+ }
+dontcr:
+ /* Move down, if necessary, until we are at the desired line */
+ while (outline < destline) {
+ j = destline - outline;
+ if (j > costDP && DOWN_PARM) {
+ /* Win big on Tek 4025 */
+ tputs(tgoto(DOWN_PARM, 0, j), j, plodput);
+ outline += j;
+ }
+ else {
+ outline++;
+ if (xNL && pfast)
+ tputs(xNL, 0, plodput);
+ else
+ plodput('\n');
+ }
+ if (plodcnt < 0)
+ goto out;
+ if (NONL || pfast == 0)
+ outcol = 0;
+ }
+ if (BT)
+ k = strlen(BT); /* should probably be cost(BT) and moved out */
+ /* Move left, if necessary, to desired column */
+ while (outcol > destcol) {
+ if (plodcnt < 0)
+ goto out;
+ if (BT && !insmode && outcol - destcol > 4+k) {
+ tputs(BT, 0, plodput);
+ outcol--;
+ outcol -= outcol % value(HARDTABS); /* outcol &= ~7; */
+ continue;
+ }
+ j = outcol - destcol;
+ if (j > costLP && LEFT_PARM) {
+ tputs(tgoto(LEFT_PARM, 0, j), j, plodput);
+ outcol -= j;
+ }
+ else {
+ outcol--;
+ if (BC)
+ tputs(BC, 0, plodput);
+ else
+ plodput('\b');
+ }
+ }
+ /* Move up, if necessary, to desired row */
+ while (outline > destline) {
+ j = outline - destline;
+ if (UP_PARM && j > 1) {
+ /* Win big on Tek 4025 */
+ tputs(tgoto(UP_PARM, 0, j), j, plodput);
+ outline -= j;
+ }
+ else {
+ outline--;
+ tputs(UP, 0, plodput);
+ }
+ if (plodcnt < 0)
+ goto out;
+ }
+ /*
+ * Now move to the right, if necessary. We first tab to
+ * as close as we can get.
+ */
+ if (GT && !insmode && destcol - outcol > 1) {
+ /* tab to right as far as possible without passing col */
+ for (;;) {
+ i = tabcol(outcol, value(HARDTABS));
+ if (i > destcol)
+ break;
+ if (TA)
+ tputs(TA, 0, plodput);
+ else
+ plodput('\t');
+ outcol = i;
+ }
+ /* consider another tab and then some backspaces */
+ if (destcol - outcol > 4 && i < TCOLUMNS && (BC || BS)) {
+ if (TA)
+ tputs(TA, 0, plodput);
+ else
+ plodput('\t');
+ outcol = i;
+ /*
+ * Back up. Don't worry about LEFT_PARM because
+ * it's never more than 4 spaces anyway.
+ */
+ while (outcol > destcol) {
+ outcol--;
+ if (BC)
+ tputs(BC, 0, plodput);
+ else
+ plodput('\b');
+ }
+ }
+ }
+ /*
+ * We've tabbed as much as possible. If we still need to go
+ * further (not exact or can't tab) space over. This is a
+ * very common case when moving to the right with space.
+ */
+ while (outcol < destcol) {
+ j = destcol - outcol;
+ if (j > costRP && RIGHT_PARM) {
+ /*
+ * This probably happens rarely, if at all.
+ * It seems mainly useful for ANSI terminals
+ * with no hardware tabs, and I don't know
+ * of any such terminal at the moment.
+ */
+ tputs(tgoto(RIGHT_PARM, 0, j), j, plodput);
+ outcol += j;
+ }
+ else {
+ /*
+ * move one char to the right. We don't use ND space
+ * because it's better to just print the char we are
+ * moving over. There are various exceptions, however.
+ * If !inopen, vtube contains garbage. If the char is
+ * a null or a tab we want to print a space. Other
+ * random chars we use space for instead, too.
+ */
+ if (!inopen || vtube[outline]==NULL ||
+#ifndef BIT8
+ ((i=vtube[outline][outcol]) < ' ')
+#else
+ ((i=vtube[outline][outcol]) == 0)
+ || (i!=MULTICOL && !printable(i&~INVBIT&~MULTICOL))
+#endif
+ )
+ i = ' ';
+ if((i & (QUOTE|INVBIT)) == QUOTE) /* mjm: no sign
+ extension on 3B */
+ i = ' ';
+ if ((insmode || i == MULTICOL) && ND)
+ tputs(ND, 0, plodput);
+ else if (i == MULTICOL) {
+ if (BS && BC)
+ tputs(BC, 0, plodput);
+ else
+ plodput('\b');
+ plodput(vtube[outline][outcol-1]);
+ } else
+ plodput(i);
+ outcol += i == MULTICOL ? 1 : colsc(i & ~MULTICOL);
+ }
+ if (plodcnt < 0)
+ goto out;
+ }
+out:
+ if (plodflg) {
+ outcol = soutcol;
+ outline = soutline;
+ }
+ return(plodcnt);
+}
+
+/*
+ * Sync the position of the output cursor.
+ * Most work here is rounding for terminal boundaries getting the
+ * column position implied by wraparound or the lack thereof and
+ * rolling up the screen to get destline on the screen.
+ */
+void
+fgoto(void)
+{
+ register int l, c;
+
+ if (destcol > TCOLUMNS - 1) {
+ destline += destcol / TCOLUMNS;
+ destcol %= TCOLUMNS;
+ }
+ if (outcol > TCOLUMNS - 1) {
+ l = (outcol + 1) / TCOLUMNS;
+ outline += l;
+ outcol %= TCOLUMNS;
+ if (AM == 0) {
+ while (l > 0) {
+ if (pfast && ospeed != B0)
+ if (xCR)
+ tputs(xCR, 0, putch);
+ else
+ putch('\r');
+ if (xNL)
+ tputs(xNL, 0, putch);
+ else
+ putch('\n');
+ l--;
+ }
+ outcol = 0;
+ }
+ if (outline > TLINES - 1) {
+ destline -= outline - (TLINES - 1);
+ outline = TLINES - 1;
+ }
+ }
+ if (destline > TLINES - 1) {
+ l = destline;
+ destline = TLINES - 1;
+ if (outline < TLINES - 1) {
+ c = destcol;
+ if (pfast == 0 && (!CA || holdcm))
+ destcol = 0;
+ fgoto();
+ destcol = c;
+ }
+ while (l > TLINES - 1) {
+ /*
+ * The following linefeed (or simulation thereof)
+ * is supposed to scroll up the screen, since we
+ * are on the bottom line. We make the assumption
+ * that linefeed will scroll. If ns is in the
+ * capability list this won't work. We should
+ * probably have an sc capability but sf will
+ * generally take the place if it works.
+ *
+ * Superbee glitch: in the middle of the screen we
+ * have to use esc B (down) because linefeed screws up
+ * in "Efficient Paging" (what a joke) mode (which is
+ * essential in some SB's because CRLF mode puts garbage
+ * in at end of memory), but you must use linefeed to
+ * scroll since down arrow won't go past memory end.
+ * I turned this off after recieving Paul Eggert's
+ * Superbee description which wins better.
+ */
+ if (xNL /* && !XB */ && pfast)
+ tputs(xNL, 0, putch);
+ else
+ putch('\n');
+ l--;
+ if (pfast == 0)
+ outcol = 0;
+ }
+ }
+ if (destline < outline && !(CA && !holdcm || UP != NOSTR))
+ destline = outline;
+ if (CA && !holdcm)
+ if (plod(costCM) > 0)
+ plod(0);
+ else
+ tputs(tgoto(CM, destcol, destline), 0, putch);
+ else
+ plod(0);
+ outline = destline;
+ outcol = destcol;
+}
+
+/*
+ * Tab to column col by flushing and then setting destcol.
+ * Used by "set all".
+ */
+void
+tab(int col)
+{
+
+ flush1();
+ destcol = col;
+}
+
+/*
+ * An input line arrived.
+ * Calculate new (approximate) screen line position.
+ * Approximate because kill character echoes newline with
+ * no feedback and also because of long input lines.
+ */
+void
+noteinp(void)
+{
+
+ outline++;
+ if (outline > TLINES - 1)
+ outline = TLINES - 1;
+ destline = outline;
+ destcol = outcol = 0;
+}
+
+/*
+ * Something weird just happened and we
+ * lost track of whats happening out there.
+ * Since we cant, in general, read where we are
+ * we just reset to some known state.
+ * On cursor addressible terminals setting to unknown
+ * will force a cursor address soon.
+ */
+void
+termreset(void)
+{
+
+ endim();
+ if (TI) /* otherwise it flushes anyway, and 'set tty=dumb' vomits */
+ putpad(TI); /*adb change -- emit terminal initial sequence */
+ destcol = 0;
+ destline = TLINES - 1;
+ if (CA) {
+ outcol = UKCOL;
+ outline = UKCOL;
+ } else {
+ outcol = destcol;
+ outline = destline;
+ }
+}
+
+/*
+ * Low level buffering, with the ability to drain
+ * buffered output without printing it.
+ */
+char *obp = obuf;
+
+void
+draino(void)
+{
+
+ obp = obuf;
+}
+
+void
+flusho(void)
+{
+
+ if (obp != obuf) {
+ write(1, obuf, obp - obuf);
+ obp = obuf;
+ }
+}
+
+void
+putnl(void)
+{
+
+ putchar('\n');
+}
+
+void
+putS(char *cp)
+{
+
+ if (cp == NULL)
+ return;
+ while (*cp)
+ putch(*cp++);
+}
+
+
+int
+putch(int c)
+{
+
+#ifdef OLD3BTTY /* mjm */
+ if(c == '\n') /* mjm: Fake "\n\r" for '\n' til fix in 3B firmware */
+ putch('\r'); /* mjm: vi does "stty -icanon" => -onlcr !! */
+#endif
+ if (c & MULTICOL) {
+ c &= ~MULTICOL;
+ if (c == 0)
+ return MULTICOL;
+ }
+ c &= ~INVBIT; /* strip '~' | INVBIT multicolumn filler */
+#ifdef MB
+ if (mb_cur_max > 1 && c & ~(wchar_t)0177) {
+ char mb[MB_LEN_MAX];
+ int i, n;
+ n = wctomb(mb, c&TRIM);
+ for (i = 0; i < n; i++) {
+ *obp++ = mb[i];
+ if (obp >= &obuf[sizeof obuf])
+ flusho();
+ }
+ } else
+#endif /* MB */
+ *obp++ = c & TRIM;
+ if (obp >= &obuf[sizeof obuf])
+ flusho();
+ return c;
+}
+
+/*
+ * Miscellaneous routines related to output.
+ */
+
+/*
+ * Put with padding
+ */
+void
+putpad(char *cp)
+{
+
+ flush();
+ tputs(cp, 0, putch);
+}
+
+/*
+ * Set output through normal command mode routine.
+ */
+void
+setoutt(void)
+{
+
+ Outchar = termchar;
+}
+
+/*
+ * Printf (temporarily) in list mode.
+ */
+/*VARARGS2*/
+void
+vlprintf(char *cp, va_list ap)
+{
+ register int (*P)(int);
+
+ P = setlist(1);
+ vprintf(cp, ap);
+ Putchar = P;
+}
+
+void
+lprintf(char *cp, ...)
+{
+ va_list ap;
+
+ va_start(ap, cp);
+ vlprintf(cp, ap);
+ va_end(ap);
+}
+
+/*
+ * Newline + flush.
+ */
+void
+putNFL(void)
+{
+
+ putnl();
+ flush();
+}
+
+/*
+ * sTTY: set the tty modes on file descriptor i to be what's
+ * currently in global "tty". (Also use nttyc if needed.)
+ */
+void
+sTTY(int i)
+{
+
+ tcsetattr(i, TCSADRAIN, &tty);
+}
+
+/*
+ * Try to start -nl mode.
+ */
+void
+pstart(void)
+{
+
+ if (NONL)
+ return;
+ if (!value(OPTIMIZE))
+ return;
+ if (ruptible == 0 || pfast)
+ return;
+ fgoto();
+ flusho();
+ pfast = 1;
+ normtty++;
+ tty = normf;
+ tty.c_oflag &= ~(ONLCR
+#if defined (TAB3)
+ | TAB3
+#elif defined (XTABS)
+ | XTABS
+#endif
+ );
+ tty.c_lflag &= ~ECHO;
+ sTTY(1);
+}
+
+/*
+ * Stop -nl mode.
+ */
+void
+pstop(void)
+{
+
+ if (inopen)
+ return;
+ phadnl = 0;
+ linp = linb;
+ draino();
+ normal(normf);
+ pfast &= ~1;
+}
+
+/*
+ * Turn off start/stop chars if they aren't the default ^S/^Q.
+ * This is so idiots who make esc their start/stop don't lose.
+ * We always turn off quit since datamedias send ^\ for their
+ * right arrow key.
+ */
+void
+ttcharoff(void)
+{
+#ifdef _PC_VDISABLE
+ long vdis;
+
+ errno = 0;
+#ifndef __dietlibc__
+ vdis = fpathconf(1, _PC_VDISABLE);
+ if (errno)
+ /*
+ * Use the old value of 0377, hope it is not
+ * the user's favourite character.
+ */
+#endif /* !__dietlibc__ */
+ vdis = '\377';
+#else /* !_PC_VDISABLE */
+#define vdis '\377';
+#endif /* !_PC_VDISABLE */
+ tty.c_cc[VQUIT] = vdis;
+#ifdef VSUSP
+ tty.c_cc[VSUSP] = vdis;
+#endif
+#ifdef VDSUSP
+ tty.c_cc[VDSUSP] = vdis;
+#endif
+#ifdef VREPRINT
+ tty.c_cc[VREPRINT] = vdis;
+#endif
+#ifdef VDISCRD
+ tty.c_cc[VDISCRD] = vdis;
+#endif
+#ifdef VWERASE
+ tty.c_cc[VWERASE] = vdis;
+#endif
+#ifdef VLNEXT
+ tty.c_cc[VLNEXT] = vdis;
+#endif
+#ifdef VSTATUS
+ tty.c_cc[VSTATUS] = vdis;
+#endif
+# ifdef VSTART
+ /*
+ * The following is sample code if USG ever lets people change
+ * their start/stop chars. As long as they can't we can't get
+ * into trouble so we just leave them alone.
+ */
+ if (tty.c_cc[VSTART] != CTRL('q'))
+ tty.c_cc[VSTART] = vdis;
+ if (tty.c_cc[VSTOP] != CTRL('s'))
+ tty.c_cc[VSTOP] = vdis;
+# endif
+}
+
+/*
+ * Prep tty for open mode.
+ */
+struct termios
+ostart(void)
+{
+ struct termios f;
+
+ if (!intty)
+ error(catgets(catd, 1, 120,
+ "Open and visual must be used interactively"));
+ gTTY(1);
+ normtty++;
+ f = tty;
+ tty = normf;
+ tty.c_iflag &= ~ICRNL;
+ tty.c_lflag &= ~(ECHO|ICANON);
+ tty.c_oflag &= ~(ONLCR
+#if defined (TAB3)
+ | TAB3
+#elif defined (XTABS)
+ | XTABS
+#endif
+ );
+ tty.c_cc[VMIN] = 1;
+ tty.c_cc[VTIME] = 1;
+ ttcharoff();
+ sTTY(1);
+ tostart();
+ pfast |= 2;
+ return (f);
+}
+
+/* actions associated with putting the terminal in open mode */
+void
+tostart(void)
+{
+ putpad(VS);
+ putpad(KS);
+ if (!value(MESG)) {
+ if (ttynbuf[0] == 0) {
+ register char *tn;
+ if ((tn=ttyname(2)) == NULL &&
+ (tn=ttyname(1)) == NULL &&
+ (tn=ttyname(0)) == NULL)
+ ttynbuf[0] = 1;
+ else
+ safecp(ttynbuf, tn, sizeof ttynbuf,
+ "%s too long", tn);
+ }
+ if (ttynbuf[0] != 1) {
+ struct stat sbuf;
+ stat(ttynbuf, &sbuf);
+ ttymesg = sbuf.st_mode & 0777;
+ chmod(ttynbuf,
+#ifdef UCBV7
+ /*
+ * This applies to the UCB V7 Pdp-11 system with the
+ * -u write option only.
+ */
+ 0611 /* 11 = urgent only allowed */
+#else
+ 0600
+#endif
+ );
+ }
+ }
+}
+
+/*
+ * Stop open, restoring tty modes.
+ */
+void
+ostop(struct termios f)
+{
+
+ pfast = (f.c_oflag & ONLCR) == 0;
+ termreset(), fgoto(), flusho();
+ normal(f);
+ tostop();
+}
+
+/* Actions associated with putting the terminal in the right mode. */
+void
+tostop(void)
+{
+ putpad(VE);
+ putpad(KE);
+ if (!value(MESG) && ttynbuf[0]>1)
+ chmod(ttynbuf, ttymesg);
+}
+
+/*
+ * Restore flags to normal state f.
+ */
+void
+normal(struct termios f)
+{
+
+ if (normtty > 0) {
+ setty(f);
+ normtty--;
+ }
+}
+
+/*
+ * Straight set of flags to state f.
+ */
+struct termios
+setty(struct termios f)
+{
+ struct termios ot;
+ ot = tty;
+
+ if (tty.c_lflag & ICANON)
+ ttcharoff();
+ tty = f;
+ sTTY(1);
+ return (ot);
+}
+
+void
+gTTY(int i)
+{
+
+ tcgetattr(i, &tty);
+}
+
+/*
+ * Print newline, or blank if in open/visual
+ */
+void
+noonl(void)
+{
+
+ putchar(Outchar != termchar ? ' ' : '\n');
+}