1149487Stjr/* $FreeBSD$ */
260786Sps/*
3240121Sdelphij * Copyright (C) 1984-2012  Mark Nudelman
460786Sps *
560786Sps * You may distribute under the terms of either the GNU General Public
660786Sps * License or the Less License, as specified in the README file.
760786Sps *
8240121Sdelphij * For more information, see the README file.
960786Sps */
1060786Sps
1160786Sps
1260786Sps/*
1360786Sps * Routines to manipulate the "line buffer".
1460786Sps * The line buffer holds a line of output as it is being built
1560786Sps * in preparation for output to the screen.
1660786Sps */
1760786Sps
1860786Sps#include "less.h"
19161478Sdelphij#include "charset.h"
2060786Sps
21161478Sdelphijstatic char *linebuf = NULL;	/* Buffer which holds the current output line */
2289019Spsstatic char *attr = NULL;	/* Extension of linebuf to hold attributes */
2389019Spspublic int size_linebuf = 0;	/* Size of line buffer (and attr buffer) */
2460786Sps
25161478Sdelphijstatic int cshift;		/* Current left-shift of output line buffer */
2660786Spspublic int hshift;		/* Desired left-shift of output line buffer */
2789019Spspublic int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */
2889019Spspublic int ntabstops = 1;	/* Number of tabstops */
2989019Spspublic int tabdefault = 8;	/* Default repeated tabstops */
30240121Sdelphijpublic POSITION highest_hilite;	/* Pos of last hilite in file found so far */
3160786Sps
3260786Spsstatic int curr;		/* Index into linebuf */
3360786Spsstatic int column;		/* Printable length, accounting for
3460786Sps				   backspaces, etc. */
3560786Spsstatic int overstrike;		/* Next char should overstrike previous char */
36128345Stjrstatic int last_overstrike = AT_NORMAL;
3760786Spsstatic int is_null_line;	/* There is no current line */
3863128Spsstatic int lmargin;		/* Left margin */
3960786Spsstatic char pendc;
4060786Spsstatic POSITION pendpos;
4160786Spsstatic char *end_ansi_chars;
42161478Sdelphijstatic char *mid_ansi_chars;
4360786Sps
44161478Sdelphijstatic int attr_swidth();
45161478Sdelphijstatic int attr_ewidth();
4660786Spsstatic int do_append();
4760786Sps
48161478Sdelphijextern int sigs;
4960786Spsextern int bs_mode;
5060786Spsextern int linenums;
5160786Spsextern int ctldisp;
5260786Spsextern int twiddle;
5360786Spsextern int binattr;
5463128Spsextern int status_col;
5560786Spsextern int auto_wrap, ignaw;
5660786Spsextern int bo_s_width, bo_e_width;
5760786Spsextern int ul_s_width, ul_e_width;
5860786Spsextern int bl_s_width, bl_e_width;
5960786Spsextern int so_s_width, so_e_width;
6060786Spsextern int sc_width, sc_height;
6160786Spsextern int utf_mode;
6263128Spsextern POSITION start_attnpos;
6363128Spsextern POSITION end_attnpos;
6460786Sps
65161478Sdelphijstatic char mbc_buf[MAX_UTF_CHAR_LEN];
66161478Sdelphijstatic int mbc_buf_len = 0;
67161478Sdelphijstatic int mbc_buf_index = 0;
68161478Sdelphijstatic POSITION mbc_pos;
69161478Sdelphij
7060786Sps/*
7160786Sps * Initialize from environment variables.
7260786Sps */
7360786Sps	public void
7460786Spsinit_line()
7560786Sps{
7660786Sps	end_ansi_chars = lgetenv("LESSANSIENDCHARS");
7760786Sps	if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
7860786Sps		end_ansi_chars = "m";
79161478Sdelphij
80161478Sdelphij	mid_ansi_chars = lgetenv("LESSANSIMIDCHARS");
81161478Sdelphij	if (mid_ansi_chars == NULL || *mid_ansi_chars == '\0')
82161478Sdelphij		mid_ansi_chars = "0123456789;[?!\"'#%()*+ ";
83161478Sdelphij
8489019Sps	linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
8589019Sps	attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
8689019Sps	size_linebuf = LINEBUF_SIZE;
8760786Sps}
8860786Sps
8960786Sps/*
9089019Sps * Expand the line buffer.
9189019Sps */
92161478Sdelphij	static int
9389019Spsexpand_linebuf()
9489019Sps{
95161478Sdelphij	/* Double the size of the line buffer. */
96161478Sdelphij	int new_size = size_linebuf * 2;
97161478Sdelphij
98161478Sdelphij	/* Just realloc to expand the buffer, if we can. */
99161478Sdelphij#if HAVE_REALLOC
100161478Sdelphij	char *new_buf = (char *) realloc(linebuf, new_size);
101161478Sdelphij	char *new_attr = (char *) realloc(attr, new_size);
102161478Sdelphij#else
10389019Sps	char *new_buf = (char *) calloc(new_size, sizeof(char));
10489019Sps	char *new_attr = (char *) calloc(new_size, sizeof(char));
105161478Sdelphij#endif
10689019Sps	if (new_buf == NULL || new_attr == NULL)
10789019Sps	{
10889019Sps		if (new_attr != NULL)
10989019Sps			free(new_attr);
11089019Sps		if (new_buf != NULL)
11189019Sps			free(new_buf);
11289019Sps		return 1;
11389019Sps	}
114161478Sdelphij#if HAVE_REALLOC
115161478Sdelphij	/*
116161478Sdelphij	 * We realloc'd the buffers; they already have the old contents.
117161478Sdelphij	 */
118161478Sdelphij	#if 0
119161478Sdelphij	memset(new_buf + size_linebuf, 0, new_size - size_linebuf);
120161478Sdelphij	memset(new_attr + size_linebuf, 0, new_size - size_linebuf);
121161478Sdelphij	#endif
122161478Sdelphij#else
123161478Sdelphij	/*
124161478Sdelphij	 * We just calloc'd the buffers; copy the old contents.
125161478Sdelphij	 */
12689019Sps	memcpy(new_buf, linebuf, size_linebuf * sizeof(char));
12789019Sps	memcpy(new_attr, attr, size_linebuf * sizeof(char));
128128345Stjr	free(attr);
129128345Stjr	free(linebuf);
130161478Sdelphij#endif
13189019Sps	linebuf = new_buf;
13289019Sps	attr = new_attr;
13389019Sps	size_linebuf = new_size;
13489019Sps	return 0;
13589019Sps}
13689019Sps
13789019Sps/*
138161478Sdelphij * Is a character ASCII?
139161478Sdelphij */
140161478Sdelphij	public int
141161478Sdelphijis_ascii_char(ch)
142161478Sdelphij	LWCHAR ch;
143161478Sdelphij{
144161478Sdelphij	return (ch <= 0x7F);
145161478Sdelphij}
146161478Sdelphij
147161478Sdelphij/*
14860786Sps * Rewind the line buffer.
14960786Sps */
15060786Sps	public void
15160786Spsprewind()
15260786Sps{
15360786Sps	curr = 0;
15460786Sps	column = 0;
155161478Sdelphij	cshift = 0;
15660786Sps	overstrike = 0;
157161478Sdelphij	last_overstrike = AT_NORMAL;
158161478Sdelphij	mbc_buf_len = 0;
15960786Sps	is_null_line = 0;
16060786Sps	pendc = '\0';
16163128Sps	lmargin = 0;
162128345Stjr	if (status_col)
163128345Stjr		lmargin += 1;
16460786Sps}
16560786Sps
16660786Sps/*
16760786Sps * Insert the line number (of the given position) into the line buffer.
16860786Sps */
16960786Sps	public void
17060786Spsplinenum(pos)
17160786Sps	POSITION pos;
17260786Sps{
173128345Stjr	register LINENUM linenum = 0;
17460786Sps	register int i;
17560786Sps
17663128Sps	if (linenums == OPT_ONPLUS)
17763128Sps	{
17863128Sps		/*
17963128Sps		 * Get the line number and put it in the current line.
18063128Sps		 * {{ Note: since find_linenum calls forw_raw_line,
18163128Sps		 *    it may seek in the input file, requiring the caller
18263128Sps		 *    of plinenum to re-seek if necessary. }}
18363128Sps		 * {{ Since forw_raw_line modifies linebuf, we must
18463128Sps		 *    do this first, before storing anything in linebuf. }}
18563128Sps		 */
186128345Stjr		linenum = find_linenum(pos);
18763128Sps	}
18863128Sps
18960786Sps	/*
19063128Sps	 * Display a status column if the -J option is set.
19160786Sps	 */
19263128Sps	if (status_col)
19363128Sps	{
19463128Sps		linebuf[curr] = ' ';
19563128Sps		if (start_attnpos != NULL_POSITION &&
19663128Sps		    pos >= start_attnpos && pos < end_attnpos)
197161478Sdelphij			attr[curr] = AT_NORMAL|AT_HILITE;
19863128Sps		else
199161478Sdelphij			attr[curr] = AT_NORMAL;
20063128Sps		curr++;
20163128Sps		column++;
20263128Sps	}
20360786Sps	/*
20463128Sps	 * Display the line number at the start of each line
20563128Sps	 * if the -N option is set.
20660786Sps	 */
20763128Sps	if (linenums == OPT_ONPLUS)
20863128Sps	{
209128345Stjr		char buf[INT_STRLEN_BOUND(pos) + 2];
210128345Stjr		int n;
211128345Stjr
212128345Stjr		linenumtoa(linenum, buf);
213128345Stjr		n = strlen(buf);
214128345Stjr		if (n < MIN_LINENUM_WIDTH)
215128345Stjr			n = MIN_LINENUM_WIDTH;
216128345Stjr		sprintf(linebuf+curr, "%*s ", n, buf);
217128345Stjr		n++;  /* One space after the line number. */
218128345Stjr		for (i = 0; i < n; i++)
219128345Stjr			attr[curr+i] = AT_NORMAL;
220128345Stjr		curr += n;
221128345Stjr		column += n;
222128345Stjr		lmargin += n;
22363128Sps	}
224128345Stjr
22560786Sps	/*
22663128Sps	 * Append enough spaces to bring us to the lmargin.
22760786Sps	 */
22863128Sps	while (column < lmargin)
22960786Sps	{
23060786Sps		linebuf[curr] = ' ';
23160786Sps		attr[curr++] = AT_NORMAL;
23260786Sps		column++;
23363128Sps	}
23460786Sps}
23560786Sps
23660786Sps/*
237161478Sdelphij * Shift the input line left.
238161478Sdelphij * This means discarding N printable chars at the start of the buffer.
23960786Sps */
240161478Sdelphij	static void
241161478Sdelphijpshift(shift)
242161478Sdelphij	int shift;
243161478Sdelphij{
244161478Sdelphij	LWCHAR prev_ch = 0;
245161478Sdelphij	unsigned char c;
246161478Sdelphij	int shifted = 0;
247161478Sdelphij	int to;
248161478Sdelphij	int from;
24989019Sps	int len;
250161478Sdelphij	int width;
251161478Sdelphij	int prev_attr;
252161478Sdelphij	int next_attr;
25389019Sps
254161478Sdelphij	if (shift > column - lmargin)
255161478Sdelphij		shift = column - lmargin;
256161478Sdelphij	if (shift > curr - lmargin)
257161478Sdelphij		shift = curr - lmargin;
258161478Sdelphij
259161478Sdelphij	to = from = lmargin;
26089019Sps	/*
261161478Sdelphij	 * We keep on going when shifted == shift
262161478Sdelphij	 * to get all combining chars.
26389019Sps	 */
264161478Sdelphij	while (shifted <= shift && from < curr)
26589019Sps	{
266161478Sdelphij		c = linebuf[from];
267172471Sdelphij		if (ctldisp == OPT_ONPLUS && IS_CSI_START(c))
26889019Sps		{
269161478Sdelphij			/* Keep cumulative effect.  */
270161478Sdelphij			linebuf[to] = c;
271161478Sdelphij			attr[to++] = attr[from++];
272161478Sdelphij			while (from < curr && linebuf[from])
273161478Sdelphij			{
274161478Sdelphij				linebuf[to] = linebuf[from];
275161478Sdelphij				attr[to++] = attr[from];
276161478Sdelphij				if (!is_ansi_middle(linebuf[from++]))
277161478Sdelphij					break;
278161478Sdelphij			}
279161478Sdelphij			continue;
280161478Sdelphij		}
281161478Sdelphij
282161478Sdelphij		width = 0;
283161478Sdelphij
284161478Sdelphij		if (!IS_ASCII_OCTET(c) && utf_mode)
285161478Sdelphij		{
286161478Sdelphij			/* Assumes well-formedness validation already done.  */
287161478Sdelphij			LWCHAR ch;
288161478Sdelphij
289161478Sdelphij			len = utf_len(c);
290161478Sdelphij			if (from + len > curr)
291161478Sdelphij				break;
292161478Sdelphij			ch = get_wchar(linebuf + from);
293161478Sdelphij			if (!is_composing_char(ch) && !is_combining_char(prev_ch, ch))
294161478Sdelphij				width = is_wide_char(ch) ? 2 : 1;
295161478Sdelphij			prev_ch = ch;
29689019Sps		} else
29789019Sps		{
298161478Sdelphij			len = 1;
299161478Sdelphij			if (c == '\b')
300161478Sdelphij				/* XXX - Incorrect if several '\b' in a row.  */
301161478Sdelphij				width = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
302161478Sdelphij			else if (!control_char(c))
303161478Sdelphij				width = 1;
304161478Sdelphij			prev_ch = 0;
305161478Sdelphij		}
306161478Sdelphij
307161478Sdelphij		if (width == 2 && shift - shifted == 1) {
308161478Sdelphij			/* Should never happen when called by pshift_all().  */
309161478Sdelphij			attr[to] = attr[from];
310161478Sdelphij			/*
311161478Sdelphij			 * Assume a wide_char will never be the first half of a
312161478Sdelphij			 * combining_char pair, so reset prev_ch in case we're
313161478Sdelphij			 * followed by a '\b'.
314161478Sdelphij			 */
315161478Sdelphij			prev_ch = linebuf[to++] = ' ';
316161478Sdelphij			from += len;
317161478Sdelphij			shifted++;
318161478Sdelphij			continue;
319161478Sdelphij		}
320161478Sdelphij
321161478Sdelphij		/* Adjust width for magic cookies. */
322161478Sdelphij		prev_attr = (to > 0) ? attr[to-1] : AT_NORMAL;
323161478Sdelphij		next_attr = (from + len < curr) ? attr[from + len] : prev_attr;
324161478Sdelphij		if (!is_at_equiv(attr[from], prev_attr) &&
325161478Sdelphij			!is_at_equiv(attr[from], next_attr))
326161478Sdelphij		{
327161478Sdelphij			width += attr_swidth(attr[from]);
328161478Sdelphij			if (from + len < curr)
329161478Sdelphij				width += attr_ewidth(attr[from]);
330161478Sdelphij			if (is_at_equiv(prev_attr, next_attr))
33189019Sps			{
332161478Sdelphij				width += attr_ewidth(prev_attr);
333161478Sdelphij				if (from + len < curr)
334161478Sdelphij					width += attr_swidth(next_attr);
33589019Sps			}
33689019Sps		}
33789019Sps
338161478Sdelphij		if (shift - shifted < width)
339161478Sdelphij			break;
340161478Sdelphij		from += len;
341161478Sdelphij		shifted += width;
342161478Sdelphij		if (shifted < 0)
343161478Sdelphij			shifted = 0;
34460786Sps	}
345161478Sdelphij	while (from < curr)
34660786Sps	{
347161478Sdelphij		linebuf[to] = linebuf[from];
348161478Sdelphij		attr[to++] = attr[from++];
34960786Sps	}
350161478Sdelphij	curr = to;
351161478Sdelphij	column -= shifted;
352161478Sdelphij	cshift += shifted;
35360786Sps}
35460786Sps
35560786Sps/*
356161478Sdelphij *
35760786Sps */
358161478Sdelphij	public void
359161478Sdelphijpshift_all()
36060786Sps{
361161478Sdelphij	pshift(column);
36260786Sps}
36360786Sps
36460786Sps/*
36560786Sps * Return the printing width of the start (enter) sequence
36660786Sps * for a given character attribute.
36760786Sps */
36860786Sps	static int
36960786Spsattr_swidth(a)
37060786Sps	int a;
37160786Sps{
372161478Sdelphij	int w = 0;
373161478Sdelphij
374161478Sdelphij	a = apply_at_specials(a);
375161478Sdelphij
376161478Sdelphij	if (a & AT_UNDERLINE)
377161478Sdelphij		w += ul_s_width;
378161478Sdelphij	if (a & AT_BOLD)
379161478Sdelphij		w += bo_s_width;
380161478Sdelphij	if (a & AT_BLINK)
381161478Sdelphij		w += bl_s_width;
382161478Sdelphij	if (a & AT_STANDOUT)
383161478Sdelphij		w += so_s_width;
384161478Sdelphij
385161478Sdelphij	return w;
38660786Sps}
38760786Sps
38860786Sps/*
38960786Sps * Return the printing width of the end (exit) sequence
39060786Sps * for a given character attribute.
39160786Sps */
39260786Sps	static int
39360786Spsattr_ewidth(a)
39460786Sps	int a;
39560786Sps{
396161478Sdelphij	int w = 0;
397161478Sdelphij
398161478Sdelphij	a = apply_at_specials(a);
399161478Sdelphij
400161478Sdelphij	if (a & AT_UNDERLINE)
401161478Sdelphij		w += ul_e_width;
402161478Sdelphij	if (a & AT_BOLD)
403161478Sdelphij		w += bo_e_width;
404161478Sdelphij	if (a & AT_BLINK)
405161478Sdelphij		w += bl_e_width;
406161478Sdelphij	if (a & AT_STANDOUT)
407161478Sdelphij		w += so_e_width;
408161478Sdelphij
409161478Sdelphij	return w;
41060786Sps}
41160786Sps
41260786Sps/*
41360786Sps * Return the printing width of a given character and attribute,
41460786Sps * if the character were added to the current position in the line buffer.
41560786Sps * Adding a character with a given attribute may cause an enter or exit
41660786Sps * attribute sequence to be inserted, so this must be taken into account.
41760786Sps */
41860786Sps	static int
419161478Sdelphijpwidth(ch, a, prev_ch)
420161478Sdelphij	LWCHAR ch;
42160786Sps	int a;
422161478Sdelphij	LWCHAR prev_ch;
42360786Sps{
424161478Sdelphij	int w;
42560786Sps
426161478Sdelphij	if (ch == '\b')
42760786Sps		/*
428161478Sdelphij		 * Backspace moves backwards one or two positions.
429161478Sdelphij		 * XXX - Incorrect if several '\b' in a row.
43060786Sps		 */
431161478Sdelphij		return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
43260786Sps
433161478Sdelphij	if (!utf_mode || is_ascii_char(ch))
434161478Sdelphij	{
435161478Sdelphij		if (control_char((char)ch))
436161478Sdelphij		{
437161478Sdelphij			/*
438161478Sdelphij			 * Control characters do unpredictable things,
439161478Sdelphij			 * so we don't even try to guess; say it doesn't move.
440161478Sdelphij			 * This can only happen if the -r flag is in effect.
441161478Sdelphij			 */
442161478Sdelphij			return (0);
443161478Sdelphij		}
444161478Sdelphij	} else
445161478Sdelphij	{
446161478Sdelphij		if (is_composing_char(ch) || is_combining_char(prev_ch, ch))
447161478Sdelphij		{
448161478Sdelphij			/*
449161478Sdelphij			 * Composing and combining chars take up no space.
450161478Sdelphij			 *
451161478Sdelphij			 * Some terminals, upon failure to compose a
452161478Sdelphij			 * composing character with the character(s) that
453161478Sdelphij			 * precede(s) it will actually take up one column
454161478Sdelphij			 * for the composing character; there isn't much
455161478Sdelphij			 * we could do short of testing the (complex)
456161478Sdelphij			 * composition process ourselves and printing
457161478Sdelphij			 * a binary representation when it fails.
458161478Sdelphij			 */
459161478Sdelphij			return (0);
460161478Sdelphij		}
461161478Sdelphij	}
46260786Sps
46360786Sps	/*
464161478Sdelphij	 * Other characters take one or two columns,
46560786Sps	 * plus the width of any attribute enter/exit sequence.
46660786Sps	 */
46760786Sps	w = 1;
468161478Sdelphij	if (is_wide_char(ch))
469161478Sdelphij		w++;
470161478Sdelphij	if (curr > 0 && !is_at_equiv(attr[curr-1], a))
47160786Sps		w += attr_ewidth(attr[curr-1]);
472161478Sdelphij	if ((apply_at_specials(a) != AT_NORMAL) &&
473161478Sdelphij	    (curr == 0 || !is_at_equiv(attr[curr-1], a)))
47460786Sps		w += attr_swidth(a);
47560786Sps	return (w);
47660786Sps}
47760786Sps
47860786Sps/*
479161478Sdelphij * Delete to the previous base character in the line buffer.
480161478Sdelphij * Return 1 if one is found.
48160786Sps */
482161478Sdelphij	static int
48360786Spsbackc()
48460786Sps{
485161478Sdelphij	LWCHAR prev_ch;
486161478Sdelphij	char *p = linebuf + curr;
487161478Sdelphij	LWCHAR ch = step_char(&p, -1, linebuf + lmargin);
488161478Sdelphij	int width;
489161478Sdelphij
490161478Sdelphij	/* This assumes that there is no '\b' in linebuf.  */
491161478Sdelphij	while (   curr > lmargin
492161478Sdelphij	       && column > lmargin
493161478Sdelphij	       && (!(attr[curr - 1] & (AT_ANSI|AT_BINARY))))
494161478Sdelphij	{
495161478Sdelphij		curr = p - linebuf;
496161478Sdelphij		prev_ch = step_char(&p, -1, linebuf + lmargin);
497161478Sdelphij		width = pwidth(ch, attr[curr], prev_ch);
498161478Sdelphij		column -= width;
499161478Sdelphij		if (width > 0)
500161478Sdelphij			return 1;
501161478Sdelphij		ch = prev_ch;
502161478Sdelphij	}
503161478Sdelphij
504161478Sdelphij	return 0;
50560786Sps}
50660786Sps
50760786Sps/*
50860786Sps * Are we currently within a recognized ANSI escape sequence?
50960786Sps */
51060786Sps	static int
51160786Spsin_ansi_esc_seq()
51260786Sps{
513161478Sdelphij	char *p;
51460786Sps
51560786Sps	/*
51660786Sps	 * Search backwards for either an ESC (which means we ARE in a seq);
51760786Sps	 * or an end char (which means we're NOT in a seq).
51860786Sps	 */
519161478Sdelphij	for (p = &linebuf[curr];  p > linebuf; )
52060786Sps	{
521161478Sdelphij		LWCHAR ch = step_char(&p, -1, linebuf);
522172471Sdelphij		if (IS_CSI_START(ch))
52360786Sps			return (1);
524161478Sdelphij		if (!is_ansi_middle(ch))
52560786Sps			return (0);
52660786Sps	}
52760786Sps	return (0);
52860786Sps}
52960786Sps
53060786Sps/*
53189019Sps * Is a character the end of an ANSI escape sequence?
53289019Sps */
53389019Sps	public int
534161478Sdelphijis_ansi_end(ch)
535161478Sdelphij	LWCHAR ch;
53689019Sps{
537161478Sdelphij	if (!is_ascii_char(ch))
538161478Sdelphij		return (0);
539161478Sdelphij	return (strchr(end_ansi_chars, (char) ch) != NULL);
54089019Sps}
54189019Sps
54289019Sps/*
543161478Sdelphij *
544161478Sdelphij */
545161478Sdelphij	public int
546161478Sdelphijis_ansi_middle(ch)
547161478Sdelphij	LWCHAR ch;
548161478Sdelphij{
549161478Sdelphij	if (!is_ascii_char(ch))
550161478Sdelphij		return (0);
551161478Sdelphij	if (is_ansi_end(ch))
552161478Sdelphij		return (0);
553161478Sdelphij	return (strchr(mid_ansi_chars, (char) ch) != NULL);
554161478Sdelphij}
555161478Sdelphij
556161478Sdelphij/*
55760786Sps * Append a character and attribute to the line buffer.
55860786Sps */
559161478Sdelphij#define	STORE_CHAR(ch,a,rep,pos) \
560161478Sdelphij	do { \
561161478Sdelphij		if (store_char((ch),(a),(rep),(pos))) return (1); \
562161478Sdelphij	} while (0)
56389019Sps
56460786Sps	static int
565161478Sdelphijstore_char(ch, a, rep, pos)
566161478Sdelphij	LWCHAR ch;
56760786Sps	int a;
568161478Sdelphij	char *rep;
56960786Sps	POSITION pos;
57060786Sps{
571161478Sdelphij	int w;
572161478Sdelphij	int replen;
573161478Sdelphij	char cs;
57460786Sps
575161478Sdelphij	w = (a & (AT_UNDERLINE|AT_BOLD));	/* Pre-use w.  */
576161478Sdelphij	if (w != AT_NORMAL)
577161478Sdelphij		last_overstrike = w;
578161478Sdelphij
57960786Sps#if HILITE_SEARCH
58089019Sps	{
581161478Sdelphij		int matches;
582161478Sdelphij		if (is_hilited(pos, pos+1, 0, &matches))
583161478Sdelphij		{
584161478Sdelphij			/*
585161478Sdelphij			 * This character should be highlighted.
586161478Sdelphij			 * Override the attribute passed in.
587161478Sdelphij			 */
588161478Sdelphij			if (a != AT_ANSI)
589240121Sdelphij			{
590240121Sdelphij				if (highest_hilite != NULL_POSITION &&
591240121Sdelphij				    pos > highest_hilite)
592240121Sdelphij				    	highest_hilite = pos;
593161478Sdelphij				a |= AT_HILITE;
594240121Sdelphij			}
595161478Sdelphij		}
59689019Sps	}
59760786Sps#endif
598161478Sdelphij
59960786Sps	if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
600161478Sdelphij	{
601161478Sdelphij		if (!is_ansi_end(ch) && !is_ansi_middle(ch)) {
602161478Sdelphij			/* Remove whole unrecognized sequence.  */
603191930Sdelphij			char *p = &linebuf[curr];
604191930Sdelphij			LWCHAR bch;
605191930Sdelphij			do {
606191930Sdelphij				bch = step_char(&p, -1, linebuf);
607191930Sdelphij			} while (p > linebuf && !IS_CSI_START(bch));
608191930Sdelphij			curr = p - linebuf;
609161478Sdelphij			return 0;
610161478Sdelphij		}
611161478Sdelphij		a = AT_ANSI;	/* Will force re-AT_'ing around it.  */
61260786Sps		w = 0;
613161478Sdelphij	}
614172471Sdelphij	else if (ctldisp == OPT_ONPLUS && IS_CSI_START(ch))
615161478Sdelphij	{
616161478Sdelphij		a = AT_ANSI;	/* Will force re-AT_'ing around it.  */
617161478Sdelphij		w = 0;
618161478Sdelphij	}
61960786Sps	else
620161478Sdelphij	{
621161478Sdelphij		char *p = &linebuf[curr];
622161478Sdelphij		LWCHAR prev_ch = step_char(&p, -1, linebuf);
623161478Sdelphij		w = pwidth(ch, a, prev_ch);
624161478Sdelphij	}
625161478Sdelphij
62660786Sps	if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
62760786Sps		/*
62860786Sps		 * Won't fit on screen.
62960786Sps		 */
63060786Sps		return (1);
63160786Sps
632161478Sdelphij	if (rep == NULL)
63389019Sps	{
634161478Sdelphij		cs = (char) ch;
635161478Sdelphij		rep = &cs;
636161478Sdelphij		replen = 1;
637161478Sdelphij	} else
638161478Sdelphij	{
639161478Sdelphij		replen = utf_len(rep[0]);
640161478Sdelphij	}
641161478Sdelphij	if (curr + replen >= size_linebuf-6)
642161478Sdelphij	{
64360786Sps		/*
64460786Sps		 * Won't fit in line buffer.
64589019Sps		 * Try to expand it.
64660786Sps		 */
64789019Sps		if (expand_linebuf())
64889019Sps			return (1);
64989019Sps	}
65060786Sps
651161478Sdelphij	while (replen-- > 0)
65260786Sps	{
653161478Sdelphij		linebuf[curr] = *rep++;
654161478Sdelphij		attr[curr] = a;
655161478Sdelphij		curr++;
65660786Sps	}
65760786Sps	column += w;
65860786Sps	return (0);
65960786Sps}
66060786Sps
66160786Sps/*
66289019Sps * Append a tab to the line buffer.
66389019Sps * Store spaces to represent the tab.
66489019Sps */
66589019Sps#define	STORE_TAB(a,pos) \
66689019Sps	do { if (store_tab((a),(pos))) return (1); } while (0)
66789019Sps
66889019Sps	static int
66989019Spsstore_tab(attr, pos)
67089019Sps	int attr;
67189019Sps	POSITION pos;
67289019Sps{
67389019Sps	int to_tab = column + cshift - lmargin;
67489019Sps	int i;
67589019Sps
67689019Sps	if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1])
67789019Sps		to_tab = tabdefault -
67889019Sps		     ((to_tab - tabstops[ntabstops-1]) % tabdefault);
67989019Sps	else
68089019Sps	{
68189019Sps		for (i = ntabstops - 2;  i >= 0;  i--)
68289019Sps			if (to_tab >= tabstops[i])
68389019Sps				break;
68489019Sps		to_tab = tabstops[i+1] - to_tab;
68589019Sps	}
68689019Sps
687161478Sdelphij	if (column + to_tab - 1 + pwidth(' ', attr, 0) + attr_ewidth(attr) > sc_width)
688161478Sdelphij		return 1;
689161478Sdelphij
69089019Sps	do {
691161478Sdelphij		STORE_CHAR(' ', attr, " ", pos);
69289019Sps	} while (--to_tab > 0);
69389019Sps	return 0;
69489019Sps}
69589019Sps
696161478Sdelphij#define STORE_PRCHAR(c, pos) \
697161478Sdelphij	do { if (store_prchar((c), (pos))) return 1; } while (0)
698161478Sdelphij
699161478Sdelphij	static int
700161478Sdelphijstore_prchar(c, pos)
701161478Sdelphij	char c;
702161478Sdelphij	POSITION pos;
703161478Sdelphij{
704161478Sdelphij	char *s;
705161478Sdelphij
706161478Sdelphij	/*
707161478Sdelphij	 * Convert to printable representation.
708161478Sdelphij	 */
709161478Sdelphij	s = prchar(c);
710161478Sdelphij
711161478Sdelphij	/*
712161478Sdelphij	 * Make sure we can get the entire representation
713161478Sdelphij	 * of the character on this line.
714161478Sdelphij	 */
715161478Sdelphij	if (column + (int) strlen(s) - 1 +
716161478Sdelphij            pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
717161478Sdelphij		return 1;
718161478Sdelphij
719161478Sdelphij	for ( ;  *s != 0;  s++)
720161478Sdelphij		STORE_CHAR(*s, AT_BINARY, NULL, pos);
721161478Sdelphij
722161478Sdelphij	return 0;
723161478Sdelphij}
724161478Sdelphij
725161478Sdelphij	static int
726161478Sdelphijflush_mbc_buf(pos)
727161478Sdelphij	POSITION pos;
728161478Sdelphij{
729161478Sdelphij	int i;
730161478Sdelphij
731161478Sdelphij	for (i = 0; i < mbc_buf_index; i++)
732161478Sdelphij		if (store_prchar(mbc_buf[i], pos))
733161478Sdelphij			return mbc_buf_index - i;
734161478Sdelphij
735161478Sdelphij	return 0;
736161478Sdelphij}
737161478Sdelphij
73889019Sps/*
73960786Sps * Append a character to the line buffer.
74060786Sps * Expand tabs into spaces, handle underlining, boldfacing, etc.
74160786Sps * Returns 0 if ok, 1 if couldn't fit in buffer.
74260786Sps */
74360786Sps	public int
74460786Spspappend(c, pos)
745161478Sdelphij	char c;
74660786Sps	POSITION pos;
74760786Sps{
74860786Sps	int r;
74960786Sps
75060786Sps	if (pendc)
75160786Sps	{
752161478Sdelphij		if (do_append(pendc, NULL, pendpos))
75360786Sps			/*
75460786Sps			 * Oops.  We've probably lost the char which
75560786Sps			 * was in pendc, since caller won't back up.
75660786Sps			 */
75760786Sps			return (1);
75860786Sps		pendc = '\0';
75960786Sps	}
76060786Sps
76160786Sps	if (c == '\r' && bs_mode == BS_SPECIAL)
76260786Sps	{
763161478Sdelphij		if (mbc_buf_len > 0)  /* utf_mode must be on. */
764161478Sdelphij		{
765161478Sdelphij			/* Flush incomplete (truncated) sequence. */
766161478Sdelphij			r = flush_mbc_buf(mbc_pos);
767161478Sdelphij			mbc_buf_index = r + 1;
768161478Sdelphij			mbc_buf_len = 0;
769161478Sdelphij			if (r)
770161478Sdelphij				return (mbc_buf_index);
771161478Sdelphij		}
772161478Sdelphij
77360786Sps		/*
77460786Sps		 * Don't put the CR into the buffer until we see
77560786Sps		 * the next char.  If the next char is a newline,
77660786Sps		 * discard the CR.
77760786Sps		 */
77860786Sps		pendc = c;
77960786Sps		pendpos = pos;
78060786Sps		return (0);
78160786Sps	}
78260786Sps
783161478Sdelphij	if (!utf_mode)
784161478Sdelphij	{
785161478Sdelphij		r = do_append((LWCHAR) c, NULL, pos);
786161478Sdelphij	} else
787161478Sdelphij	{
788161478Sdelphij		/* Perform strict validation in all possible cases. */
789161478Sdelphij		if (mbc_buf_len == 0)
790161478Sdelphij		{
791161478Sdelphij		retry:
792161478Sdelphij			mbc_buf_index = 1;
793161478Sdelphij			*mbc_buf = c;
794161478Sdelphij			if (IS_ASCII_OCTET(c))
795161478Sdelphij				r = do_append((LWCHAR) c, NULL, pos);
796161478Sdelphij			else if (IS_UTF8_LEAD(c))
797161478Sdelphij			{
798161478Sdelphij				mbc_buf_len = utf_len(c);
799161478Sdelphij				mbc_pos = pos;
800161478Sdelphij				return (0);
801161478Sdelphij			} else
802161478Sdelphij				/* UTF8_INVALID or stray UTF8_TRAIL */
803161478Sdelphij				r = flush_mbc_buf(pos);
804161478Sdelphij		} else if (IS_UTF8_TRAIL(c))
805161478Sdelphij		{
806161478Sdelphij			mbc_buf[mbc_buf_index++] = c;
807161478Sdelphij			if (mbc_buf_index < mbc_buf_len)
808161478Sdelphij				return (0);
809161478Sdelphij			if (is_utf8_well_formed(mbc_buf))
810161478Sdelphij				r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos);
811161478Sdelphij			else
812161478Sdelphij				/* Complete, but not shortest form, sequence. */
813161478Sdelphij				mbc_buf_index = r = flush_mbc_buf(mbc_pos);
814161478Sdelphij			mbc_buf_len = 0;
815161478Sdelphij		} else
816161478Sdelphij		{
817161478Sdelphij			/* Flush incomplete (truncated) sequence.  */
818161478Sdelphij			r = flush_mbc_buf(mbc_pos);
819161478Sdelphij			mbc_buf_index = r + 1;
820161478Sdelphij			mbc_buf_len = 0;
821161478Sdelphij			/* Handle new char.  */
822161478Sdelphij			if (!r)
823161478Sdelphij				goto retry;
824161478Sdelphij 		}
825161478Sdelphij	}
826161478Sdelphij
82760786Sps	/*
82860786Sps	 * If we need to shift the line, do it.
82960786Sps	 * But wait until we get to at least the middle of the screen,
83060786Sps	 * so shifting it doesn't affect the chars we're currently
83160786Sps	 * pappending.  (Bold & underline can get messed up otherwise.)
83260786Sps	 */
83360786Sps	if (cshift < hshift && column > sc_width / 2)
83489019Sps	{
83589019Sps		linebuf[curr] = '\0';
83660786Sps		pshift(hshift - cshift);
83789019Sps	}
838161478Sdelphij	if (r)
839161478Sdelphij	{
840161478Sdelphij		/* How many chars should caller back up? */
841161478Sdelphij		r = (!utf_mode) ? 1 : mbc_buf_index;
842161478Sdelphij	}
84360786Sps	return (r);
84460786Sps}
84560786Sps
84660786Sps	static int
847161478Sdelphijdo_append(ch, rep, pos)
848161478Sdelphij	LWCHAR ch;
849161478Sdelphij	char *rep;
85060786Sps	POSITION pos;
85160786Sps{
85260786Sps	register int a;
853161478Sdelphij	LWCHAR prev_ch;
85460786Sps
855161478Sdelphij	a = AT_NORMAL;
85660786Sps
857161478Sdelphij	if (ch == '\b')
85860786Sps	{
859161478Sdelphij		if (bs_mode == BS_CONTROL)
86060786Sps			goto do_control_char;
861161478Sdelphij
862161478Sdelphij		/*
863161478Sdelphij		 * A better test is needed here so we don't
864161478Sdelphij		 * backspace over part of the printed
865161478Sdelphij		 * representation of a binary character.
866161478Sdelphij		 */
867161478Sdelphij		if (   curr <= lmargin
868161478Sdelphij		    || column <= lmargin
869161478Sdelphij		    || (attr[curr - 1] & (AT_ANSI|AT_BINARY)))
870161478Sdelphij			STORE_PRCHAR('\b', pos);
871161478Sdelphij		else if (bs_mode == BS_NORMAL)
872161478Sdelphij			STORE_CHAR(ch, AT_NORMAL, NULL, pos);
873161478Sdelphij		else if (bs_mode == BS_SPECIAL)
874161478Sdelphij			overstrike = backc();
875161478Sdelphij
876161478Sdelphij		return 0;
877161478Sdelphij	}
878161478Sdelphij
879161478Sdelphij	if (overstrike > 0)
88060786Sps	{
88160786Sps		/*
88260786Sps		 * Overstrike the character at the current position
88360786Sps		 * in the line buffer.  This will cause either
88460786Sps		 * underline (if a "_" is overstruck),
88560786Sps		 * bold (if an identical character is overstruck),
88660786Sps		 * or just deletion of the character in the buffer.
88760786Sps		 */
888161478Sdelphij		overstrike = utf_mode ? -1 : 0;
889161478Sdelphij		/* To be correct, this must be a base character.  */
890161478Sdelphij		prev_ch = get_wchar(linebuf + curr);
891161478Sdelphij		a = attr[curr];
892161478Sdelphij		if (ch == prev_ch)
89389019Sps		{
894128345Stjr			/*
895128345Stjr			 * Overstriking a char with itself means make it bold.
896128345Stjr			 * But overstriking an underscore with itself is
897128345Stjr			 * ambiguous.  It could mean make it bold, or
898128345Stjr			 * it could mean make it underlined.
899128345Stjr			 * Use the previous overstrike to resolve it.
900128345Stjr			 */
901161478Sdelphij			if (ch == '_')
90289019Sps			{
903161478Sdelphij				if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL)
904161478Sdelphij					a |= (AT_BOLD|AT_UNDERLINE);
905161478Sdelphij				else if (last_overstrike != AT_NORMAL)
906161478Sdelphij					a |= last_overstrike;
907161478Sdelphij				else
908161478Sdelphij					a |= AT_BOLD;
909161478Sdelphij			} else
910161478Sdelphij				a |= AT_BOLD;
911161478Sdelphij		} else if (ch == '_')
91289019Sps		{
913161478Sdelphij			a |= AT_UNDERLINE;
914161478Sdelphij			ch = prev_ch;
915161478Sdelphij			rep = linebuf + curr;
916161478Sdelphij		} else if (prev_ch == '_')
917161478Sdelphij		{
918161478Sdelphij			a |= AT_UNDERLINE;
919161478Sdelphij		}
920161478Sdelphij		/* Else we replace prev_ch, but we keep its attributes.  */
921161478Sdelphij	} else if (overstrike < 0)
922161478Sdelphij	{
923161478Sdelphij		if (   is_composing_char(ch)
924161478Sdelphij		    || is_combining_char(get_wchar(linebuf + curr), ch))
925161478Sdelphij			/* Continuation of the same overstrike.  */
926161478Sdelphij			a = last_overstrike;
92760786Sps		else
928161478Sdelphij			overstrike = 0;
929161478Sdelphij	}
930161478Sdelphij
931161478Sdelphij	if (ch == '\t')
93260786Sps	{
93360786Sps		/*
93460786Sps		 * Expand a tab into spaces.
93560786Sps		 */
93660786Sps		switch (bs_mode)
93760786Sps		{
93860786Sps		case BS_CONTROL:
93960786Sps			goto do_control_char;
94060786Sps		case BS_NORMAL:
94160786Sps		case BS_SPECIAL:
942161478Sdelphij			STORE_TAB(a, pos);
94360786Sps			break;
94460786Sps		}
945161478Sdelphij	} else if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch))
94660786Sps	{
94760786Sps	do_control_char:
948172471Sdelphij		if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && IS_CSI_START(ch)))
94960786Sps		{
95060786Sps			/*
95160786Sps			 * Output as a normal character.
95260786Sps			 */
953161478Sdelphij			STORE_CHAR(ch, AT_NORMAL, rep, pos);
95460786Sps		} else
95560786Sps		{
956161478Sdelphij			STORE_PRCHAR((char) ch, pos);
957161478Sdelphij		}
958161478Sdelphij	} else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch))
959161478Sdelphij	{
960161478Sdelphij		char *s;
96160786Sps
962161478Sdelphij		s = prutfchar(ch);
96360786Sps
964161478Sdelphij		if (column + (int) strlen(s) - 1 +
965161478Sdelphij		    pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
966161478Sdelphij			return (1);
967161478Sdelphij
968161478Sdelphij		for ( ;  *s != 0;  s++)
969161478Sdelphij			STORE_CHAR(*s, AT_BINARY, NULL, pos);
970161478Sdelphij 	} else
97160786Sps	{
972161478Sdelphij		STORE_CHAR(ch, a, rep, pos);
97360786Sps	}
974161478Sdelphij 	return (0);
975161478Sdelphij}
97660786Sps
977161478Sdelphij/*
978161478Sdelphij *
979161478Sdelphij */
980161478Sdelphij	public int
981161478Sdelphijpflushmbc()
982161478Sdelphij{
983161478Sdelphij	int r = 0;
984161478Sdelphij
985161478Sdelphij	if (mbc_buf_len > 0)
986161478Sdelphij	{
987161478Sdelphij		/* Flush incomplete (truncated) sequence.  */
988161478Sdelphij		r = flush_mbc_buf(mbc_pos);
989161478Sdelphij		mbc_buf_len = 0;
990161478Sdelphij	}
991161478Sdelphij	return r;
99260786Sps}
99360786Sps
99460786Sps/*
99560786Sps * Terminate the line in the line buffer.
99660786Sps */
99760786Sps	public void
998195941Sdelphijpdone(endline, forw)
99960786Sps	int endline;
1000195941Sdelphij	int forw;
100160786Sps{
1002161478Sdelphij	(void) pflushmbc();
1003161478Sdelphij
100460786Sps	if (pendc && (pendc != '\r' || !endline))
100560786Sps		/*
100660786Sps		 * If we had a pending character, put it in the buffer.
100760786Sps		 * But discard a pending CR if we are at end of line
100860786Sps		 * (that is, discard the CR in a CR/LF sequence).
100960786Sps		 */
1010161478Sdelphij		(void) do_append(pendc, NULL, pendpos);
101160786Sps
101260786Sps	/*
101360786Sps	 * Make sure we've shifted the line, if we need to.
101460786Sps	 */
101560786Sps	if (cshift < hshift)
101660786Sps		pshift(hshift - cshift);
101760786Sps
1018161478Sdelphij	if (ctldisp == OPT_ONPLUS && is_ansi_end('m'))
1019161478Sdelphij	{
1020161478Sdelphij		/* Switch to normal attribute at end of line. */
1021161478Sdelphij		char *p = "\033[m";
1022161478Sdelphij		for ( ;  *p != '\0';  p++)
1023161478Sdelphij		{
1024161478Sdelphij			linebuf[curr] = *p;
1025161478Sdelphij			attr[curr++] = AT_ANSI;
1026161478Sdelphij		}
1027161478Sdelphij	}
1028161478Sdelphij
102960786Sps	/*
103060786Sps	 * Add a newline if necessary,
103160786Sps	 * and append a '\0' to the end of the line.
1032170259Sdelphij	 * We output a newline if we're not at the right edge of the screen,
1033170259Sdelphij	 * or if the terminal doesn't auto wrap,
1034170259Sdelphij	 * or if this is really the end of the line AND the terminal ignores
1035170259Sdelphij	 * a newline at the right edge.
1036170259Sdelphij	 * (In the last case we don't want to output a newline if the terminal
1037170259Sdelphij	 * doesn't ignore it since that would produce an extra blank line.
1038170259Sdelphij	 * But we do want to output a newline if the terminal ignores it in case
1039170259Sdelphij	 * the next line is blank.  In that case the single newline output for
1040170259Sdelphij	 * that blank line would be ignored!)
104160786Sps	 */
1042191930Sdelphij	if (column < sc_width || !auto_wrap || (endline && ignaw) || ctldisp == OPT_ON)
104360786Sps	{
104460786Sps		linebuf[curr] = '\n';
104560786Sps		attr[curr] = AT_NORMAL;
104660786Sps		curr++;
1047173685Sdelphij	}
1048195941Sdelphij	else if (ignaw && column >= sc_width && forw)
1049173685Sdelphij	{
1050173685Sdelphij		/*
1051191930Sdelphij		 * Terminals with "ignaw" don't wrap until they *really* need
1052191930Sdelphij		 * to, i.e. when the character *after* the last one to fit on a
1053191930Sdelphij		 * line is output. But they are too hard to deal with when they
1054191930Sdelphij		 * get in the state where a full screen width of characters
1055191930Sdelphij		 * have been output but the cursor is sitting on the right edge
1056191930Sdelphij		 * instead of at the start of the next line.
1057195941Sdelphij		 * So we nudge them into wrapping by outputting a space
1058195941Sdelphij		 * character plus a backspace.  But do this only if moving
1059195941Sdelphij		 * forward; if we're moving backward and drawing this line at
1060195941Sdelphij		 * the top of the screen, the space would overwrite the first
1061195941Sdelphij		 * char on the next line.  We don't need to do this "nudge"
1062195941Sdelphij		 * at the top of the screen anyway.
1063173685Sdelphij		 */
1064195941Sdelphij		linebuf[curr] = ' ';
1065173685Sdelphij		attr[curr++] = AT_NORMAL;
1066173685Sdelphij		linebuf[curr] = '\b';
1067173685Sdelphij		attr[curr++] = AT_NORMAL;
106860786Sps	}
106960786Sps	linebuf[curr] = '\0';
107060786Sps	attr[curr] = AT_NORMAL;
1071191930Sdelphij}
107289019Sps
1073191930Sdelphij/*
1074191930Sdelphij *
1075191930Sdelphij */
1076191930Sdelphij	public void
1077191930Sdelphijset_status_col(c)
1078191930Sdelphij	char c;
1079191930Sdelphij{
1080191930Sdelphij	linebuf[0] = c;
1081191930Sdelphij	attr[0] = AT_NORMAL|AT_HILITE;
108260786Sps}
108360786Sps
108460786Sps/*
108560786Sps * Get a character from the current line.
108660786Sps * Return the character as the function return value,
108760786Sps * and the character attribute in *ap.
108860786Sps */
108960786Sps	public int
109060786Spsgline(i, ap)
109160786Sps	register int i;
109260786Sps	register int *ap;
109360786Sps{
109460786Sps	if (is_null_line)
109560786Sps	{
109660786Sps		/*
109760786Sps		 * If there is no current line, we pretend the line is
109860786Sps		 * either "~" or "", depending on the "twiddle" flag.
109960786Sps		 */
1100161478Sdelphij		if (twiddle)
1101161478Sdelphij		{
1102161478Sdelphij			if (i == 0)
1103161478Sdelphij			{
1104161478Sdelphij				*ap = AT_BOLD;
1105161478Sdelphij				return '~';
1106161478Sdelphij			}
1107161478Sdelphij			--i;
1108161478Sdelphij		}
1109161478Sdelphij		/* Make sure we're back to AT_NORMAL before the '\n'.  */
1110161478Sdelphij		*ap = AT_NORMAL;
1111161478Sdelphij		return i ? '\0' : '\n';
111260786Sps	}
111360786Sps
111460786Sps	*ap = attr[i];
1115161478Sdelphij	return (linebuf[i] & 0xFF);
111660786Sps}
111760786Sps
111860786Sps/*
111960786Sps * Indicate that there is no current line.
112060786Sps */
112160786Sps	public void
112260786Spsnull_line()
112360786Sps{
112460786Sps	is_null_line = 1;
112560786Sps	cshift = 0;
112660786Sps}
112760786Sps
112860786Sps/*
112960786Sps * Analogous to forw_line(), but deals with "raw lines":
113060786Sps * lines which are not split for screen width.
113160786Sps * {{ This is supposed to be more efficient than forw_line(). }}
113260786Sps */
113360786Sps	public POSITION
1134170259Sdelphijforw_raw_line(curr_pos, linep, line_lenp)
113560786Sps	POSITION curr_pos;
113660786Sps	char **linep;
1137170259Sdelphij	int *line_lenp;
113860786Sps{
113989019Sps	register int n;
114060786Sps	register int c;
114160786Sps	POSITION new_pos;
114260786Sps
114360786Sps	if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
114460786Sps		(c = ch_forw_get()) == EOI)
114560786Sps		return (NULL_POSITION);
114660786Sps
114789019Sps	n = 0;
114860786Sps	for (;;)
114960786Sps	{
1150161478Sdelphij		if (c == '\n' || c == EOI || ABORT_SIGS())
115160786Sps		{
115260786Sps			new_pos = ch_tell();
115360786Sps			break;
115460786Sps		}
115589019Sps		if (n >= size_linebuf-1)
115660786Sps		{
115789019Sps			if (expand_linebuf())
115889019Sps			{
115989019Sps				/*
116089019Sps				 * Overflowed the input buffer.
116189019Sps				 * Pretend the line ended here.
116289019Sps				 */
116389019Sps				new_pos = ch_tell() - 1;
116489019Sps				break;
116589019Sps			}
116660786Sps		}
116789019Sps		linebuf[n++] = c;
116860786Sps		c = ch_forw_get();
116960786Sps	}
117089019Sps	linebuf[n] = '\0';
117160786Sps	if (linep != NULL)
117260786Sps		*linep = linebuf;
1173170259Sdelphij	if (line_lenp != NULL)
1174170259Sdelphij		*line_lenp = n;
117560786Sps	return (new_pos);
117660786Sps}
117760786Sps
117860786Sps/*
117960786Sps * Analogous to back_line(), but deals with "raw lines".
118060786Sps * {{ This is supposed to be more efficient than back_line(). }}
118160786Sps */
118260786Sps	public POSITION
1183170259Sdelphijback_raw_line(curr_pos, linep, line_lenp)
118460786Sps	POSITION curr_pos;
118560786Sps	char **linep;
1186170259Sdelphij	int *line_lenp;
118760786Sps{
118889019Sps	register int n;
118960786Sps	register int c;
119060786Sps	POSITION new_pos;
119160786Sps
119260786Sps	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
119360786Sps		ch_seek(curr_pos-1))
119460786Sps		return (NULL_POSITION);
119560786Sps
119689019Sps	n = size_linebuf;
119789019Sps	linebuf[--n] = '\0';
119860786Sps	for (;;)
119960786Sps	{
120060786Sps		c = ch_back_get();
1201161478Sdelphij		if (c == '\n' || ABORT_SIGS())
120260786Sps		{
120360786Sps			/*
120460786Sps			 * This is the newline ending the previous line.
120560786Sps			 * We have hit the beginning of the line.
120660786Sps			 */
120760786Sps			new_pos = ch_tell() + 1;
120860786Sps			break;
120960786Sps		}
121060786Sps		if (c == EOI)
121160786Sps		{
121260786Sps			/*
121360786Sps			 * We have hit the beginning of the file.
121460786Sps			 * This must be the first line in the file.
121560786Sps			 * This must, of course, be the beginning of the line.
121660786Sps			 */
121760786Sps			new_pos = ch_zero();
121860786Sps			break;
121960786Sps		}
122089019Sps		if (n <= 0)
122160786Sps		{
122289019Sps			int old_size_linebuf = size_linebuf;
122389019Sps			char *fm;
122489019Sps			char *to;
122589019Sps			if (expand_linebuf())
122689019Sps			{
122789019Sps				/*
122889019Sps				 * Overflowed the input buffer.
122989019Sps				 * Pretend the line ended here.
123089019Sps				 */
123189019Sps				new_pos = ch_tell() + 1;
123289019Sps				break;
123389019Sps			}
123460786Sps			/*
123589019Sps			 * Shift the data to the end of the new linebuf.
123660786Sps			 */
1237149487Stjr			for (fm = linebuf + old_size_linebuf - 1,
1238149487Stjr			      to = linebuf + size_linebuf - 1;
123989019Sps			     fm >= linebuf;  fm--, to--)
124089019Sps				*to = *fm;
124189019Sps			n = size_linebuf - old_size_linebuf;
124260786Sps		}
124389019Sps		linebuf[--n] = c;
124460786Sps	}
124560786Sps	if (linep != NULL)
124689019Sps		*linep = &linebuf[n];
1247170259Sdelphij	if (line_lenp != NULL)
1248170259Sdelphij		*line_lenp = size_linebuf - 1 - n;
124960786Sps	return (new_pos);
125060786Sps}
1251