1238730Sdelphij/*
2330571Sdelphij * Copyright (C) 1984-2017  Mark Nudelman
3238730Sdelphij *
4238730Sdelphij * You may distribute under the terms of either the GNU General Public
5238730Sdelphij * License or the Less License, as specified in the README file.
6238730Sdelphij *
7238730Sdelphij * For more information, see the README file.
8238730Sdelphij */
960786Sps
1060786Sps/*
1160786Sps * Routines to manipulate the "line buffer".
1260786Sps * The line buffer holds a line of output as it is being built
1360786Sps * in preparation for output to the screen.
1460786Sps */
1560786Sps
1660786Sps#include "less.h"
17161478Sdelphij#include "charset.h"
18330571Sdelphij#include "position.h"
1960786Sps
20330571Sdelphij#if MSDOS_COMPILER==WIN32C
21330571Sdelphij#define WIN32_LEAN_AND_MEAN
22330571Sdelphij#include <windows.h>
23330571Sdelphij#endif
24330571Sdelphij
25161478Sdelphijstatic char *linebuf = NULL;	/* Buffer which holds the current output line */
2689019Spsstatic char *attr = NULL;	/* Extension of linebuf to hold attributes */
2789019Spspublic int size_linebuf = 0;	/* Size of line buffer (and attr buffer) */
2860786Sps
29161478Sdelphijstatic int cshift;		/* Current left-shift of output line buffer */
3060786Spspublic int hshift;		/* Desired left-shift of output line buffer */
3189019Spspublic int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */
3289019Spspublic int ntabstops = 1;	/* Number of tabstops */
3389019Spspublic int tabdefault = 8;	/* Default repeated tabstops */
34237613Sdelphijpublic POSITION highest_hilite;	/* Pos of last hilite in file found so far */
3560786Sps
3660786Spsstatic int curr;		/* Index into linebuf */
3760786Spsstatic int column;		/* Printable length, accounting for
3860786Sps				   backspaces, etc. */
39330571Sdelphijstatic int right_curr;
40330571Sdelphijstatic int right_column;
4160786Spsstatic int overstrike;		/* Next char should overstrike previous char */
42128345Stjrstatic int last_overstrike = AT_NORMAL;
4360786Spsstatic int is_null_line;	/* There is no current line */
4463128Spsstatic int lmargin;		/* Left margin */
45294286Sdelphijstatic LWCHAR pendc;
4660786Spsstatic POSITION pendpos;
4760786Spsstatic char *end_ansi_chars;
48161478Sdelphijstatic char *mid_ansi_chars;
4960786Sps
50330571Sdelphijstatic int attr_swidth LESSPARAMS ((int a));
51330571Sdelphijstatic int attr_ewidth LESSPARAMS ((int a));
52330571Sdelphijstatic int do_append LESSPARAMS ((LWCHAR ch, char *rep, POSITION pos));
5360786Sps
54161478Sdelphijextern int sigs;
5560786Spsextern int bs_mode;
5660786Spsextern int linenums;
5760786Spsextern int ctldisp;
5860786Spsextern int twiddle;
5960786Spsextern int binattr;
6063128Spsextern int status_col;
6160786Spsextern int auto_wrap, ignaw;
6260786Spsextern int bo_s_width, bo_e_width;
6360786Spsextern int ul_s_width, ul_e_width;
6460786Spsextern int bl_s_width, bl_e_width;
6560786Spsextern int so_s_width, so_e_width;
6660786Spsextern int sc_width, sc_height;
6760786Spsextern int utf_mode;
6863128Spsextern POSITION start_attnpos;
6963128Spsextern POSITION end_attnpos;
70330571Sdelphijextern LWCHAR rscroll_char;
71330571Sdelphijextern int rscroll_attr;
7260786Sps
73161478Sdelphijstatic char mbc_buf[MAX_UTF_CHAR_LEN];
74161478Sdelphijstatic int mbc_buf_len = 0;
75161478Sdelphijstatic int mbc_buf_index = 0;
76161478Sdelphijstatic POSITION mbc_pos;
77161478Sdelphij
7860786Sps/*
7960786Sps * Initialize from environment variables.
8060786Sps */
8160786Sps	public void
8260786Spsinit_line()
8360786Sps{
8460786Sps	end_ansi_chars = lgetenv("LESSANSIENDCHARS");
8560786Sps	if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
8660786Sps		end_ansi_chars = "m";
87161478Sdelphij
88161478Sdelphij	mid_ansi_chars = lgetenv("LESSANSIMIDCHARS");
89161478Sdelphij	if (mid_ansi_chars == NULL || *mid_ansi_chars == '\0')
90294286Sdelphij		mid_ansi_chars = "0123456789:;[?!\"'#%()*+ ";
91161478Sdelphij
9289019Sps	linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
9389019Sps	attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
9489019Sps	size_linebuf = LINEBUF_SIZE;
9560786Sps}
9660786Sps
9760786Sps/*
9889019Sps * Expand the line buffer.
9989019Sps */
100161478Sdelphij	static int
10189019Spsexpand_linebuf()
10289019Sps{
103161478Sdelphij	/* Double the size of the line buffer. */
104161478Sdelphij	int new_size = size_linebuf * 2;
105161478Sdelphij
106161478Sdelphij	/* Just realloc to expand the buffer, if we can. */
107161478Sdelphij#if HAVE_REALLOC
108161478Sdelphij	char *new_buf = (char *) realloc(linebuf, new_size);
109161478Sdelphij	char *new_attr = (char *) realloc(attr, new_size);
110161478Sdelphij#else
11189019Sps	char *new_buf = (char *) calloc(new_size, sizeof(char));
11289019Sps	char *new_attr = (char *) calloc(new_size, sizeof(char));
113161478Sdelphij#endif
11489019Sps	if (new_buf == NULL || new_attr == NULL)
11589019Sps	{
11689019Sps		if (new_attr != NULL)
11789019Sps			free(new_attr);
11889019Sps		if (new_buf != NULL)
11989019Sps			free(new_buf);
12089019Sps		return 1;
12189019Sps	}
122161478Sdelphij#if HAVE_REALLOC
123161478Sdelphij	/*
124161478Sdelphij	 * We realloc'd the buffers; they already have the old contents.
125161478Sdelphij	 */
126161478Sdelphij	#if 0
127161478Sdelphij	memset(new_buf + size_linebuf, 0, new_size - size_linebuf);
128161478Sdelphij	memset(new_attr + size_linebuf, 0, new_size - size_linebuf);
129161478Sdelphij	#endif
130161478Sdelphij#else
131161478Sdelphij	/*
132161478Sdelphij	 * We just calloc'd the buffers; copy the old contents.
133161478Sdelphij	 */
13489019Sps	memcpy(new_buf, linebuf, size_linebuf * sizeof(char));
13589019Sps	memcpy(new_attr, attr, size_linebuf * sizeof(char));
136128345Stjr	free(attr);
137128345Stjr	free(linebuf);
138161478Sdelphij#endif
13989019Sps	linebuf = new_buf;
14089019Sps	attr = new_attr;
14189019Sps	size_linebuf = new_size;
14289019Sps	return 0;
14389019Sps}
14489019Sps
14589019Sps/*
146161478Sdelphij * Is a character ASCII?
147161478Sdelphij */
148161478Sdelphij	public int
149161478Sdelphijis_ascii_char(ch)
150161478Sdelphij	LWCHAR ch;
151161478Sdelphij{
152161478Sdelphij	return (ch <= 0x7F);
153161478Sdelphij}
154161478Sdelphij
155161478Sdelphij/*
15660786Sps * Rewind the line buffer.
15760786Sps */
15860786Sps	public void
15960786Spsprewind()
16060786Sps{
16160786Sps	curr = 0;
16260786Sps	column = 0;
163330571Sdelphij	right_curr = 0;
164330571Sdelphij	right_column = 0;
165161478Sdelphij	cshift = 0;
16660786Sps	overstrike = 0;
167161478Sdelphij	last_overstrike = AT_NORMAL;
168161478Sdelphij	mbc_buf_len = 0;
16960786Sps	is_null_line = 0;
17060786Sps	pendc = '\0';
17163128Sps	lmargin = 0;
172128345Stjr	if (status_col)
173330571Sdelphij		lmargin += 2;
17460786Sps}
17560786Sps
17660786Sps/*
177330571Sdelphij * Set a character in the line buffer.
178330571Sdelphij */
179330571Sdelphij	static void
180330571Sdelphijset_linebuf(n, ch, a)
181330571Sdelphij	int n;
182330571Sdelphij	LWCHAR ch;
183330571Sdelphij	char a;
184330571Sdelphij{
185330571Sdelphij	linebuf[n] = ch;
186330571Sdelphij	attr[n] = a;
187330571Sdelphij}
188330571Sdelphij
189330571Sdelphij/*
190330571Sdelphij * Append a character to the line buffer.
191330571Sdelphij */
192330571Sdelphij	static void
193330571Sdelphijadd_linebuf(ch, a, w)
194330571Sdelphij	LWCHAR ch;
195330571Sdelphij	char a;
196330571Sdelphij	int w;
197330571Sdelphij{
198330571Sdelphij	set_linebuf(curr++, ch, a);
199330571Sdelphij	column += w;
200330571Sdelphij}
201330571Sdelphij
202330571Sdelphij/*
20360786Sps * Insert the line number (of the given position) into the line buffer.
20460786Sps */
20560786Sps	public void
20660786Spsplinenum(pos)
20760786Sps	POSITION pos;
20860786Sps{
209330571Sdelphij	LINENUM linenum = 0;
210330571Sdelphij	int i;
21160786Sps
21263128Sps	if (linenums == OPT_ONPLUS)
21363128Sps	{
21463128Sps		/*
21563128Sps		 * Get the line number and put it in the current line.
21663128Sps		 * {{ Note: since find_linenum calls forw_raw_line,
21763128Sps		 *    it may seek in the input file, requiring the caller
21863128Sps		 *    of plinenum to re-seek if necessary. }}
21963128Sps		 * {{ Since forw_raw_line modifies linebuf, we must
22063128Sps		 *    do this first, before storing anything in linebuf. }}
22163128Sps		 */
222128345Stjr		linenum = find_linenum(pos);
22363128Sps	}
22463128Sps
22560786Sps	/*
22663128Sps	 * Display a status column if the -J option is set.
22760786Sps	 */
22863128Sps	if (status_col)
22963128Sps	{
230330571Sdelphij		int a = AT_NORMAL;
231330571Sdelphij		char c = posmark(pos);
232330571Sdelphij		if (c != 0)
233330571Sdelphij			a |= AT_HILITE;
234330571Sdelphij		else
235330571Sdelphij		{
236330571Sdelphij			c = ' ';
237330571Sdelphij			if (start_attnpos != NULL_POSITION &&
238330571Sdelphij			    pos >= start_attnpos && pos <= end_attnpos)
239330571Sdelphij				a |= AT_HILITE;
240330571Sdelphij		}
241330571Sdelphij		add_linebuf(c, a, 1); /* column 0: status */
242330571Sdelphij		add_linebuf(' ', AT_NORMAL, 1); /* column 1: empty */
24363128Sps	}
244330571Sdelphij
24560786Sps	/*
24663128Sps	 * Display the line number at the start of each line
24763128Sps	 * if the -N option is set.
24860786Sps	 */
24963128Sps	if (linenums == OPT_ONPLUS)
25063128Sps	{
251330571Sdelphij		char buf[INT_STRLEN_BOUND(linenum) + 2];
252330571Sdelphij		int pad = 0;
253128345Stjr		int n;
254128345Stjr
255128345Stjr		linenumtoa(linenum, buf);
256294286Sdelphij		n = (int) strlen(buf);
257128345Stjr		if (n < MIN_LINENUM_WIDTH)
258330571Sdelphij			pad = MIN_LINENUM_WIDTH - n;
259330571Sdelphij		for (i = 0; i < pad; i++)
260330571Sdelphij			add_linebuf(' ', AT_NORMAL, 1);
261128345Stjr		for (i = 0; i < n; i++)
262330571Sdelphij			add_linebuf(buf[i], AT_BOLD, 1);
263330571Sdelphij		add_linebuf(' ', AT_NORMAL, 1);
264330571Sdelphij		lmargin += n + pad + 1;
26563128Sps	}
26660786Sps	/*
26763128Sps	 * Append enough spaces to bring us to the lmargin.
26860786Sps	 */
26963128Sps	while (column < lmargin)
27060786Sps	{
271330571Sdelphij		add_linebuf(' ', AT_NORMAL, 1);
27263128Sps	}
27360786Sps}
27460786Sps
27560786Sps/*
276161478Sdelphij * Shift the input line left.
277161478Sdelphij * This means discarding N printable chars at the start of the buffer.
27860786Sps */
279161478Sdelphij	static void
280161478Sdelphijpshift(shift)
281161478Sdelphij	int shift;
282161478Sdelphij{
283161478Sdelphij	LWCHAR prev_ch = 0;
284161478Sdelphij	unsigned char c;
285161478Sdelphij	int shifted = 0;
286161478Sdelphij	int to;
287161478Sdelphij	int from;
28889019Sps	int len;
289161478Sdelphij	int width;
290161478Sdelphij	int prev_attr;
291161478Sdelphij	int next_attr;
29289019Sps
293161478Sdelphij	if (shift > column - lmargin)
294161478Sdelphij		shift = column - lmargin;
295161478Sdelphij	if (shift > curr - lmargin)
296161478Sdelphij		shift = curr - lmargin;
297161478Sdelphij
298161478Sdelphij	to = from = lmargin;
29989019Sps	/*
300161478Sdelphij	 * We keep on going when shifted == shift
301161478Sdelphij	 * to get all combining chars.
30289019Sps	 */
303161478Sdelphij	while (shifted <= shift && from < curr)
30489019Sps	{
305161478Sdelphij		c = linebuf[from];
306172471Sdelphij		if (ctldisp == OPT_ONPLUS && IS_CSI_START(c))
30789019Sps		{
308161478Sdelphij			/* Keep cumulative effect.  */
309161478Sdelphij			linebuf[to] = c;
310161478Sdelphij			attr[to++] = attr[from++];
311161478Sdelphij			while (from < curr && linebuf[from])
312161478Sdelphij			{
313161478Sdelphij				linebuf[to] = linebuf[from];
314161478Sdelphij				attr[to++] = attr[from];
315161478Sdelphij				if (!is_ansi_middle(linebuf[from++]))
316161478Sdelphij					break;
317161478Sdelphij			}
318161478Sdelphij			continue;
319161478Sdelphij		}
320161478Sdelphij
321161478Sdelphij		width = 0;
322161478Sdelphij
323161478Sdelphij		if (!IS_ASCII_OCTET(c) && utf_mode)
324161478Sdelphij		{
325161478Sdelphij			/* Assumes well-formedness validation already done.  */
326161478Sdelphij			LWCHAR ch;
327161478Sdelphij
328161478Sdelphij			len = utf_len(c);
329161478Sdelphij			if (from + len > curr)
330161478Sdelphij				break;
331161478Sdelphij			ch = get_wchar(linebuf + from);
332161478Sdelphij			if (!is_composing_char(ch) && !is_combining_char(prev_ch, ch))
333161478Sdelphij				width = is_wide_char(ch) ? 2 : 1;
334161478Sdelphij			prev_ch = ch;
33589019Sps		} else
33689019Sps		{
337161478Sdelphij			len = 1;
338161478Sdelphij			if (c == '\b')
339161478Sdelphij				/* XXX - Incorrect if several '\b' in a row.  */
340161478Sdelphij				width = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
341161478Sdelphij			else if (!control_char(c))
342161478Sdelphij				width = 1;
343161478Sdelphij			prev_ch = 0;
344161478Sdelphij		}
345161478Sdelphij
346161478Sdelphij		if (width == 2 && shift - shifted == 1) {
347161478Sdelphij			/* Should never happen when called by pshift_all().  */
348161478Sdelphij			attr[to] = attr[from];
349161478Sdelphij			/*
350161478Sdelphij			 * Assume a wide_char will never be the first half of a
351161478Sdelphij			 * combining_char pair, so reset prev_ch in case we're
352161478Sdelphij			 * followed by a '\b'.
353161478Sdelphij			 */
354161478Sdelphij			prev_ch = linebuf[to++] = ' ';
355161478Sdelphij			from += len;
356161478Sdelphij			shifted++;
357161478Sdelphij			continue;
358161478Sdelphij		}
359161478Sdelphij
360161478Sdelphij		/* Adjust width for magic cookies. */
361161478Sdelphij		prev_attr = (to > 0) ? attr[to-1] : AT_NORMAL;
362161478Sdelphij		next_attr = (from + len < curr) ? attr[from + len] : prev_attr;
363161478Sdelphij		if (!is_at_equiv(attr[from], prev_attr) &&
364161478Sdelphij			!is_at_equiv(attr[from], next_attr))
365161478Sdelphij		{
366161478Sdelphij			width += attr_swidth(attr[from]);
367161478Sdelphij			if (from + len < curr)
368161478Sdelphij				width += attr_ewidth(attr[from]);
369161478Sdelphij			if (is_at_equiv(prev_attr, next_attr))
37089019Sps			{
371161478Sdelphij				width += attr_ewidth(prev_attr);
372161478Sdelphij				if (from + len < curr)
373161478Sdelphij					width += attr_swidth(next_attr);
37489019Sps			}
37589019Sps		}
37689019Sps
377161478Sdelphij		if (shift - shifted < width)
378161478Sdelphij			break;
379161478Sdelphij		from += len;
380161478Sdelphij		shifted += width;
381161478Sdelphij		if (shifted < 0)
382161478Sdelphij			shifted = 0;
38360786Sps	}
384161478Sdelphij	while (from < curr)
38560786Sps	{
386161478Sdelphij		linebuf[to] = linebuf[from];
387161478Sdelphij		attr[to++] = attr[from++];
38860786Sps	}
389161478Sdelphij	curr = to;
390161478Sdelphij	column -= shifted;
391161478Sdelphij	cshift += shifted;
39260786Sps}
39360786Sps
39460786Sps/*
395161478Sdelphij *
39660786Sps */
397161478Sdelphij	public void
398161478Sdelphijpshift_all()
39960786Sps{
400161478Sdelphij	pshift(column);
40160786Sps}
40260786Sps
40360786Sps/*
40460786Sps * Return the printing width of the start (enter) sequence
40560786Sps * for a given character attribute.
40660786Sps */
40760786Sps	static int
40860786Spsattr_swidth(a)
40960786Sps	int a;
41060786Sps{
411161478Sdelphij	int w = 0;
412161478Sdelphij
413161478Sdelphij	a = apply_at_specials(a);
414161478Sdelphij
415161478Sdelphij	if (a & AT_UNDERLINE)
416161478Sdelphij		w += ul_s_width;
417161478Sdelphij	if (a & AT_BOLD)
418161478Sdelphij		w += bo_s_width;
419161478Sdelphij	if (a & AT_BLINK)
420161478Sdelphij		w += bl_s_width;
421161478Sdelphij	if (a & AT_STANDOUT)
422161478Sdelphij		w += so_s_width;
423161478Sdelphij
424161478Sdelphij	return w;
42560786Sps}
42660786Sps
42760786Sps/*
42860786Sps * Return the printing width of the end (exit) sequence
42960786Sps * for a given character attribute.
43060786Sps */
43160786Sps	static int
43260786Spsattr_ewidth(a)
43360786Sps	int a;
43460786Sps{
435161478Sdelphij	int w = 0;
436161478Sdelphij
437161478Sdelphij	a = apply_at_specials(a);
438161478Sdelphij
439161478Sdelphij	if (a & AT_UNDERLINE)
440161478Sdelphij		w += ul_e_width;
441161478Sdelphij	if (a & AT_BOLD)
442161478Sdelphij		w += bo_e_width;
443161478Sdelphij	if (a & AT_BLINK)
444161478Sdelphij		w += bl_e_width;
445161478Sdelphij	if (a & AT_STANDOUT)
446161478Sdelphij		w += so_e_width;
447161478Sdelphij
448161478Sdelphij	return w;
44960786Sps}
45060786Sps
45160786Sps/*
45260786Sps * Return the printing width of a given character and attribute,
45360786Sps * if the character were added to the current position in the line buffer.
45460786Sps * Adding a character with a given attribute may cause an enter or exit
45560786Sps * attribute sequence to be inserted, so this must be taken into account.
45660786Sps */
45760786Sps	static int
458161478Sdelphijpwidth(ch, a, prev_ch)
459161478Sdelphij	LWCHAR ch;
46060786Sps	int a;
461161478Sdelphij	LWCHAR prev_ch;
46260786Sps{
463161478Sdelphij	int w;
46460786Sps
465161478Sdelphij	if (ch == '\b')
46660786Sps		/*
467161478Sdelphij		 * Backspace moves backwards one or two positions.
468161478Sdelphij		 * XXX - Incorrect if several '\b' in a row.
46960786Sps		 */
470161478Sdelphij		return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
47160786Sps
472161478Sdelphij	if (!utf_mode || is_ascii_char(ch))
473161478Sdelphij	{
474161478Sdelphij		if (control_char((char)ch))
475161478Sdelphij		{
476161478Sdelphij			/*
477161478Sdelphij			 * Control characters do unpredictable things,
478161478Sdelphij			 * so we don't even try to guess; say it doesn't move.
479161478Sdelphij			 * This can only happen if the -r flag is in effect.
480161478Sdelphij			 */
481161478Sdelphij			return (0);
482161478Sdelphij		}
483161478Sdelphij	} else
484161478Sdelphij	{
485161478Sdelphij		if (is_composing_char(ch) || is_combining_char(prev_ch, ch))
486161478Sdelphij		{
487161478Sdelphij			/*
488161478Sdelphij			 * Composing and combining chars take up no space.
489161478Sdelphij			 *
490161478Sdelphij			 * Some terminals, upon failure to compose a
491161478Sdelphij			 * composing character with the character(s) that
492161478Sdelphij			 * precede(s) it will actually take up one column
493161478Sdelphij			 * for the composing character; there isn't much
494161478Sdelphij			 * we could do short of testing the (complex)
495161478Sdelphij			 * composition process ourselves and printing
496161478Sdelphij			 * a binary representation when it fails.
497161478Sdelphij			 */
498161478Sdelphij			return (0);
499161478Sdelphij		}
500161478Sdelphij	}
50160786Sps
50260786Sps	/*
503161478Sdelphij	 * Other characters take one or two columns,
50460786Sps	 * plus the width of any attribute enter/exit sequence.
50560786Sps	 */
50660786Sps	w = 1;
507161478Sdelphij	if (is_wide_char(ch))
508161478Sdelphij		w++;
509161478Sdelphij	if (curr > 0 && !is_at_equiv(attr[curr-1], a))
51060786Sps		w += attr_ewidth(attr[curr-1]);
511161478Sdelphij	if ((apply_at_specials(a) != AT_NORMAL) &&
512161478Sdelphij	    (curr == 0 || !is_at_equiv(attr[curr-1], a)))
51360786Sps		w += attr_swidth(a);
51460786Sps	return (w);
51560786Sps}
51660786Sps
51760786Sps/*
518161478Sdelphij * Delete to the previous base character in the line buffer.
519161478Sdelphij * Return 1 if one is found.
52060786Sps */
521161478Sdelphij	static int
52260786Spsbackc()
52360786Sps{
524161478Sdelphij	LWCHAR prev_ch;
525161478Sdelphij	char *p = linebuf + curr;
526161478Sdelphij	LWCHAR ch = step_char(&p, -1, linebuf + lmargin);
527161478Sdelphij	int width;
528161478Sdelphij
529161478Sdelphij	/* This assumes that there is no '\b' in linebuf.  */
530161478Sdelphij	while (   curr > lmargin
531161478Sdelphij	       && column > lmargin
532161478Sdelphij	       && (!(attr[curr - 1] & (AT_ANSI|AT_BINARY))))
533161478Sdelphij	{
534294286Sdelphij		curr = (int) (p - linebuf);
535161478Sdelphij		prev_ch = step_char(&p, -1, linebuf + lmargin);
536161478Sdelphij		width = pwidth(ch, attr[curr], prev_ch);
537161478Sdelphij		column -= width;
538161478Sdelphij		if (width > 0)
539161478Sdelphij			return 1;
540161478Sdelphij		ch = prev_ch;
541161478Sdelphij	}
542161478Sdelphij
543161478Sdelphij	return 0;
54460786Sps}
54560786Sps
54660786Sps/*
54760786Sps * Are we currently within a recognized ANSI escape sequence?
54860786Sps */
54960786Sps	static int
55060786Spsin_ansi_esc_seq()
55160786Sps{
552161478Sdelphij	char *p;
55360786Sps
55460786Sps	/*
55560786Sps	 * Search backwards for either an ESC (which means we ARE in a seq);
55660786Sps	 * or an end char (which means we're NOT in a seq).
55760786Sps	 */
558161478Sdelphij	for (p = &linebuf[curr];  p > linebuf; )
55960786Sps	{
560161478Sdelphij		LWCHAR ch = step_char(&p, -1, linebuf);
561172471Sdelphij		if (IS_CSI_START(ch))
56260786Sps			return (1);
563161478Sdelphij		if (!is_ansi_middle(ch))
56460786Sps			return (0);
56560786Sps	}
56660786Sps	return (0);
56760786Sps}
56860786Sps
56960786Sps/*
57089019Sps * Is a character the end of an ANSI escape sequence?
57189019Sps */
57289019Sps	public int
573161478Sdelphijis_ansi_end(ch)
574161478Sdelphij	LWCHAR ch;
57589019Sps{
576161478Sdelphij	if (!is_ascii_char(ch))
577161478Sdelphij		return (0);
578161478Sdelphij	return (strchr(end_ansi_chars, (char) ch) != NULL);
57989019Sps}
58089019Sps
58189019Sps/*
582330571Sdelphij * Can a char appear in an ANSI escape sequence, before the end char?
583161478Sdelphij */
584161478Sdelphij	public int
585161478Sdelphijis_ansi_middle(ch)
586161478Sdelphij	LWCHAR ch;
587161478Sdelphij{
588161478Sdelphij	if (!is_ascii_char(ch))
589161478Sdelphij		return (0);
590161478Sdelphij	if (is_ansi_end(ch))
591161478Sdelphij		return (0);
592161478Sdelphij	return (strchr(mid_ansi_chars, (char) ch) != NULL);
593161478Sdelphij}
594161478Sdelphij
595161478Sdelphij/*
596330571Sdelphij * Skip past an ANSI escape sequence.
597330571Sdelphij * pp is initially positioned just after the CSI_START char.
598330571Sdelphij */
599330571Sdelphij	public void
600330571Sdelphijskip_ansi(pp, limit)
601330571Sdelphij	char **pp;
602330571Sdelphij	constant char *limit;
603330571Sdelphij{
604330571Sdelphij	LWCHAR c;
605330571Sdelphij	do {
606330571Sdelphij		c = step_char(pp, +1, limit);
607330571Sdelphij	} while (*pp < limit && is_ansi_middle(c));
608330571Sdelphij	/* Note that we discard final char, for which is_ansi_middle is false. */
609330571Sdelphij}
610330571Sdelphij
611330571Sdelphij
612330571Sdelphij/*
61360786Sps * Append a character and attribute to the line buffer.
61460786Sps */
615161478Sdelphij#define	STORE_CHAR(ch,a,rep,pos) \
616161478Sdelphij	do { \
617161478Sdelphij		if (store_char((ch),(a),(rep),(pos))) return (1); \
618161478Sdelphij	} while (0)
61989019Sps
62060786Sps	static int
621161478Sdelphijstore_char(ch, a, rep, pos)
622161478Sdelphij	LWCHAR ch;
62360786Sps	int a;
624161478Sdelphij	char *rep;
62560786Sps	POSITION pos;
62660786Sps{
627161478Sdelphij	int w;
628161478Sdelphij	int replen;
629161478Sdelphij	char cs;
63060786Sps
631161478Sdelphij	w = (a & (AT_UNDERLINE|AT_BOLD));	/* Pre-use w.  */
632161478Sdelphij	if (w != AT_NORMAL)
633161478Sdelphij		last_overstrike = w;
634161478Sdelphij
63560786Sps#if HILITE_SEARCH
63689019Sps	{
637161478Sdelphij		int matches;
638161478Sdelphij		if (is_hilited(pos, pos+1, 0, &matches))
639161478Sdelphij		{
640161478Sdelphij			/*
641161478Sdelphij			 * This character should be highlighted.
642161478Sdelphij			 * Override the attribute passed in.
643161478Sdelphij			 */
644161478Sdelphij			if (a != AT_ANSI)
645237613Sdelphij			{
646237613Sdelphij				if (highest_hilite != NULL_POSITION &&
647237613Sdelphij				    pos > highest_hilite)
648237613Sdelphij				    	highest_hilite = pos;
649161478Sdelphij				a |= AT_HILITE;
650237613Sdelphij			}
651161478Sdelphij		}
65289019Sps	}
65360786Sps#endif
654161478Sdelphij
65560786Sps	if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
656161478Sdelphij	{
657161478Sdelphij		if (!is_ansi_end(ch) && !is_ansi_middle(ch)) {
658161478Sdelphij			/* Remove whole unrecognized sequence.  */
659191930Sdelphij			char *p = &linebuf[curr];
660191930Sdelphij			LWCHAR bch;
661191930Sdelphij			do {
662191930Sdelphij				bch = step_char(&p, -1, linebuf);
663191930Sdelphij			} while (p > linebuf && !IS_CSI_START(bch));
664294286Sdelphij			curr = (int) (p - linebuf);
665161478Sdelphij			return 0;
666161478Sdelphij		}
667161478Sdelphij		a = AT_ANSI;	/* Will force re-AT_'ing around it.  */
66860786Sps		w = 0;
669161478Sdelphij	}
670172471Sdelphij	else if (ctldisp == OPT_ONPLUS && IS_CSI_START(ch))
671161478Sdelphij	{
672161478Sdelphij		a = AT_ANSI;	/* Will force re-AT_'ing around it.  */
673161478Sdelphij		w = 0;
674161478Sdelphij	}
67560786Sps	else
676161478Sdelphij	{
677161478Sdelphij		char *p = &linebuf[curr];
678161478Sdelphij		LWCHAR prev_ch = step_char(&p, -1, linebuf);
679161478Sdelphij		w = pwidth(ch, a, prev_ch);
680161478Sdelphij	}
681161478Sdelphij
68260786Sps	if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
68360786Sps		/*
68460786Sps		 * Won't fit on screen.
68560786Sps		 */
68660786Sps		return (1);
68760786Sps
688161478Sdelphij	if (rep == NULL)
68989019Sps	{
690161478Sdelphij		cs = (char) ch;
691161478Sdelphij		rep = &cs;
692161478Sdelphij		replen = 1;
693161478Sdelphij	} else
694161478Sdelphij	{
695161478Sdelphij		replen = utf_len(rep[0]);
696161478Sdelphij	}
697161478Sdelphij	if (curr + replen >= size_linebuf-6)
698161478Sdelphij	{
69960786Sps		/*
70060786Sps		 * Won't fit in line buffer.
70189019Sps		 * Try to expand it.
70260786Sps		 */
70389019Sps		if (expand_linebuf())
70489019Sps			return (1);
70589019Sps	}
70660786Sps
707330571Sdelphij	if (column > right_column && w > 0)
708330571Sdelphij	{
709330571Sdelphij		right_column = column;
710330571Sdelphij		right_curr = curr;
711330571Sdelphij	}
712330571Sdelphij
713161478Sdelphij	while (replen-- > 0)
71460786Sps	{
715330571Sdelphij		add_linebuf(*rep++, a, 0);
71660786Sps	}
71760786Sps	column += w;
71860786Sps	return (0);
71960786Sps}
72060786Sps
72160786Sps/*
72289019Sps * Append a tab to the line buffer.
72389019Sps * Store spaces to represent the tab.
72489019Sps */
72589019Sps#define	STORE_TAB(a,pos) \
72689019Sps	do { if (store_tab((a),(pos))) return (1); } while (0)
72789019Sps
72889019Sps	static int
72989019Spsstore_tab(attr, pos)
73089019Sps	int attr;
73189019Sps	POSITION pos;
73289019Sps{
73389019Sps	int to_tab = column + cshift - lmargin;
73489019Sps	int i;
73589019Sps
73689019Sps	if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1])
73789019Sps		to_tab = tabdefault -
73889019Sps		     ((to_tab - tabstops[ntabstops-1]) % tabdefault);
73989019Sps	else
74089019Sps	{
74189019Sps		for (i = ntabstops - 2;  i >= 0;  i--)
74289019Sps			if (to_tab >= tabstops[i])
74389019Sps				break;
74489019Sps		to_tab = tabstops[i+1] - to_tab;
74589019Sps	}
74689019Sps
747161478Sdelphij	if (column + to_tab - 1 + pwidth(' ', attr, 0) + attr_ewidth(attr) > sc_width)
748161478Sdelphij		return 1;
749161478Sdelphij
75089019Sps	do {
751161478Sdelphij		STORE_CHAR(' ', attr, " ", pos);
75289019Sps	} while (--to_tab > 0);
75389019Sps	return 0;
75489019Sps}
75589019Sps
756161478Sdelphij#define STORE_PRCHAR(c, pos) \
757161478Sdelphij	do { if (store_prchar((c), (pos))) return 1; } while (0)
758161478Sdelphij
759161478Sdelphij	static int
760161478Sdelphijstore_prchar(c, pos)
761294286Sdelphij	LWCHAR c;
762161478Sdelphij	POSITION pos;
763161478Sdelphij{
764161478Sdelphij	char *s;
765161478Sdelphij
766161478Sdelphij	/*
767161478Sdelphij	 * Convert to printable representation.
768161478Sdelphij	 */
769161478Sdelphij	s = prchar(c);
770161478Sdelphij
771161478Sdelphij	/*
772161478Sdelphij	 * Make sure we can get the entire representation
773161478Sdelphij	 * of the character on this line.
774161478Sdelphij	 */
775161478Sdelphij	if (column + (int) strlen(s) - 1 +
776161478Sdelphij            pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
777161478Sdelphij		return 1;
778161478Sdelphij
779161478Sdelphij	for ( ;  *s != 0;  s++)
780161478Sdelphij		STORE_CHAR(*s, AT_BINARY, NULL, pos);
781161478Sdelphij
782161478Sdelphij	return 0;
783161478Sdelphij}
784161478Sdelphij
785161478Sdelphij	static int
786161478Sdelphijflush_mbc_buf(pos)
787161478Sdelphij	POSITION pos;
788161478Sdelphij{
789161478Sdelphij	int i;
790161478Sdelphij
791161478Sdelphij	for (i = 0; i < mbc_buf_index; i++)
792161478Sdelphij		if (store_prchar(mbc_buf[i], pos))
793161478Sdelphij			return mbc_buf_index - i;
794161478Sdelphij
795161478Sdelphij	return 0;
796161478Sdelphij}
797161478Sdelphij
79889019Sps/*
79960786Sps * Append a character to the line buffer.
80060786Sps * Expand tabs into spaces, handle underlining, boldfacing, etc.
80160786Sps * Returns 0 if ok, 1 if couldn't fit in buffer.
80260786Sps */
80360786Sps	public int
80460786Spspappend(c, pos)
805294286Sdelphij	unsigned char c;
80660786Sps	POSITION pos;
80760786Sps{
80860786Sps	int r;
80960786Sps
81060786Sps	if (pendc)
81160786Sps	{
812294286Sdelphij		if (c == '\r' && pendc == '\r')
813294286Sdelphij			return (0);
814161478Sdelphij		if (do_append(pendc, NULL, pendpos))
81560786Sps			/*
81660786Sps			 * Oops.  We've probably lost the char which
81760786Sps			 * was in pendc, since caller won't back up.
81860786Sps			 */
81960786Sps			return (1);
82060786Sps		pendc = '\0';
82160786Sps	}
82260786Sps
82360786Sps	if (c == '\r' && bs_mode == BS_SPECIAL)
82460786Sps	{
825161478Sdelphij		if (mbc_buf_len > 0)  /* utf_mode must be on. */
826161478Sdelphij		{
827161478Sdelphij			/* Flush incomplete (truncated) sequence. */
828161478Sdelphij			r = flush_mbc_buf(mbc_pos);
829161478Sdelphij			mbc_buf_index = r + 1;
830161478Sdelphij			mbc_buf_len = 0;
831161478Sdelphij			if (r)
832161478Sdelphij				return (mbc_buf_index);
833161478Sdelphij		}
834161478Sdelphij
83560786Sps		/*
83660786Sps		 * Don't put the CR into the buffer until we see
83760786Sps		 * the next char.  If the next char is a newline,
83860786Sps		 * discard the CR.
83960786Sps		 */
84060786Sps		pendc = c;
84160786Sps		pendpos = pos;
84260786Sps		return (0);
84360786Sps	}
84460786Sps
845161478Sdelphij	if (!utf_mode)
846161478Sdelphij	{
847294286Sdelphij		r = do_append(c, NULL, pos);
848161478Sdelphij	} else
849161478Sdelphij	{
850161478Sdelphij		/* Perform strict validation in all possible cases. */
851161478Sdelphij		if (mbc_buf_len == 0)
852161478Sdelphij		{
853161478Sdelphij		retry:
854161478Sdelphij			mbc_buf_index = 1;
855161478Sdelphij			*mbc_buf = c;
856161478Sdelphij			if (IS_ASCII_OCTET(c))
857294286Sdelphij				r = do_append(c, NULL, pos);
858161478Sdelphij			else if (IS_UTF8_LEAD(c))
859161478Sdelphij			{
860161478Sdelphij				mbc_buf_len = utf_len(c);
861161478Sdelphij				mbc_pos = pos;
862161478Sdelphij				return (0);
863161478Sdelphij			} else
864161478Sdelphij				/* UTF8_INVALID or stray UTF8_TRAIL */
865161478Sdelphij				r = flush_mbc_buf(pos);
866161478Sdelphij		} else if (IS_UTF8_TRAIL(c))
867161478Sdelphij		{
868161478Sdelphij			mbc_buf[mbc_buf_index++] = c;
869161478Sdelphij			if (mbc_buf_index < mbc_buf_len)
870161478Sdelphij				return (0);
871294286Sdelphij			if (is_utf8_well_formed(mbc_buf, mbc_buf_index))
872161478Sdelphij				r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos);
873161478Sdelphij			else
874161478Sdelphij				/* Complete, but not shortest form, sequence. */
875161478Sdelphij				mbc_buf_index = r = flush_mbc_buf(mbc_pos);
876161478Sdelphij			mbc_buf_len = 0;
877161478Sdelphij		} else
878161478Sdelphij		{
879161478Sdelphij			/* Flush incomplete (truncated) sequence.  */
880161478Sdelphij			r = flush_mbc_buf(mbc_pos);
881161478Sdelphij			mbc_buf_index = r + 1;
882161478Sdelphij			mbc_buf_len = 0;
883161478Sdelphij			/* Handle new char.  */
884161478Sdelphij			if (!r)
885161478Sdelphij				goto retry;
886161478Sdelphij 		}
887161478Sdelphij	}
888161478Sdelphij
88960786Sps	/*
89060786Sps	 * If we need to shift the line, do it.
89160786Sps	 * But wait until we get to at least the middle of the screen,
89260786Sps	 * so shifting it doesn't affect the chars we're currently
89360786Sps	 * pappending.  (Bold & underline can get messed up otherwise.)
89460786Sps	 */
89560786Sps	if (cshift < hshift && column > sc_width / 2)
89689019Sps	{
89789019Sps		linebuf[curr] = '\0';
89860786Sps		pshift(hshift - cshift);
89989019Sps	}
900161478Sdelphij	if (r)
901161478Sdelphij	{
902161478Sdelphij		/* How many chars should caller back up? */
903161478Sdelphij		r = (!utf_mode) ? 1 : mbc_buf_index;
904161478Sdelphij	}
90560786Sps	return (r);
90660786Sps}
90760786Sps
90860786Sps	static int
909161478Sdelphijdo_append(ch, rep, pos)
910161478Sdelphij	LWCHAR ch;
911161478Sdelphij	char *rep;
91260786Sps	POSITION pos;
91360786Sps{
914330571Sdelphij	int a;
915161478Sdelphij	LWCHAR prev_ch;
91660786Sps
917161478Sdelphij	a = AT_NORMAL;
91860786Sps
919161478Sdelphij	if (ch == '\b')
92060786Sps	{
921161478Sdelphij		if (bs_mode == BS_CONTROL)
92260786Sps			goto do_control_char;
923161478Sdelphij
924161478Sdelphij		/*
925161478Sdelphij		 * A better test is needed here so we don't
926161478Sdelphij		 * backspace over part of the printed
927161478Sdelphij		 * representation of a binary character.
928161478Sdelphij		 */
929161478Sdelphij		if (   curr <= lmargin
930161478Sdelphij		    || column <= lmargin
931161478Sdelphij		    || (attr[curr - 1] & (AT_ANSI|AT_BINARY)))
932161478Sdelphij			STORE_PRCHAR('\b', pos);
933161478Sdelphij		else if (bs_mode == BS_NORMAL)
934161478Sdelphij			STORE_CHAR(ch, AT_NORMAL, NULL, pos);
935161478Sdelphij		else if (bs_mode == BS_SPECIAL)
936161478Sdelphij			overstrike = backc();
937161478Sdelphij
938161478Sdelphij		return 0;
939161478Sdelphij	}
940161478Sdelphij
941161478Sdelphij	if (overstrike > 0)
94260786Sps	{
94360786Sps		/*
94460786Sps		 * Overstrike the character at the current position
94560786Sps		 * in the line buffer.  This will cause either
94660786Sps		 * underline (if a "_" is overstruck),
94760786Sps		 * bold (if an identical character is overstruck),
94860786Sps		 * or just deletion of the character in the buffer.
94960786Sps		 */
950161478Sdelphij		overstrike = utf_mode ? -1 : 0;
951294286Sdelphij		if (utf_mode)
952294286Sdelphij		{
953294286Sdelphij			/* To be correct, this must be a base character.  */
954294286Sdelphij			prev_ch = get_wchar(linebuf + curr);
955294286Sdelphij		} else
956294286Sdelphij		{
957294286Sdelphij			prev_ch = (unsigned char) linebuf[curr];
958294286Sdelphij		}
959161478Sdelphij		a = attr[curr];
960161478Sdelphij		if (ch == prev_ch)
96189019Sps		{
962128345Stjr			/*
963128345Stjr			 * Overstriking a char with itself means make it bold.
964128345Stjr			 * But overstriking an underscore with itself is
965128345Stjr			 * ambiguous.  It could mean make it bold, or
966128345Stjr			 * it could mean make it underlined.
967128345Stjr			 * Use the previous overstrike to resolve it.
968128345Stjr			 */
969161478Sdelphij			if (ch == '_')
97089019Sps			{
971161478Sdelphij				if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL)
972161478Sdelphij					a |= (AT_BOLD|AT_UNDERLINE);
973161478Sdelphij				else if (last_overstrike != AT_NORMAL)
974161478Sdelphij					a |= last_overstrike;
975161478Sdelphij				else
976161478Sdelphij					a |= AT_BOLD;
977161478Sdelphij			} else
978161478Sdelphij				a |= AT_BOLD;
979161478Sdelphij		} else if (ch == '_')
98089019Sps		{
981161478Sdelphij			a |= AT_UNDERLINE;
982161478Sdelphij			ch = prev_ch;
983161478Sdelphij			rep = linebuf + curr;
984161478Sdelphij		} else if (prev_ch == '_')
985161478Sdelphij		{
986161478Sdelphij			a |= AT_UNDERLINE;
987161478Sdelphij		}
988161478Sdelphij		/* Else we replace prev_ch, but we keep its attributes.  */
989161478Sdelphij	} else if (overstrike < 0)
990161478Sdelphij	{
991161478Sdelphij		if (   is_composing_char(ch)
992161478Sdelphij		    || is_combining_char(get_wchar(linebuf + curr), ch))
993161478Sdelphij			/* Continuation of the same overstrike.  */
994161478Sdelphij			a = last_overstrike;
99560786Sps		else
996161478Sdelphij			overstrike = 0;
997161478Sdelphij	}
998161478Sdelphij
999161478Sdelphij	if (ch == '\t')
100060786Sps	{
100160786Sps		/*
100260786Sps		 * Expand a tab into spaces.
100360786Sps		 */
100460786Sps		switch (bs_mode)
100560786Sps		{
100660786Sps		case BS_CONTROL:
100760786Sps			goto do_control_char;
100860786Sps		case BS_NORMAL:
100960786Sps		case BS_SPECIAL:
1010161478Sdelphij			STORE_TAB(a, pos);
101160786Sps			break;
101260786Sps		}
1013161478Sdelphij	} else if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch))
101460786Sps	{
101560786Sps	do_control_char:
1016172471Sdelphij		if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && IS_CSI_START(ch)))
101760786Sps		{
101860786Sps			/*
101960786Sps			 * Output as a normal character.
102060786Sps			 */
1021161478Sdelphij			STORE_CHAR(ch, AT_NORMAL, rep, pos);
102260786Sps		} else
102360786Sps		{
1024161478Sdelphij			STORE_PRCHAR((char) ch, pos);
1025161478Sdelphij		}
1026161478Sdelphij	} else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch))
1027161478Sdelphij	{
1028161478Sdelphij		char *s;
102960786Sps
1030161478Sdelphij		s = prutfchar(ch);
103160786Sps
1032161478Sdelphij		if (column + (int) strlen(s) - 1 +
1033161478Sdelphij		    pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
1034161478Sdelphij			return (1);
1035161478Sdelphij
1036161478Sdelphij		for ( ;  *s != 0;  s++)
1037161478Sdelphij			STORE_CHAR(*s, AT_BINARY, NULL, pos);
1038161478Sdelphij 	} else
103960786Sps	{
1040161478Sdelphij		STORE_CHAR(ch, a, rep, pos);
104160786Sps	}
1042161478Sdelphij 	return (0);
1043161478Sdelphij}
104460786Sps
1045161478Sdelphij/*
1046161478Sdelphij *
1047161478Sdelphij */
1048161478Sdelphij	public int
1049161478Sdelphijpflushmbc()
1050161478Sdelphij{
1051161478Sdelphij	int r = 0;
1052161478Sdelphij
1053161478Sdelphij	if (mbc_buf_len > 0)
1054161478Sdelphij	{
1055161478Sdelphij		/* Flush incomplete (truncated) sequence.  */
1056161478Sdelphij		r = flush_mbc_buf(mbc_pos);
1057161478Sdelphij		mbc_buf_len = 0;
1058161478Sdelphij	}
1059161478Sdelphij	return r;
106060786Sps}
106160786Sps
106260786Sps/*
1063330571Sdelphij * Switch to normal attribute at end of line.
1064330571Sdelphij */
1065330571Sdelphij	static void
1066330571Sdelphijadd_attr_normal()
1067330571Sdelphij{
1068330571Sdelphij	char *p = "\033[m";
1069330571Sdelphij
1070330571Sdelphij	if (ctldisp != OPT_ONPLUS || !is_ansi_end('m'))
1071330571Sdelphij		return;
1072330571Sdelphij	for ( ;  *p != '\0';  p++)
1073330571Sdelphij		add_linebuf(*p, AT_ANSI, 0);
1074330571Sdelphij}
1075330571Sdelphij
1076330571Sdelphij/*
107760786Sps * Terminate the line in the line buffer.
107860786Sps */
107960786Sps	public void
1080330571Sdelphijpdone(endline, chopped, forw)
108160786Sps	int endline;
1082330571Sdelphij	int chopped;
1083195941Sdelphij	int forw;
108460786Sps{
1085161478Sdelphij	(void) pflushmbc();
1086161478Sdelphij
108760786Sps	if (pendc && (pendc != '\r' || !endline))
108860786Sps		/*
108960786Sps		 * If we had a pending character, put it in the buffer.
109060786Sps		 * But discard a pending CR if we are at end of line
109160786Sps		 * (that is, discard the CR in a CR/LF sequence).
109260786Sps		 */
1093161478Sdelphij		(void) do_append(pendc, NULL, pendpos);
109460786Sps
109560786Sps	/*
109660786Sps	 * Make sure we've shifted the line, if we need to.
109760786Sps	 */
109860786Sps	if (cshift < hshift)
109960786Sps		pshift(hshift - cshift);
110060786Sps
1101330571Sdelphij	if (chopped && rscroll_char)
1102161478Sdelphij	{
1103330571Sdelphij		/*
1104330571Sdelphij		 * Display the right scrolling char.
1105330571Sdelphij		 * If we've already filled the rightmost screen char
1106330571Sdelphij		 * (in the buffer), overwrite it.
1107330571Sdelphij		 */
1108330571Sdelphij		if (column >= sc_width)
1109161478Sdelphij		{
1110330571Sdelphij			/* We've already written in the rightmost char. */
1111330571Sdelphij			column = right_column;
1112330571Sdelphij			curr = right_curr;
1113161478Sdelphij		}
1114330571Sdelphij		add_attr_normal();
1115330571Sdelphij		while (column < sc_width-1)
1116330571Sdelphij		{
1117330571Sdelphij			/*
1118330571Sdelphij			 * Space to last (rightmost) char on screen.
1119330571Sdelphij			 * This may be necessary if the char we overwrote
1120330571Sdelphij			 * was double-width.
1121330571Sdelphij			 */
1122330571Sdelphij			add_linebuf(' ', AT_NORMAL, 1);
1123330571Sdelphij		}
1124330571Sdelphij		/* Print rscroll char. It must be single-width. */
1125330571Sdelphij		add_linebuf(rscroll_char, rscroll_attr, 1);
1126330571Sdelphij	} else
1127330571Sdelphij	{
1128330571Sdelphij		add_attr_normal();
1129161478Sdelphij	}
1130161478Sdelphij
113160786Sps	/*
113260786Sps	 * Add a newline if necessary,
113360786Sps	 * and append a '\0' to the end of the line.
1134170259Sdelphij	 * We output a newline if we're not at the right edge of the screen,
1135170259Sdelphij	 * or if the terminal doesn't auto wrap,
1136170259Sdelphij	 * or if this is really the end of the line AND the terminal ignores
1137170259Sdelphij	 * a newline at the right edge.
1138170259Sdelphij	 * (In the last case we don't want to output a newline if the terminal
1139170259Sdelphij	 * doesn't ignore it since that would produce an extra blank line.
1140170259Sdelphij	 * But we do want to output a newline if the terminal ignores it in case
1141170259Sdelphij	 * the next line is blank.  In that case the single newline output for
1142170259Sdelphij	 * that blank line would be ignored!)
114360786Sps	 */
1144191930Sdelphij	if (column < sc_width || !auto_wrap || (endline && ignaw) || ctldisp == OPT_ON)
114560786Sps	{
1146330571Sdelphij		add_linebuf('\n', AT_NORMAL, 0);
1147173685Sdelphij	}
1148195941Sdelphij	else if (ignaw && column >= sc_width && forw)
1149173685Sdelphij	{
1150173685Sdelphij		/*
1151191930Sdelphij		 * Terminals with "ignaw" don't wrap until they *really* need
1152191930Sdelphij		 * to, i.e. when the character *after* the last one to fit on a
1153191930Sdelphij		 * line is output. But they are too hard to deal with when they
1154191930Sdelphij		 * get in the state where a full screen width of characters
1155191930Sdelphij		 * have been output but the cursor is sitting on the right edge
1156191930Sdelphij		 * instead of at the start of the next line.
1157195941Sdelphij		 * So we nudge them into wrapping by outputting a space
1158195941Sdelphij		 * character plus a backspace.  But do this only if moving
1159195941Sdelphij		 * forward; if we're moving backward and drawing this line at
1160195941Sdelphij		 * the top of the screen, the space would overwrite the first
1161195941Sdelphij		 * char on the next line.  We don't need to do this "nudge"
1162195941Sdelphij		 * at the top of the screen anyway.
1163173685Sdelphij		 */
1164330571Sdelphij		add_linebuf(' ', AT_NORMAL, 1);
1165330571Sdelphij		add_linebuf('\b', AT_NORMAL, -1);
116660786Sps	}
1167330571Sdelphij	set_linebuf(curr, '\0', AT_NORMAL);
1168191930Sdelphij}
116989019Sps
1170191930Sdelphij/*
1171191930Sdelphij *
1172191930Sdelphij */
1173191930Sdelphij	public void
1174191930Sdelphijset_status_col(c)
1175191930Sdelphij	char c;
1176191930Sdelphij{
1177330571Sdelphij	set_linebuf(0, c, AT_NORMAL|AT_HILITE);
117860786Sps}
117960786Sps
118060786Sps/*
118160786Sps * Get a character from the current line.
118260786Sps * Return the character as the function return value,
118360786Sps * and the character attribute in *ap.
118460786Sps */
118560786Sps	public int
118660786Spsgline(i, ap)
1187330571Sdelphij	int i;
1188330571Sdelphij	int *ap;
118960786Sps{
119060786Sps	if (is_null_line)
119160786Sps	{
119260786Sps		/*
119360786Sps		 * If there is no current line, we pretend the line is
119460786Sps		 * either "~" or "", depending on the "twiddle" flag.
119560786Sps		 */
1196161478Sdelphij		if (twiddle)
1197161478Sdelphij		{
1198161478Sdelphij			if (i == 0)
1199161478Sdelphij			{
1200161478Sdelphij				*ap = AT_BOLD;
1201161478Sdelphij				return '~';
1202161478Sdelphij			}
1203161478Sdelphij			--i;
1204161478Sdelphij		}
1205161478Sdelphij		/* Make sure we're back to AT_NORMAL before the '\n'.  */
1206161478Sdelphij		*ap = AT_NORMAL;
1207161478Sdelphij		return i ? '\0' : '\n';
120860786Sps	}
120960786Sps
121060786Sps	*ap = attr[i];
1211161478Sdelphij	return (linebuf[i] & 0xFF);
121260786Sps}
121360786Sps
121460786Sps/*
121560786Sps * Indicate that there is no current line.
121660786Sps */
121760786Sps	public void
121860786Spsnull_line()
121960786Sps{
122060786Sps	is_null_line = 1;
122160786Sps	cshift = 0;
122260786Sps}
122360786Sps
122460786Sps/*
122560786Sps * Analogous to forw_line(), but deals with "raw lines":
122660786Sps * lines which are not split for screen width.
122760786Sps * {{ This is supposed to be more efficient than forw_line(). }}
122860786Sps */
122960786Sps	public POSITION
1230170259Sdelphijforw_raw_line(curr_pos, linep, line_lenp)
123160786Sps	POSITION curr_pos;
123260786Sps	char **linep;
1233170259Sdelphij	int *line_lenp;
123460786Sps{
1235330571Sdelphij	int n;
1236330571Sdelphij	int c;
123760786Sps	POSITION new_pos;
123860786Sps
123960786Sps	if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
124060786Sps		(c = ch_forw_get()) == EOI)
124160786Sps		return (NULL_POSITION);
124260786Sps
124389019Sps	n = 0;
124460786Sps	for (;;)
124560786Sps	{
1246161478Sdelphij		if (c == '\n' || c == EOI || ABORT_SIGS())
124760786Sps		{
124860786Sps			new_pos = ch_tell();
124960786Sps			break;
125060786Sps		}
125189019Sps		if (n >= size_linebuf-1)
125260786Sps		{
125389019Sps			if (expand_linebuf())
125489019Sps			{
125589019Sps				/*
125689019Sps				 * Overflowed the input buffer.
125789019Sps				 * Pretend the line ended here.
125889019Sps				 */
125989019Sps				new_pos = ch_tell() - 1;
126089019Sps				break;
126189019Sps			}
126260786Sps		}
126389019Sps		linebuf[n++] = c;
126460786Sps		c = ch_forw_get();
126560786Sps	}
126689019Sps	linebuf[n] = '\0';
126760786Sps	if (linep != NULL)
126860786Sps		*linep = linebuf;
1269170259Sdelphij	if (line_lenp != NULL)
1270170259Sdelphij		*line_lenp = n;
127160786Sps	return (new_pos);
127260786Sps}
127360786Sps
127460786Sps/*
127560786Sps * Analogous to back_line(), but deals with "raw lines".
127660786Sps * {{ This is supposed to be more efficient than back_line(). }}
127760786Sps */
127860786Sps	public POSITION
1279170259Sdelphijback_raw_line(curr_pos, linep, line_lenp)
128060786Sps	POSITION curr_pos;
128160786Sps	char **linep;
1282170259Sdelphij	int *line_lenp;
128360786Sps{
1284330571Sdelphij	int n;
1285330571Sdelphij	int c;
128660786Sps	POSITION new_pos;
128760786Sps
128860786Sps	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
128960786Sps		ch_seek(curr_pos-1))
129060786Sps		return (NULL_POSITION);
129160786Sps
129289019Sps	n = size_linebuf;
129389019Sps	linebuf[--n] = '\0';
129460786Sps	for (;;)
129560786Sps	{
129660786Sps		c = ch_back_get();
1297161478Sdelphij		if (c == '\n' || ABORT_SIGS())
129860786Sps		{
129960786Sps			/*
130060786Sps			 * This is the newline ending the previous line.
130160786Sps			 * We have hit the beginning of the line.
130260786Sps			 */
130360786Sps			new_pos = ch_tell() + 1;
130460786Sps			break;
130560786Sps		}
130660786Sps		if (c == EOI)
130760786Sps		{
130860786Sps			/*
130960786Sps			 * We have hit the beginning of the file.
131060786Sps			 * This must be the first line in the file.
131160786Sps			 * This must, of course, be the beginning of the line.
131260786Sps			 */
131360786Sps			new_pos = ch_zero();
131460786Sps			break;
131560786Sps		}
131689019Sps		if (n <= 0)
131760786Sps		{
131889019Sps			int old_size_linebuf = size_linebuf;
131989019Sps			char *fm;
132089019Sps			char *to;
132189019Sps			if (expand_linebuf())
132289019Sps			{
132389019Sps				/*
132489019Sps				 * Overflowed the input buffer.
132589019Sps				 * Pretend the line ended here.
132689019Sps				 */
132789019Sps				new_pos = ch_tell() + 1;
132889019Sps				break;
132989019Sps			}
133060786Sps			/*
133189019Sps			 * Shift the data to the end of the new linebuf.
133260786Sps			 */
1333149487Stjr			for (fm = linebuf + old_size_linebuf - 1,
1334149487Stjr			      to = linebuf + size_linebuf - 1;
133589019Sps			     fm >= linebuf;  fm--, to--)
133689019Sps				*to = *fm;
133789019Sps			n = size_linebuf - old_size_linebuf;
133860786Sps		}
133989019Sps		linebuf[--n] = c;
134060786Sps	}
134160786Sps	if (linep != NULL)
134289019Sps		*linep = &linebuf[n];
1343170259Sdelphij	if (line_lenp != NULL)
1344170259Sdelphij		*line_lenp = size_linebuf - 1 - n;
134560786Sps	return (new_pos);
134660786Sps}
1347330571Sdelphij
1348330571Sdelphij/*
1349330571Sdelphij * Find the shift necessary to show the end of the longest displayed line.
1350330571Sdelphij */
1351330571Sdelphij	public int
1352330571Sdelphijrrshift()
1353330571Sdelphij{
1354330571Sdelphij	POSITION pos;
1355330571Sdelphij	int save_width;
1356330571Sdelphij	int line;
1357330571Sdelphij	int longest = 0;
1358330571Sdelphij
1359330571Sdelphij	save_width = sc_width;
1360330571Sdelphij	sc_width = INT_MAX;
1361330571Sdelphij	hshift = 0;
1362330571Sdelphij	pos = position(TOP);
1363330571Sdelphij	for (line = 0; line < sc_height && pos != NULL_POSITION; line++)
1364330571Sdelphij	{
1365330571Sdelphij		pos = forw_line(pos);
1366330571Sdelphij		if (column > longest)
1367330571Sdelphij			longest = column;
1368330571Sdelphij	}
1369330571Sdelphij	sc_width = save_width;
1370330571Sdelphij	if (longest < sc_width)
1371330571Sdelphij		return 0;
1372330571Sdelphij	return longest - sc_width;
1373330571Sdelphij}
1374