"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "less-424/line.c" of archive less-424.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /*
    2  * Copyright (C) 1984-2008  Mark Nudelman
    3  *
    4  * You may distribute under the terms of either the GNU General Public
    5  * License or the Less License, as specified in the README file.
    6  *
    7  * For more information about less, or for information on how to
    8  * contact the author, see the README file.
    9  */
   10 
   11 
   12 /*
   13  * Routines to manipulate the "line buffer".
   14  * The line buffer holds a line of output as it is being built
   15  * in preparation for output to the screen.
   16  */
   17 
   18 #include "less.h"
   19 #include "charset.h"
   20 
   21 static char *linebuf = NULL;	/* Buffer which holds the current output line */
   22 static char *attr = NULL;	/* Extension of linebuf to hold attributes */
   23 public int size_linebuf = 0;	/* Size of line buffer (and attr buffer) */
   24 
   25 static int cshift;		/* Current left-shift of output line buffer */
   26 public int hshift;		/* Desired left-shift of output line buffer */
   27 public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */
   28 public int ntabstops = 1;	/* Number of tabstops */
   29 public int tabdefault = 8;	/* Default repeated tabstops */
   30 
   31 static int curr;		/* Index into linebuf */
   32 static int column;		/* Printable length, accounting for
   33 				   backspaces, etc. */
   34 static int overstrike;		/* Next char should overstrike previous char */
   35 static int last_overstrike = AT_NORMAL;
   36 static int is_null_line;	/* There is no current line */
   37 static int lmargin;		/* Left margin */
   38 static char pendc;
   39 static POSITION pendpos;
   40 static char *end_ansi_chars;
   41 static char *mid_ansi_chars;
   42 
   43 static int attr_swidth();
   44 static int attr_ewidth();
   45 static int do_append();
   46 
   47 extern int sigs;
   48 extern int bs_mode;
   49 extern int linenums;
   50 extern int ctldisp;
   51 extern int twiddle;
   52 extern int binattr;
   53 extern int status_col;
   54 extern int auto_wrap, ignaw;
   55 extern int bo_s_width, bo_e_width;
   56 extern int ul_s_width, ul_e_width;
   57 extern int bl_s_width, bl_e_width;
   58 extern int so_s_width, so_e_width;
   59 extern int sc_width, sc_height;
   60 extern int utf_mode;
   61 extern int oldbot;
   62 extern POSITION start_attnpos;
   63 extern POSITION end_attnpos;
   64 
   65 static char mbc_buf[MAX_UTF_CHAR_LEN];
   66 static int mbc_buf_len = 0;
   67 static int mbc_buf_index = 0;
   68 static POSITION mbc_pos;
   69 
   70 /*
   71  * Initialize from environment variables.
   72  */
   73 	public void
   74 init_line()
   75 {
   76 	end_ansi_chars = lgetenv("LESSANSIENDCHARS");
   77 	if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
   78 		end_ansi_chars = "m";
   79 
   80 	mid_ansi_chars = lgetenv("LESSANSIMIDCHARS");
   81 	if (mid_ansi_chars == NULL || *mid_ansi_chars == '\0')
   82 		mid_ansi_chars = "0123456789;[?!\"'#%()*+ ";
   83 
   84 	linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
   85 	attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
   86 	size_linebuf = LINEBUF_SIZE;
   87 }
   88 
   89 /*
   90  * Expand the line buffer.
   91  */
   92 	static int
   93 expand_linebuf()
   94 {
   95 	/* Double the size of the line buffer. */
   96 	int new_size = size_linebuf * 2;
   97 
   98 	/* Just realloc to expand the buffer, if we can. */
   99 #if HAVE_REALLOC
  100 	char *new_buf = (char *) realloc(linebuf, new_size);
  101 	char *new_attr = (char *) realloc(attr, new_size);
  102 #else
  103 	char *new_buf = (char *) calloc(new_size, sizeof(char));
  104 	char *new_attr = (char *) calloc(new_size, sizeof(char));
  105 #endif
  106 	if (new_buf == NULL || new_attr == NULL)
  107 	{
  108 		if (new_attr != NULL)
  109 			free(new_attr);
  110 		if (new_buf != NULL)
  111 			free(new_buf);
  112 		return 1;
  113 	}
  114 #if HAVE_REALLOC
  115 	/*
  116 	 * We realloc'd the buffers; they already have the old contents.
  117 	 */
  118 	#if 0
  119 	memset(new_buf + size_linebuf, 0, new_size - size_linebuf);
  120 	memset(new_attr + size_linebuf, 0, new_size - size_linebuf);
  121 	#endif
  122 #else
  123 	/*
  124 	 * We just calloc'd the buffers; copy the old contents.
  125 	 */
  126 	memcpy(new_buf, linebuf, size_linebuf * sizeof(char));
  127 	memcpy(new_attr, attr, size_linebuf * sizeof(char));
  128 	free(attr);
  129 	free(linebuf);
  130 #endif
  131 	linebuf = new_buf;
  132 	attr = new_attr;
  133 	size_linebuf = new_size;
  134 	return 0;
  135 }
  136 
  137 /*
  138  * Is a character ASCII?
  139  */
  140 	public int
  141 is_ascii_char(ch)
  142 	LWCHAR ch;
  143 {
  144 	return (ch <= 0x7F);
  145 }
  146 
  147 /*
  148  * Rewind the line buffer.
  149  */
  150 	public void
  151 prewind()
  152 {
  153 	curr = 0;
  154 	column = 0;
  155 	cshift = 0;
  156 	overstrike = 0;
  157 	last_overstrike = AT_NORMAL;
  158 	mbc_buf_len = 0;
  159 	is_null_line = 0;
  160 	pendc = '\0';
  161 	lmargin = 0;
  162 	if (status_col)
  163 		lmargin += 1;
  164 }
  165 
  166 /*
  167  * Insert the line number (of the given position) into the line buffer.
  168  */
  169 	public void
  170 plinenum(pos)
  171 	POSITION pos;
  172 {
  173 	register LINENUM linenum = 0;
  174 	register int i;
  175 
  176 	if (linenums == OPT_ONPLUS)
  177 	{
  178 		/*
  179 		 * Get the line number and put it in the current line.
  180 		 * {{ Note: since find_linenum calls forw_raw_line,
  181 		 *    it may seek in the input file, requiring the caller
  182 		 *    of plinenum to re-seek if necessary. }}
  183 		 * {{ Since forw_raw_line modifies linebuf, we must
  184 		 *    do this first, before storing anything in linebuf. }}
  185 		 */
  186 		linenum = find_linenum(pos);
  187 	}
  188 
  189 	/*
  190 	 * Display a status column if the -J option is set.
  191 	 */
  192 	if (status_col)
  193 	{
  194 		linebuf[curr] = ' ';
  195 		if (start_attnpos != NULL_POSITION &&
  196 		    pos >= start_attnpos && pos < end_attnpos)
  197 			attr[curr] = AT_NORMAL|AT_HILITE;
  198 		else
  199 			attr[curr] = AT_NORMAL;
  200 		curr++;
  201 		column++;
  202 	}
  203 	/*
  204 	 * Display the line number at the start of each line
  205 	 * if the -N option is set.
  206 	 */
  207 	if (linenums == OPT_ONPLUS)
  208 	{
  209 		char buf[INT_STRLEN_BOUND(pos) + 2];
  210 		int n;
  211 
  212 		linenumtoa(linenum, buf);
  213 		n = strlen(buf);
  214 		if (n < MIN_LINENUM_WIDTH)
  215 			n = MIN_LINENUM_WIDTH;
  216 		sprintf(linebuf+curr, "%*s ", n, buf);
  217 		n++;  /* One space after the line number. */
  218 		for (i = 0; i < n; i++)
  219 			attr[curr+i] = AT_NORMAL;
  220 		curr += n;
  221 		column += n;
  222 		lmargin += n;
  223 	}
  224 
  225 	/*
  226 	 * Append enough spaces to bring us to the lmargin.
  227 	 */
  228 	while (column < lmargin)
  229 	{
  230 		linebuf[curr] = ' ';
  231 		attr[curr++] = AT_NORMAL;
  232 		column++;
  233 	}
  234 }
  235 
  236 /*
  237  * Shift the input line left.
  238  * This means discarding N printable chars at the start of the buffer.
  239  */
  240 	static void
  241 pshift(shift)
  242 	int shift;
  243 {
  244 	LWCHAR prev_ch = 0;
  245 	unsigned char c;
  246 	int shifted = 0;
  247 	int to;
  248 	int from;
  249 	int len;
  250 	int width;
  251 	int prev_attr;
  252 	int next_attr;
  253 
  254 	if (shift > column - lmargin)
  255 		shift = column - lmargin;
  256 	if (shift > curr - lmargin)
  257 		shift = curr - lmargin;
  258 
  259 	to = from = lmargin;
  260 	/*
  261 	 * We keep on going when shifted == shift
  262 	 * to get all combining chars.
  263 	 */
  264 	while (shifted <= shift && from < curr)
  265 	{
  266 		c = linebuf[from];
  267 		if (ctldisp == OPT_ONPLUS && IS_CSI_START(c))
  268 		{
  269 			/* Keep cumulative effect.  */
  270 			linebuf[to] = c;
  271 			attr[to++] = attr[from++];
  272 			while (from < curr && linebuf[from])
  273 			{
  274 				linebuf[to] = linebuf[from];
  275 				attr[to++] = attr[from];
  276 				if (!is_ansi_middle(linebuf[from++]))
  277 					break;
  278 			}
  279 			continue;
  280 		}
  281 
  282 		width = 0;
  283 
  284 		if (!IS_ASCII_OCTET(c) && utf_mode)
  285 		{
  286 			/* Assumes well-formedness validation already done.  */
  287 			LWCHAR ch;
  288 
  289 			len = utf_len(c);
  290 			if (from + len > curr)
  291 				break;
  292 			ch = get_wchar(linebuf + from);
  293 			if (!is_composing_char(ch) && !is_combining_char(prev_ch, ch))
  294 				width = is_wide_char(ch) ? 2 : 1;
  295 			prev_ch = ch;
  296 		} else
  297 		{
  298 			len = 1;
  299 			if (c == '\b')
  300 				/* XXX - Incorrect if several '\b' in a row.  */
  301 				width = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
  302 			else if (!control_char(c))
  303 				width = 1;
  304 			prev_ch = 0;
  305 		}
  306 
  307 		if (width == 2 && shift - shifted == 1) {
  308 			/* Should never happen when called by pshift_all().  */
  309 			attr[to] = attr[from];
  310 			/*
  311 			 * Assume a wide_char will never be the first half of a
  312 			 * combining_char pair, so reset prev_ch in case we're
  313 			 * followed by a '\b'.
  314 			 */
  315 			prev_ch = linebuf[to++] = ' ';
  316 			from += len;
  317 			shifted++;
  318 			continue;
  319 		}
  320 
  321 		/* Adjust width for magic cookies. */
  322 		prev_attr = (to > 0) ? attr[to-1] : AT_NORMAL;
  323 		next_attr = (from + len < curr) ? attr[from + len] : prev_attr;
  324 		if (!is_at_equiv(attr[from], prev_attr) &&
  325 			!is_at_equiv(attr[from], next_attr))
  326 		{
  327 			width += attr_swidth(attr[from]);
  328 			if (from + len < curr)
  329 				width += attr_ewidth(attr[from]);
  330 			if (is_at_equiv(prev_attr, next_attr))
  331 			{
  332 				width += attr_ewidth(prev_attr);
  333 				if (from + len < curr)
  334 					width += attr_swidth(next_attr);
  335 			}
  336 		}
  337 
  338 		if (shift - shifted < width)
  339 			break;
  340 		from += len;
  341 		shifted += width;
  342 		if (shifted < 0)
  343 			shifted = 0;
  344 	}
  345 	while (from < curr)
  346 	{
  347 		linebuf[to] = linebuf[from];
  348 		attr[to++] = attr[from++];
  349 	}
  350 	curr = to;
  351 	column -= shifted;
  352 	cshift += shifted;
  353 }
  354 
  355 /*
  356  *
  357  */
  358 	public void
  359 pshift_all()
  360 {
  361 	pshift(column);
  362 }
  363 
  364 /*
  365  * Return the printing width of the start (enter) sequence
  366  * for a given character attribute.
  367  */
  368 	static int
  369 attr_swidth(a)
  370 	int a;
  371 {
  372 	int w = 0;
  373 
  374 	a = apply_at_specials(a);
  375 
  376 	if (a & AT_UNDERLINE)
  377 		w += ul_s_width;
  378 	if (a & AT_BOLD)
  379 		w += bo_s_width;
  380 	if (a & AT_BLINK)
  381 		w += bl_s_width;
  382 	if (a & AT_STANDOUT)
  383 		w += so_s_width;
  384 
  385 	return w;
  386 }
  387 
  388 /*
  389  * Return the printing width of the end (exit) sequence
  390  * for a given character attribute.
  391  */
  392 	static int
  393 attr_ewidth(a)
  394 	int a;
  395 {
  396 	int w = 0;
  397 
  398 	a = apply_at_specials(a);
  399 
  400 	if (a & AT_UNDERLINE)
  401 		w += ul_e_width;
  402 	if (a & AT_BOLD)
  403 		w += bo_e_width;
  404 	if (a & AT_BLINK)
  405 		w += bl_e_width;
  406 	if (a & AT_STANDOUT)
  407 		w += so_e_width;
  408 
  409 	return w;
  410 }
  411 
  412 /*
  413  * Return the printing width of a given character and attribute,
  414  * if the character were added to the current position in the line buffer.
  415  * Adding a character with a given attribute may cause an enter or exit
  416  * attribute sequence to be inserted, so this must be taken into account.
  417  */
  418 	static int
  419 pwidth(ch, a, prev_ch)
  420 	LWCHAR ch;
  421 	int a;
  422 	LWCHAR prev_ch;
  423 {
  424 	int w;
  425 
  426 	if (ch == '\b')
  427 		/*
  428 		 * Backspace moves backwards one or two positions.
  429 		 * XXX - Incorrect if several '\b' in a row.
  430 		 */
  431 		return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
  432 
  433 	if (!utf_mode || is_ascii_char(ch))
  434 	{
  435 		if (control_char((char)ch))
  436 		{
  437 			/*
  438 			 * Control characters do unpredictable things,
  439 			 * so we don't even try to guess; say it doesn't move.
  440 			 * This can only happen if the -r flag is in effect.
  441 			 */
  442 			return (0);
  443 		}
  444 	} else
  445 	{
  446 		if (is_composing_char(ch) || is_combining_char(prev_ch, ch))
  447 		{
  448 			/*
  449 			 * Composing and combining chars take up no space.
  450 			 *
  451 			 * Some terminals, upon failure to compose a
  452 			 * composing character with the character(s) that
  453 			 * precede(s) it will actually take up one column
  454 			 * for the composing character; there isn't much
  455 			 * we could do short of testing the (complex)
  456 			 * composition process ourselves and printing
  457 			 * a binary representation when it fails.
  458 			 */
  459 			return (0);
  460 		}
  461 	}
  462 
  463 	/*
  464 	 * Other characters take one or two columns,
  465 	 * plus the width of any attribute enter/exit sequence.
  466 	 */
  467 	w = 1;
  468 	if (is_wide_char(ch))
  469 		w++;
  470 	if (curr > 0 && !is_at_equiv(attr[curr-1], a))
  471 		w += attr_ewidth(attr[curr-1]);
  472 	if ((apply_at_specials(a) != AT_NORMAL) &&
  473 	    (curr == 0 || !is_at_equiv(attr[curr-1], a)))
  474 		w += attr_swidth(a);
  475 	return (w);
  476 }
  477 
  478 /*
  479  * Delete to the previous base character in the line buffer.
  480  * Return 1 if one is found.
  481  */
  482 	static int
  483 backc()
  484 {
  485 	LWCHAR prev_ch;
  486 	char *p = linebuf + curr;
  487 	LWCHAR ch = step_char(&p, -1, linebuf + lmargin);
  488 	int width;
  489 
  490 	/* This assumes that there is no '\b' in linebuf.  */
  491 	while (   curr > lmargin
  492 	       && column > lmargin
  493 	       && (!(attr[curr - 1] & (AT_ANSI|AT_BINARY))))
  494 	{
  495 		curr = p - linebuf;
  496 		prev_ch = step_char(&p, -1, linebuf + lmargin);
  497 		width = pwidth(ch, attr[curr], prev_ch);
  498 		column -= width;
  499 		if (width > 0)
  500 			return 1;
  501 		ch = prev_ch;
  502 	}
  503 
  504 	return 0;
  505 }
  506 
  507 /*
  508  * Are we currently within a recognized ANSI escape sequence?
  509  */
  510 	static int
  511 in_ansi_esc_seq()
  512 {
  513 	char *p;
  514 
  515 	/*
  516 	 * Search backwards for either an ESC (which means we ARE in a seq);
  517 	 * or an end char (which means we're NOT in a seq).
  518 	 */
  519 	for (p = &linebuf[curr];  p > linebuf; )
  520 	{
  521 		LWCHAR ch = step_char(&p, -1, linebuf);
  522 		if (IS_CSI_START(ch))
  523 			return (1);
  524 		if (!is_ansi_middle(ch))
  525 			return (0);
  526 	}
  527 	return (0);
  528 }
  529 
  530 /*
  531  * Is a character the end of an ANSI escape sequence?
  532  */
  533 	public int
  534 is_ansi_end(ch)
  535 	LWCHAR ch;
  536 {
  537 	if (!is_ascii_char(ch))
  538 		return (0);
  539 	return (strchr(end_ansi_chars, (char) ch) != NULL);
  540 }
  541 
  542 /*
  543  *
  544  */
  545 	public int
  546 is_ansi_middle(ch)
  547 	LWCHAR ch;
  548 {
  549 	if (!is_ascii_char(ch))
  550 		return (0);
  551 	if (is_ansi_end(ch))
  552 		return (0);
  553 	return (strchr(mid_ansi_chars, (char) ch) != NULL);
  554 }
  555 
  556 /*
  557  * Append a character and attribute to the line buffer.
  558  */
  559 #define	STORE_CHAR(ch,a,rep,pos) \
  560 	do { \
  561 		if (store_char((ch),(a),(rep),(pos))) return (1); \
  562 	} while (0)
  563 
  564 	static int
  565 store_char(ch, a, rep, pos)
  566 	LWCHAR ch;
  567 	int a;
  568 	char *rep;
  569 	POSITION pos;
  570 {
  571 	int w;
  572 	int replen;
  573 	char cs;
  574 
  575 	w = (a & (AT_UNDERLINE|AT_BOLD));	/* Pre-use w.  */
  576 	if (w != AT_NORMAL)
  577 		last_overstrike = w;
  578 
  579 #if HILITE_SEARCH
  580 	{
  581 		int matches;
  582 		if (is_hilited(pos, pos+1, 0, &matches))
  583 		{
  584 			/*
  585 			 * This character should be highlighted.
  586 			 * Override the attribute passed in.
  587 			 */
  588 			if (a != AT_ANSI)
  589 				a |= AT_HILITE;
  590 		}
  591 	}
  592 #endif
  593 
  594 	if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
  595 	{
  596 		if (!is_ansi_end(ch) && !is_ansi_middle(ch)) {
  597 			/* Remove whole unrecognized sequence.  */
  598 			char *p = &linebuf[curr];
  599 			LWCHAR bch;
  600 			do {
  601 				bch = step_char(&p, -1, linebuf);
  602 			} while (p > linebuf && !IS_CSI_START(bch));
  603 			curr = p - linebuf;
  604 			return 0;
  605 		}
  606 		a = AT_ANSI;	/* Will force re-AT_'ing around it.  */
  607 		w = 0;
  608 	}
  609 	else if (ctldisp == OPT_ONPLUS && IS_CSI_START(ch))
  610 	{
  611 		a = AT_ANSI;	/* Will force re-AT_'ing around it.  */
  612 		w = 0;
  613 	}
  614 	else
  615 	{
  616 		char *p = &linebuf[curr];
  617 		LWCHAR prev_ch = step_char(&p, -1, linebuf);
  618 		w = pwidth(ch, a, prev_ch);
  619 	}
  620 
  621 	if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
  622 		/*
  623 		 * Won't fit on screen.
  624 		 */
  625 		return (1);
  626 
  627 	if (rep == NULL)
  628 	{
  629 		cs = (char) ch;
  630 		rep = &cs;
  631 		replen = 1;
  632 	} else
  633 	{
  634 		replen = utf_len(rep[0]);
  635 	}
  636 	if (curr + replen >= size_linebuf-6)
  637 	{
  638 		/*
  639 		 * Won't fit in line buffer.
  640 		 * Try to expand it.
  641 		 */
  642 		if (expand_linebuf())
  643 			return (1);
  644 	}
  645 
  646 	while (replen-- > 0)
  647 	{
  648 		linebuf[curr] = *rep++;
  649 		attr[curr] = a;
  650 		curr++;
  651 	}
  652 	column += w;
  653 	return (0);
  654 }
  655 
  656 /*
  657  * Append a tab to the line buffer.
  658  * Store spaces to represent the tab.
  659  */
  660 #define	STORE_TAB(a,pos) \
  661 	do { if (store_tab((a),(pos))) return (1); } while (0)
  662 
  663 	static int
  664 store_tab(attr, pos)
  665 	int attr;
  666 	POSITION pos;
  667 {
  668 	int to_tab = column + cshift - lmargin;
  669 	int i;
  670 
  671 	if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1])
  672 		to_tab = tabdefault -
  673 		     ((to_tab - tabstops[ntabstops-1]) % tabdefault);
  674 	else
  675 	{
  676 		for (i = ntabstops - 2;  i >= 0;  i--)
  677 			if (to_tab >= tabstops[i])
  678 				break;
  679 		to_tab = tabstops[i+1] - to_tab;
  680 	}
  681 
  682 	if (column + to_tab - 1 + pwidth(' ', attr, 0) + attr_ewidth(attr) > sc_width)
  683 		return 1;
  684 
  685 	do {
  686 		STORE_CHAR(' ', attr, " ", pos);
  687 	} while (--to_tab > 0);
  688 	return 0;
  689 }
  690 
  691 #define STORE_PRCHAR(c, pos) \
  692 	do { if (store_prchar((c), (pos))) return 1; } while (0)
  693 
  694 	static int
  695 store_prchar(c, pos)
  696 	char c;
  697 	POSITION pos;
  698 {
  699 	char *s;
  700 
  701 	/*
  702 	 * Convert to printable representation.
  703 	 */
  704 	s = prchar(c);
  705 
  706 	/*
  707 	 * Make sure we can get the entire representation
  708 	 * of the character on this line.
  709 	 */
  710 	if (column + (int) strlen(s) - 1 +
  711             pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
  712 		return 1;
  713 
  714 	for ( ;  *s != 0;  s++)
  715 		STORE_CHAR(*s, AT_BINARY, NULL, pos);
  716 
  717 	return 0;
  718 }
  719 
  720 	static int
  721 flush_mbc_buf(pos)
  722 	POSITION pos;
  723 {
  724 	int i;
  725 
  726 	for (i = 0; i < mbc_buf_index; i++)
  727 		if (store_prchar(mbc_buf[i], pos))
  728 			return mbc_buf_index - i;
  729 
  730 	return 0;
  731 }
  732 
  733 /*
  734  * Append a character to the line buffer.
  735  * Expand tabs into spaces, handle underlining, boldfacing, etc.
  736  * Returns 0 if ok, 1 if couldn't fit in buffer.
  737  */
  738 	public int
  739 pappend(c, pos)
  740 	char c;
  741 	POSITION pos;
  742 {
  743 	int r;
  744 
  745 	if (pendc)
  746 	{
  747 		if (do_append(pendc, NULL, pendpos))
  748 			/*
  749 			 * Oops.  We've probably lost the char which
  750 			 * was in pendc, since caller won't back up.
  751 			 */
  752 			return (1);
  753 		pendc = '\0';
  754 	}
  755 
  756 	if (c == '\r' && bs_mode == BS_SPECIAL)
  757 	{
  758 		if (mbc_buf_len > 0)  /* utf_mode must be on. */
  759 		{
  760 			/* Flush incomplete (truncated) sequence. */
  761 			r = flush_mbc_buf(mbc_pos);
  762 			mbc_buf_index = r + 1;
  763 			mbc_buf_len = 0;
  764 			if (r)
  765 				return (mbc_buf_index);
  766 		}
  767 
  768 		/*
  769 		 * Don't put the CR into the buffer until we see
  770 		 * the next char.  If the next char is a newline,
  771 		 * discard the CR.
  772 		 */
  773 		pendc = c;
  774 		pendpos = pos;
  775 		return (0);
  776 	}
  777 
  778 	if (!utf_mode)
  779 	{
  780 		r = do_append((LWCHAR) c, NULL, pos);
  781 	} else
  782 	{
  783 		/* Perform strict validation in all possible cases. */
  784 		if (mbc_buf_len == 0)
  785 		{
  786 		retry:
  787 			mbc_buf_index = 1;
  788 			*mbc_buf = c;
  789 			if (IS_ASCII_OCTET(c))
  790 				r = do_append((LWCHAR) c, NULL, pos);
  791 			else if (IS_UTF8_LEAD(c))
  792 			{
  793 				mbc_buf_len = utf_len(c);
  794 				mbc_pos = pos;
  795 				return (0);
  796 			} else
  797 				/* UTF8_INVALID or stray UTF8_TRAIL */
  798 				r = flush_mbc_buf(pos);
  799 		} else if (IS_UTF8_TRAIL(c))
  800 		{
  801 			mbc_buf[mbc_buf_index++] = c;
  802 			if (mbc_buf_index < mbc_buf_len)
  803 				return (0);
  804 			if (is_utf8_well_formed(mbc_buf))
  805 				r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos);
  806 			else
  807 				/* Complete, but not shortest form, sequence. */
  808 				mbc_buf_index = r = flush_mbc_buf(mbc_pos);
  809 			mbc_buf_len = 0;
  810 		} else
  811 		{
  812 			/* Flush incomplete (truncated) sequence.  */
  813 			r = flush_mbc_buf(mbc_pos);
  814 			mbc_buf_index = r + 1;
  815 			mbc_buf_len = 0;
  816 			/* Handle new char.  */
  817 			if (!r)
  818 				goto retry;
  819  		}
  820 	}
  821 
  822 	/*
  823 	 * If we need to shift the line, do it.
  824 	 * But wait until we get to at least the middle of the screen,
  825 	 * so shifting it doesn't affect the chars we're currently
  826 	 * pappending.  (Bold & underline can get messed up otherwise.)
  827 	 */
  828 	if (cshift < hshift && column > sc_width / 2)
  829 	{
  830 		linebuf[curr] = '\0';
  831 		pshift(hshift - cshift);
  832 	}
  833 	if (r)
  834 	{
  835 		/* How many chars should caller back up? */
  836 		r = (!utf_mode) ? 1 : mbc_buf_index;
  837 	}
  838 	return (r);
  839 }
  840 
  841 	static int
  842 do_append(ch, rep, pos)
  843 	LWCHAR ch;
  844 	char *rep;
  845 	POSITION pos;
  846 {
  847 	register int a;
  848 	LWCHAR prev_ch;
  849 
  850 	a = AT_NORMAL;
  851 
  852 	if (ch == '\b')
  853 	{
  854 		if (bs_mode == BS_CONTROL)
  855 			goto do_control_char;
  856 
  857 		/*
  858 		 * A better test is needed here so we don't
  859 		 * backspace over part of the printed
  860 		 * representation of a binary character.
  861 		 */
  862 		if (   curr <= lmargin
  863 		    || column <= lmargin
  864 		    || (attr[curr - 1] & (AT_ANSI|AT_BINARY)))
  865 			STORE_PRCHAR('\b', pos);
  866 		else if (bs_mode == BS_NORMAL)
  867 			STORE_CHAR(ch, AT_NORMAL, NULL, pos);
  868 		else if (bs_mode == BS_SPECIAL)
  869 			overstrike = backc();
  870 
  871 		return 0;
  872 	}
  873 
  874 	if (overstrike > 0)
  875 	{
  876 		/*
  877 		 * Overstrike the character at the current position
  878 		 * in the line buffer.  This will cause either
  879 		 * underline (if a "_" is overstruck),
  880 		 * bold (if an identical character is overstruck),
  881 		 * or just deletion of the character in the buffer.
  882 		 */
  883 		overstrike = utf_mode ? -1 : 0;
  884 		/* To be correct, this must be a base character.  */
  885 		prev_ch = get_wchar(linebuf + curr);
  886 		a = attr[curr];
  887 		if (ch == prev_ch)
  888 		{
  889 			/*
  890 			 * Overstriking a char with itself means make it bold.
  891 			 * But overstriking an underscore with itself is
  892 			 * ambiguous.  It could mean make it bold, or
  893 			 * it could mean make it underlined.
  894 			 * Use the previous overstrike to resolve it.
  895 			 */
  896 			if (ch == '_')
  897 			{
  898 				if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL)
  899 					a |= (AT_BOLD|AT_UNDERLINE);
  900 				else if (last_overstrike != AT_NORMAL)
  901 					a |= last_overstrike;
  902 				else
  903 					a |= AT_BOLD;
  904 			} else
  905 				a |= AT_BOLD;
  906 		} else if (ch == '_')
  907 		{
  908 			a |= AT_UNDERLINE;
  909 			ch = prev_ch;
  910 			rep = linebuf + curr;
  911 		} else if (prev_ch == '_')
  912 		{
  913 			a |= AT_UNDERLINE;
  914 		}
  915 		/* Else we replace prev_ch, but we keep its attributes.  */
  916 	} else if (overstrike < 0)
  917 	{
  918 		if (   is_composing_char(ch)
  919 		    || is_combining_char(get_wchar(linebuf + curr), ch))
  920 			/* Continuation of the same overstrike.  */
  921 			a = last_overstrike;
  922 		else
  923 			overstrike = 0;
  924 	}
  925 
  926 	if (ch == '\t')
  927 	{
  928 		/*
  929 		 * Expand a tab into spaces.
  930 		 */
  931 		switch (bs_mode)
  932 		{
  933 		case BS_CONTROL:
  934 			goto do_control_char;
  935 		case BS_NORMAL:
  936 		case BS_SPECIAL:
  937 			STORE_TAB(a, pos);
  938 			break;
  939 		}
  940 	} else if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch))
  941 	{
  942 	do_control_char:
  943 		if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && IS_CSI_START(ch)))
  944 		{
  945 			/*
  946 			 * Output as a normal character.
  947 			 */
  948 			STORE_CHAR(ch, AT_NORMAL, rep, pos);
  949 		} else
  950 		{
  951 			STORE_PRCHAR((char) ch, pos);
  952 		}
  953 	} else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch))
  954 	{
  955 		char *s;
  956 
  957 		s = prutfchar(ch);
  958 
  959 		if (column + (int) strlen(s) - 1 +
  960 		    pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
  961 			return (1);
  962 
  963 		for ( ;  *s != 0;  s++)
  964 			STORE_CHAR(*s, AT_BINARY, NULL, pos);
  965  	} else
  966 	{
  967 		STORE_CHAR(ch, a, rep, pos);
  968 	}
  969  	return (0);
  970 }
  971 
  972 /*
  973  *
  974  */
  975 	public int
  976 pflushmbc()
  977 {
  978 	int r = 0;
  979 
  980 	if (mbc_buf_len > 0)
  981 	{
  982 		/* Flush incomplete (truncated) sequence.  */
  983 		r = flush_mbc_buf(mbc_pos);
  984 		mbc_buf_len = 0;
  985 	}
  986 	return r;
  987 }
  988 
  989 /*
  990  * Terminate the line in the line buffer.
  991  */
  992 	public void
  993 pdone(endline)
  994 	int endline;
  995 {
  996 	int nl;
  997 
  998 	(void) pflushmbc();
  999 
 1000 	if (pendc && (pendc != '\r' || !endline))
 1001 		/*
 1002 		 * If we had a pending character, put it in the buffer.
 1003 		 * But discard a pending CR if we are at end of line
 1004 		 * (that is, discard the CR in a CR/LF sequence).
 1005 		 */
 1006 		(void) do_append(pendc, NULL, pendpos);
 1007 
 1008 	/*
 1009 	 * Make sure we've shifted the line, if we need to.
 1010 	 */
 1011 	if (cshift < hshift)
 1012 		pshift(hshift - cshift);
 1013 
 1014 	if (ctldisp == OPT_ONPLUS && is_ansi_end('m'))
 1015 	{
 1016 		/* Switch to normal attribute at end of line. */
 1017 		char *p = "\033[m";
 1018 		for ( ;  *p != '\0';  p++)
 1019 		{
 1020 			linebuf[curr] = *p;
 1021 			attr[curr++] = AT_ANSI;
 1022 		}
 1023 	}
 1024 
 1025 	/*
 1026 	 * Add a newline if necessary,
 1027 	 * and append a '\0' to the end of the line.
 1028 	 * We output a newline if we're not at the right edge of the screen,
 1029 	 * or if the terminal doesn't auto wrap,
 1030 	 * or if this is really the end of the line AND the terminal ignores
 1031 	 * a newline at the right edge.
 1032 	 * (In the last case we don't want to output a newline if the terminal
 1033 	 * doesn't ignore it since that would produce an extra blank line.
 1034 	 * But we do want to output a newline if the terminal ignores it in case
 1035 	 * the next line is blank.  In that case the single newline output for
 1036 	 * that blank line would be ignored!)
 1037 	 */
 1038 	if (!oldbot)
 1039 		nl = (column < sc_width || !