xref: /freebsd/contrib/less/line.c (revision 8ed69c6ff9a57551420d2cb6f67c989b37477cb5)
1a5f0fb15SPaul Saab /*
2a5f0fb15SPaul Saab  * Copyright (C) 1984-2000  Mark Nudelman
3a5f0fb15SPaul Saab  *
4a5f0fb15SPaul Saab  * You may distribute under the terms of either the GNU General Public
5a5f0fb15SPaul Saab  * License or the Less License, as specified in the README file.
6a5f0fb15SPaul Saab  *
7a5f0fb15SPaul Saab  * For more information about less, or for information on how to
8a5f0fb15SPaul Saab  * contact the author, see the README file.
9a5f0fb15SPaul Saab  */
10a5f0fb15SPaul Saab 
11a5f0fb15SPaul Saab 
12a5f0fb15SPaul Saab /*
13a5f0fb15SPaul Saab  * Routines to manipulate the "line buffer".
14a5f0fb15SPaul Saab  * The line buffer holds a line of output as it is being built
15a5f0fb15SPaul Saab  * in preparation for output to the screen.
16a5f0fb15SPaul Saab  */
17a5f0fb15SPaul Saab 
18a5f0fb15SPaul Saab #include "less.h"
19a5f0fb15SPaul Saab 
20a5f0fb15SPaul Saab #define IS_CONT(c)  (((c) & 0xC0) == 0x80)
218ed69c6fSPaul Saab #define LINENUM_WIDTH   8       /* Chars to use for line number */
22a5f0fb15SPaul Saab 
23a5f0fb15SPaul Saab /* Buffer which holds the current output line */
24a5f0fb15SPaul Saab public char linebuf[LINEBUF_SIZE];
25a5f0fb15SPaul Saab public int size_linebuf = sizeof(linebuf);
26a5f0fb15SPaul Saab 
27a5f0fb15SPaul Saab public int cshift;		/* Current left-shift of output line buffer */
28a5f0fb15SPaul Saab public int hshift;		/* Desired left-shift of output line buffer */
29a5f0fb15SPaul Saab 
30a5f0fb15SPaul Saab static char attr[LINEBUF_SIZE];	/* Extension of linebuf to hold attributes */
31a5f0fb15SPaul Saab static int curr;		/* Index into linebuf */
32a5f0fb15SPaul Saab static int column;		/* Printable length, accounting for
33a5f0fb15SPaul Saab 				   backspaces, etc. */
34a5f0fb15SPaul Saab static int overstrike;		/* Next char should overstrike previous char */
35a5f0fb15SPaul Saab static int is_null_line;	/* There is no current line */
368ed69c6fSPaul Saab static int lmargin;		/* Left margin */
37a5f0fb15SPaul Saab static char pendc;
38a5f0fb15SPaul Saab static POSITION pendpos;
39a5f0fb15SPaul Saab static char *end_ansi_chars;
40a5f0fb15SPaul Saab 
41a5f0fb15SPaul Saab static int do_append();
42a5f0fb15SPaul Saab 
43a5f0fb15SPaul Saab extern int bs_mode;
44a5f0fb15SPaul Saab extern int tabstop;
45a5f0fb15SPaul Saab extern int linenums;
46a5f0fb15SPaul Saab extern int ctldisp;
47a5f0fb15SPaul Saab extern int twiddle;
48a5f0fb15SPaul Saab extern int binattr;
498ed69c6fSPaul Saab extern int status_col;
50a5f0fb15SPaul Saab extern int auto_wrap, ignaw;
51a5f0fb15SPaul Saab extern int bo_s_width, bo_e_width;
52a5f0fb15SPaul Saab extern int ul_s_width, ul_e_width;
53a5f0fb15SPaul Saab extern int bl_s_width, bl_e_width;
54a5f0fb15SPaul Saab extern int so_s_width, so_e_width;
55a5f0fb15SPaul Saab extern int sc_width, sc_height;
56a5f0fb15SPaul Saab extern int utf_mode;
578ed69c6fSPaul Saab extern POSITION start_attnpos;
588ed69c6fSPaul Saab extern POSITION end_attnpos;
59a5f0fb15SPaul Saab 
60a5f0fb15SPaul Saab /*
61a5f0fb15SPaul Saab  * Initialize from environment variables.
62a5f0fb15SPaul Saab  */
63a5f0fb15SPaul Saab 	public void
64a5f0fb15SPaul Saab init_line()
65a5f0fb15SPaul Saab {
66a5f0fb15SPaul Saab 	end_ansi_chars = lgetenv("LESSANSIENDCHARS");
67a5f0fb15SPaul Saab 	if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
68a5f0fb15SPaul Saab 		end_ansi_chars = "m";
69a5f0fb15SPaul Saab }
70a5f0fb15SPaul Saab 
71a5f0fb15SPaul Saab /*
72a5f0fb15SPaul Saab  * Rewind the line buffer.
73a5f0fb15SPaul Saab  */
74a5f0fb15SPaul Saab 	public void
75a5f0fb15SPaul Saab prewind()
76a5f0fb15SPaul Saab {
77a5f0fb15SPaul Saab 	curr = 0;
78a5f0fb15SPaul Saab 	column = 0;
79a5f0fb15SPaul Saab 	overstrike = 0;
80a5f0fb15SPaul Saab 	is_null_line = 0;
81a5f0fb15SPaul Saab 	pendc = '\0';
828ed69c6fSPaul Saab 	lmargin = 0;
838ed69c6fSPaul Saab 	if (status_col)
848ed69c6fSPaul Saab 		lmargin += 1;
858ed69c6fSPaul Saab 	if (linenums == OPT_ONPLUS)
868ed69c6fSPaul Saab 		lmargin += LINENUM_WIDTH+1;
87a5f0fb15SPaul Saab }
88a5f0fb15SPaul Saab 
89a5f0fb15SPaul Saab /*
90a5f0fb15SPaul Saab  * Insert the line number (of the given position) into the line buffer.
91a5f0fb15SPaul Saab  */
92a5f0fb15SPaul Saab 	public void
93a5f0fb15SPaul Saab plinenum(pos)
94a5f0fb15SPaul Saab 	POSITION pos;
95a5f0fb15SPaul Saab {
96a5f0fb15SPaul Saab 	register int lno;
97a5f0fb15SPaul Saab 	register int i;
98a5f0fb15SPaul Saab 
998ed69c6fSPaul Saab 	if (linenums == OPT_ONPLUS)
1008ed69c6fSPaul Saab 	{
101a5f0fb15SPaul Saab 		/*
102a5f0fb15SPaul Saab 		 * Get the line number and put it in the current line.
103a5f0fb15SPaul Saab 		 * {{ Note: since find_linenum calls forw_raw_line,
104a5f0fb15SPaul Saab 		 *    it may seek in the input file, requiring the caller
105a5f0fb15SPaul Saab 		 *    of plinenum to re-seek if necessary. }}
1068ed69c6fSPaul Saab 		 * {{ Since forw_raw_line modifies linebuf, we must
1078ed69c6fSPaul Saab 		 *    do this first, before storing anything in linebuf. }}
108a5f0fb15SPaul Saab 		 */
109a5f0fb15SPaul Saab 		lno = find_linenum(pos);
1108ed69c6fSPaul Saab 	}
111a5f0fb15SPaul Saab 
112a5f0fb15SPaul Saab 	/*
1138ed69c6fSPaul Saab 	 * Display a status column if the -J option is set.
114a5f0fb15SPaul Saab 	 */
1158ed69c6fSPaul Saab 	if (status_col)
1168ed69c6fSPaul Saab 	{
1178ed69c6fSPaul Saab 		linebuf[curr] = ' ';
1188ed69c6fSPaul Saab 		if (start_attnpos != NULL_POSITION &&
1198ed69c6fSPaul Saab 		    pos >= start_attnpos && pos < end_attnpos)
1208ed69c6fSPaul Saab 			attr[curr] = AT_STANDOUT;
1218ed69c6fSPaul Saab 		else
1228ed69c6fSPaul Saab 			attr[curr] = 0;
1238ed69c6fSPaul Saab 		curr++;
1248ed69c6fSPaul Saab 		column++;
1258ed69c6fSPaul Saab 	}
1268ed69c6fSPaul Saab 	/*
1278ed69c6fSPaul Saab 	 * Display the line number at the start of each line
1288ed69c6fSPaul Saab 	 * if the -N option is set.
1298ed69c6fSPaul Saab 	 */
1308ed69c6fSPaul Saab 	if (linenums == OPT_ONPLUS)
1318ed69c6fSPaul Saab 	{
1328ed69c6fSPaul Saab 		sprintf(&linebuf[curr], "%*d", LINENUM_WIDTH, lno);
1338ed69c6fSPaul Saab 		column += LINENUM_WIDTH;
1348ed69c6fSPaul Saab 		for (i = 0;  i < LINENUM_WIDTH;  i++)
1358ed69c6fSPaul Saab 			attr[curr++] = 0;
1368ed69c6fSPaul Saab 	}
1378ed69c6fSPaul Saab 	/*
1388ed69c6fSPaul Saab 	 * Append enough spaces to bring us to the lmargin.
1398ed69c6fSPaul Saab 	 */
1408ed69c6fSPaul Saab 	while (column < lmargin)
141a5f0fb15SPaul Saab 	{
142a5f0fb15SPaul Saab 		linebuf[curr] = ' ';
143a5f0fb15SPaul Saab 		attr[curr++] = AT_NORMAL;
144a5f0fb15SPaul Saab 		column++;
1458ed69c6fSPaul Saab 	}
146a5f0fb15SPaul Saab }
147a5f0fb15SPaul Saab 
148a5f0fb15SPaul Saab /*
149a5f0fb15SPaul Saab  *
150a5f0fb15SPaul Saab  */
151a5f0fb15SPaul Saab 	static int
152a5f0fb15SPaul Saab utf_len(char *s, int len)
153a5f0fb15SPaul Saab {
154a5f0fb15SPaul Saab 	int ulen = 0;
155a5f0fb15SPaul Saab 
156a5f0fb15SPaul Saab 	while (*s != '\0' && len > 0)
157a5f0fb15SPaul Saab 	{
158a5f0fb15SPaul Saab 		if (!IS_CONT(*s))
159a5f0fb15SPaul Saab 			len--;
160a5f0fb15SPaul Saab 		s++;
161a5f0fb15SPaul Saab 		ulen++;
162a5f0fb15SPaul Saab 	}
163a5f0fb15SPaul Saab 	while (IS_CONT(*s))
164a5f0fb15SPaul Saab 	{
165a5f0fb15SPaul Saab 		s++;
166a5f0fb15SPaul Saab 		ulen++;
167a5f0fb15SPaul Saab 	}
168a5f0fb15SPaul Saab 	return (ulen);
169a5f0fb15SPaul Saab }
170a5f0fb15SPaul Saab 
171a5f0fb15SPaul Saab /*
172a5f0fb15SPaul Saab  * Shift the input line left.
173a5f0fb15SPaul Saab  * This means discarding N printable chars at the start of the buffer.
174a5f0fb15SPaul Saab  */
175a5f0fb15SPaul Saab 	static void
176a5f0fb15SPaul Saab pshift(shift)
177a5f0fb15SPaul Saab 	int shift;
178a5f0fb15SPaul Saab {
179a5f0fb15SPaul Saab 	int i;
180a5f0fb15SPaul Saab 	int real_shift;
181a5f0fb15SPaul Saab 
1828ed69c6fSPaul Saab 	if (shift > column - lmargin)
1838ed69c6fSPaul Saab 		shift = column - lmargin;
1848ed69c6fSPaul Saab 	if (shift > curr - lmargin)
1858ed69c6fSPaul Saab 		shift = curr - lmargin;
186a5f0fb15SPaul Saab 
187a5f0fb15SPaul Saab 	if (!utf_mode)
188a5f0fb15SPaul Saab 		real_shift = shift;
189a5f0fb15SPaul Saab 	else
190a5f0fb15SPaul Saab 	{
1918ed69c6fSPaul Saab 		real_shift = utf_len(linebuf + lmargin, shift);
192a5f0fb15SPaul Saab 		if (real_shift > curr)
193a5f0fb15SPaul Saab 			real_shift = curr;
194a5f0fb15SPaul Saab 	}
195a5f0fb15SPaul Saab 	for (i = 0;  i < curr - real_shift;  i++)
196a5f0fb15SPaul Saab 	{
1978ed69c6fSPaul Saab 		linebuf[lmargin + i] = linebuf[lmargin + i + real_shift];
1988ed69c6fSPaul Saab 		attr[lmargin + i] = attr[lmargin + i + real_shift];
199a5f0fb15SPaul Saab 	}
200a5f0fb15SPaul Saab 	column -= shift;
201a5f0fb15SPaul Saab 	curr -= real_shift;
202a5f0fb15SPaul Saab 	cshift += shift;
203a5f0fb15SPaul Saab }
204a5f0fb15SPaul Saab 
205a5f0fb15SPaul Saab /*
206a5f0fb15SPaul Saab  * Return the printing width of the start (enter) sequence
207a5f0fb15SPaul Saab  * for a given character attribute.
208a5f0fb15SPaul Saab  */
209a5f0fb15SPaul Saab 	static int
210a5f0fb15SPaul Saab attr_swidth(a)
211a5f0fb15SPaul Saab 	int a;
212a5f0fb15SPaul Saab {
213a5f0fb15SPaul Saab 	switch (a)
214a5f0fb15SPaul Saab 	{
215a5f0fb15SPaul Saab 	case AT_BOLD:		return (bo_s_width);
216a5f0fb15SPaul Saab 	case AT_UNDERLINE:	return (ul_s_width);
217a5f0fb15SPaul Saab 	case AT_BLINK:		return (bl_s_width);
218a5f0fb15SPaul Saab 	case AT_STANDOUT:	return (so_s_width);
219a5f0fb15SPaul Saab 	}
220a5f0fb15SPaul Saab 	return (0);
221a5f0fb15SPaul Saab }
222a5f0fb15SPaul Saab 
223a5f0fb15SPaul Saab /*
224a5f0fb15SPaul Saab  * Return the printing width of the end (exit) sequence
225a5f0fb15SPaul Saab  * for a given character attribute.
226a5f0fb15SPaul Saab  */
227a5f0fb15SPaul Saab 	static int
228a5f0fb15SPaul Saab attr_ewidth(a)
229a5f0fb15SPaul Saab 	int a;
230a5f0fb15SPaul Saab {
231a5f0fb15SPaul Saab 	switch (a)
232a5f0fb15SPaul Saab 	{
233a5f0fb15SPaul Saab 	case AT_BOLD:		return (bo_e_width);
234a5f0fb15SPaul Saab 	case AT_UNDERLINE:	return (ul_e_width);
235a5f0fb15SPaul Saab 	case AT_BLINK:		return (bl_e_width);
236a5f0fb15SPaul Saab 	case AT_STANDOUT:	return (so_e_width);
237a5f0fb15SPaul Saab 	}
238a5f0fb15SPaul Saab 	return (0);
239a5f0fb15SPaul Saab }
240a5f0fb15SPaul Saab 
241a5f0fb15SPaul Saab /*
242a5f0fb15SPaul Saab  * Return the printing width of a given character and attribute,
243a5f0fb15SPaul Saab  * if the character were added to the current position in the line buffer.
244a5f0fb15SPaul Saab  * Adding a character with a given attribute may cause an enter or exit
245a5f0fb15SPaul Saab  * attribute sequence to be inserted, so this must be taken into account.
246a5f0fb15SPaul Saab  */
247a5f0fb15SPaul Saab 	static int
248a5f0fb15SPaul Saab pwidth(c, a)
249a5f0fb15SPaul Saab 	int c;
250a5f0fb15SPaul Saab 	int a;
251a5f0fb15SPaul Saab {
252a5f0fb15SPaul Saab 	register int w;
253a5f0fb15SPaul Saab 
254a5f0fb15SPaul Saab 	if (utf_mode && IS_CONT(c))
255a5f0fb15SPaul Saab 		return (0);
256a5f0fb15SPaul Saab 
257a5f0fb15SPaul Saab 	if (c == '\b')
258a5f0fb15SPaul Saab 		/*
259a5f0fb15SPaul Saab 		 * Backspace moves backwards one position.
260a5f0fb15SPaul Saab 		 */
261a5f0fb15SPaul Saab 		return (-1);
262a5f0fb15SPaul Saab 
263a5f0fb15SPaul Saab 	if (control_char(c))
264a5f0fb15SPaul Saab 		/*
265a5f0fb15SPaul Saab 		 * Control characters do unpredicatable things,
266a5f0fb15SPaul Saab 		 * so we don't even try to guess; say it doesn't move.
267a5f0fb15SPaul Saab 		 * This can only happen if the -r flag is in effect.
268a5f0fb15SPaul Saab 		 */
269a5f0fb15SPaul Saab 		return (0);
270a5f0fb15SPaul Saab 
271a5f0fb15SPaul Saab 	/*
272a5f0fb15SPaul Saab 	 * Other characters take one space,
273a5f0fb15SPaul Saab 	 * plus the width of any attribute enter/exit sequence.
274a5f0fb15SPaul Saab 	 */
275a5f0fb15SPaul Saab 	w = 1;
276a5f0fb15SPaul Saab 	if (curr > 0 && attr[curr-1] != a)
277a5f0fb15SPaul Saab 		w += attr_ewidth(attr[curr-1]);
278a5f0fb15SPaul Saab 	if (a && (curr == 0 || attr[curr-1] != a))
279a5f0fb15SPaul Saab 		w += attr_swidth(a);
280a5f0fb15SPaul Saab 	return (w);
281a5f0fb15SPaul Saab }
282a5f0fb15SPaul Saab 
283a5f0fb15SPaul Saab /*
284a5f0fb15SPaul Saab  * Delete the previous character in the line buffer.
285a5f0fb15SPaul Saab  */
286a5f0fb15SPaul Saab 	static void
287a5f0fb15SPaul Saab backc()
288a5f0fb15SPaul Saab {
289a5f0fb15SPaul Saab 	curr--;
290a5f0fb15SPaul Saab 	column -= pwidth(linebuf[curr], attr[curr]);
291a5f0fb15SPaul Saab }
292a5f0fb15SPaul Saab 
293a5f0fb15SPaul Saab /*
294a5f0fb15SPaul Saab  * Are we currently within a recognized ANSI escape sequence?
295a5f0fb15SPaul Saab  */
296a5f0fb15SPaul Saab 	static int
297a5f0fb15SPaul Saab in_ansi_esc_seq()
298a5f0fb15SPaul Saab {
299a5f0fb15SPaul Saab 	int i;
300a5f0fb15SPaul Saab 
301a5f0fb15SPaul Saab 	/*
302a5f0fb15SPaul Saab 	 * Search backwards for either an ESC (which means we ARE in a seq);
303a5f0fb15SPaul Saab 	 * or an end char (which means we're NOT in a seq).
304a5f0fb15SPaul Saab 	 */
305a5f0fb15SPaul Saab 	for (i = curr-1;  i >= 0;  i--)
306a5f0fb15SPaul Saab 	{
307a5f0fb15SPaul Saab 		if (linebuf[i] == ESC)
308a5f0fb15SPaul Saab 			return (1);
309a5f0fb15SPaul Saab 		if (strchr(end_ansi_chars, linebuf[i]) != NULL)
310a5f0fb15SPaul Saab 			return (0);
311a5f0fb15SPaul Saab 	}
312a5f0fb15SPaul Saab 	return (0);
313a5f0fb15SPaul Saab }
314a5f0fb15SPaul Saab 
315a5f0fb15SPaul Saab /*
316a5f0fb15SPaul Saab  * Append a character and attribute to the line buffer.
317a5f0fb15SPaul Saab  */
318a5f0fb15SPaul Saab 	static int
319a5f0fb15SPaul Saab storec(c, a, pos)
320a5f0fb15SPaul Saab 	int c;
321a5f0fb15SPaul Saab 	int a;
322a5f0fb15SPaul Saab 	POSITION pos;
323a5f0fb15SPaul Saab {
324a5f0fb15SPaul Saab 	register int w;
325a5f0fb15SPaul Saab 
326a5f0fb15SPaul Saab #if HILITE_SEARCH
327a5f0fb15SPaul Saab 	if (is_hilited(pos, pos+1, 0))
328a5f0fb15SPaul Saab 		/*
329a5f0fb15SPaul Saab 		 * This character should be highlighted.
330a5f0fb15SPaul Saab 		 * Override the attribute passed in.
331a5f0fb15SPaul Saab 		 */
332a5f0fb15SPaul Saab 		a = AT_STANDOUT;
333a5f0fb15SPaul Saab #endif
334a5f0fb15SPaul Saab 	if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
335a5f0fb15SPaul Saab 		w = 0;
336a5f0fb15SPaul Saab 	else
337a5f0fb15SPaul Saab 		w = pwidth(c, a);
338a5f0fb15SPaul Saab 	if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
339a5f0fb15SPaul Saab 		/*
340a5f0fb15SPaul Saab 		 * Won't fit on screen.
341a5f0fb15SPaul Saab 		 */
342a5f0fb15SPaul Saab 		return (1);
343a5f0fb15SPaul Saab 
344a5f0fb15SPaul Saab 	if (curr >= sizeof(linebuf)-2)
345a5f0fb15SPaul Saab 		/*
346a5f0fb15SPaul Saab 		 * Won't fit in line buffer.
347a5f0fb15SPaul Saab 		 */
348a5f0fb15SPaul Saab 		return (1);
349a5f0fb15SPaul Saab 
350a5f0fb15SPaul Saab 	/*
351a5f0fb15SPaul Saab 	 * Special handling for "magic cookie" terminals.
352a5f0fb15SPaul Saab 	 * If an attribute enter/exit sequence has a printing width > 0,
353a5f0fb15SPaul Saab 	 * and the sequence is adjacent to a space, delete the space.
354a5f0fb15SPaul Saab 	 * We just mark the space as invisible, to avoid having too
355a5f0fb15SPaul Saab 	 * many spaces deleted.
356a5f0fb15SPaul Saab 	 * {{ Note that even if the attribute width is > 1, we
357a5f0fb15SPaul Saab 	 *    delete only one space.  It's not worth trying to do more.
358a5f0fb15SPaul Saab 	 *    It's hardly worth doing this much. }}
359a5f0fb15SPaul Saab 	 */
360a5f0fb15SPaul Saab 	if (curr > 0 && a != AT_NORMAL &&
361a5f0fb15SPaul Saab 		linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL &&
362a5f0fb15SPaul Saab 		attr_swidth(a) > 0)
363a5f0fb15SPaul Saab 	{
364a5f0fb15SPaul Saab 		/*
365a5f0fb15SPaul Saab 		 * We are about to append an enter-attribute sequence
366a5f0fb15SPaul Saab 		 * just after a space.  Delete the space.
367a5f0fb15SPaul Saab 		 */
368a5f0fb15SPaul Saab 		attr[curr-1] = AT_INVIS;
369a5f0fb15SPaul Saab 		column--;
370a5f0fb15SPaul Saab 	} else if (curr > 0 && attr[curr-1] != AT_NORMAL &&
371a5f0fb15SPaul Saab 		attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL &&
372a5f0fb15SPaul Saab 		attr_ewidth(attr[curr-1]) > 0)
373a5f0fb15SPaul Saab 	{
374a5f0fb15SPaul Saab 		/*
375a5f0fb15SPaul Saab 		 * We are about to append a space just after an
376a5f0fb15SPaul Saab 		 * exit-attribute sequence.  Delete the space.
377a5f0fb15SPaul Saab 		 */
378a5f0fb15SPaul Saab 		a = AT_INVIS;
379a5f0fb15SPaul Saab 		column--;
380a5f0fb15SPaul Saab 	}
381a5f0fb15SPaul Saab 	/* End of magic cookie handling. */
382a5f0fb15SPaul Saab 
383a5f0fb15SPaul Saab 	linebuf[curr] = c;
384a5f0fb15SPaul Saab 	attr[curr] = a;
385a5f0fb15SPaul Saab 	column += w;
386a5f0fb15SPaul Saab 	return (0);
387a5f0fb15SPaul Saab }
388a5f0fb15SPaul Saab 
389a5f0fb15SPaul Saab /*
390a5f0fb15SPaul Saab  * Append a character to the line buffer.
391a5f0fb15SPaul Saab  * Expand tabs into spaces, handle underlining, boldfacing, etc.
392a5f0fb15SPaul Saab  * Returns 0 if ok, 1 if couldn't fit in buffer.
393a5f0fb15SPaul Saab  */
394a5f0fb15SPaul Saab 	public int
395a5f0fb15SPaul Saab pappend(c, pos)
396a5f0fb15SPaul Saab 	register int c;
397a5f0fb15SPaul Saab 	POSITION pos;
398a5f0fb15SPaul Saab {
399a5f0fb15SPaul Saab 	int r;
400a5f0fb15SPaul Saab 
401a5f0fb15SPaul Saab 	if (pendc)
402a5f0fb15SPaul Saab 	{
403a5f0fb15SPaul Saab 		if (do_append(pendc, pendpos))
404a5f0fb15SPaul Saab 			/*
405a5f0fb15SPaul Saab 			 * Oops.  We've probably lost the char which
406a5f0fb15SPaul Saab 			 * was in pendc, since caller won't back up.
407a5f0fb15SPaul Saab 			 */
408a5f0fb15SPaul Saab 			return (1);
409a5f0fb15SPaul Saab 		pendc = '\0';
410a5f0fb15SPaul Saab 	}
411a5f0fb15SPaul Saab 
412a5f0fb15SPaul Saab 	if (c == '\r' && bs_mode == BS_SPECIAL)
413a5f0fb15SPaul Saab 	{
414a5f0fb15SPaul Saab 		/*
415a5f0fb15SPaul Saab 		 * Don't put the CR into the buffer until we see
416a5f0fb15SPaul Saab 		 * the next char.  If the next char is a newline,
417a5f0fb15SPaul Saab 		 * discard the CR.
418a5f0fb15SPaul Saab 		 */
419a5f0fb15SPaul Saab 		pendc = c;
420a5f0fb15SPaul Saab 		pendpos = pos;
421a5f0fb15SPaul Saab 		return (0);
422a5f0fb15SPaul Saab 	}
423a5f0fb15SPaul Saab 
424a5f0fb15SPaul Saab 	r = do_append(c, pos);
425a5f0fb15SPaul Saab 	/*
426a5f0fb15SPaul Saab 	 * If we need to shift the line, do it.
427a5f0fb15SPaul Saab 	 * But wait until we get to at least the middle of the screen,
428a5f0fb15SPaul Saab 	 * so shifting it doesn't affect the chars we're currently
429a5f0fb15SPaul Saab 	 * pappending.  (Bold & underline can get messed up otherwise.)
430a5f0fb15SPaul Saab 	 */
431a5f0fb15SPaul Saab 	if (cshift < hshift && column > sc_width / 2)
432a5f0fb15SPaul Saab 		pshift(hshift - cshift);
433a5f0fb15SPaul Saab 	return (r);
434a5f0fb15SPaul Saab }
435a5f0fb15SPaul Saab 
436a5f0fb15SPaul Saab 	static int
437a5f0fb15SPaul Saab do_append(c, pos)
438a5f0fb15SPaul Saab 	int c;
439a5f0fb15SPaul Saab 	POSITION pos;
440a5f0fb15SPaul Saab {
441a5f0fb15SPaul Saab 	register char *s;
442a5f0fb15SPaul Saab 	register int a;
443a5f0fb15SPaul Saab 
444a5f0fb15SPaul Saab #define	STOREC(c,a) \
445a5f0fb15SPaul Saab 	if (storec((c),(a),pos)) return (1); else curr++
446a5f0fb15SPaul Saab 
447a5f0fb15SPaul Saab 	if (c == '\b')
448a5f0fb15SPaul Saab 	{
449a5f0fb15SPaul Saab 		switch (bs_mode)
450a5f0fb15SPaul Saab 		{
451a5f0fb15SPaul Saab 		case BS_NORMAL:
452a5f0fb15SPaul Saab 			STOREC(c, AT_NORMAL);
453a5f0fb15SPaul Saab 			break;
454a5f0fb15SPaul Saab 		case BS_CONTROL:
455a5f0fb15SPaul Saab 			goto do_control_char;
456a5f0fb15SPaul Saab 		case BS_SPECIAL:
457a5f0fb15SPaul Saab 			if (curr == 0)
458a5f0fb15SPaul Saab 				break;
459a5f0fb15SPaul Saab 			backc();
460a5f0fb15SPaul Saab 			overstrike = 1;
461a5f0fb15SPaul Saab 			break;
462a5f0fb15SPaul Saab 		}
463a5f0fb15SPaul Saab 	} else if (overstrike)
464a5f0fb15SPaul Saab 	{
465a5f0fb15SPaul Saab 		/*
466a5f0fb15SPaul Saab 		 * Overstrike the character at the current position
467a5f0fb15SPaul Saab 		 * in the line buffer.  This will cause either
468a5f0fb15SPaul Saab 		 * underline (if a "_" is overstruck),
469a5f0fb15SPaul Saab 		 * bold (if an identical character is overstruck),
470a5f0fb15SPaul Saab 		 * or just deletion of the character in the buffer.
471a5f0fb15SPaul Saab 		 */
472a5f0fb15SPaul Saab 		overstrike = 0;
473a5f0fb15SPaul Saab 		if ((char)c == linebuf[curr])
474a5f0fb15SPaul Saab 			STOREC(linebuf[curr], AT_BOLD);
475a5f0fb15SPaul Saab 		else if (c == '_')
476a5f0fb15SPaul Saab 			STOREC(linebuf[curr], AT_UNDERLINE);
477a5f0fb15SPaul Saab 		else if (linebuf[curr] == '_')
478a5f0fb15SPaul Saab 			STOREC(c, AT_UNDERLINE);
479a5f0fb15SPaul Saab 		else if (control_char(c))
480a5f0fb15SPaul Saab 			goto do_control_char;
481a5f0fb15SPaul Saab 		else
482a5f0fb15SPaul Saab 			STOREC(c, AT_NORMAL);
483a5f0fb15SPaul Saab 	} else if (c == '\t')
484a5f0fb15SPaul Saab 	{
485a5f0fb15SPaul Saab 		/*
486a5f0fb15SPaul Saab 		 * Expand a tab into spaces.
487a5f0fb15SPaul Saab 		 */
488a5f0fb15SPaul Saab 		if (tabstop == 0)
489a5f0fb15SPaul Saab 			tabstop = 1;
490a5f0fb15SPaul Saab 		switch (bs_mode)
491a5f0fb15SPaul Saab 		{
492a5f0fb15SPaul Saab 		case BS_CONTROL:
493a5f0fb15SPaul Saab 			goto do_control_char;
494a5f0fb15SPaul Saab 		case BS_NORMAL:
495a5f0fb15SPaul Saab 		case BS_SPECIAL:
496a5f0fb15SPaul Saab 			do
497a5f0fb15SPaul Saab 			{
498a5f0fb15SPaul Saab 				STOREC(' ', AT_NORMAL);
4998ed69c6fSPaul Saab 			} while (((column + cshift - lmargin) % tabstop) != 0);
500a5f0fb15SPaul Saab 			break;
501a5f0fb15SPaul Saab 		}
502a5f0fb15SPaul Saab 	} else if (control_char(c))
503a5f0fb15SPaul Saab 	{
504a5f0fb15SPaul Saab 	do_control_char:
505a5f0fb15SPaul Saab 		if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && c == ESC))
506a5f0fb15SPaul Saab 		{
507a5f0fb15SPaul Saab 			/*
508a5f0fb15SPaul Saab 			 * Output as a normal character.
509a5f0fb15SPaul Saab 			 */
510a5f0fb15SPaul Saab 			STOREC(c, AT_NORMAL);
511a5f0fb15SPaul Saab 		} else
512a5f0fb15SPaul Saab 		{
513a5f0fb15SPaul Saab 			/*
514a5f0fb15SPaul Saab 			 * Convert to printable representation.
515a5f0fb15SPaul Saab 			 */
516a5f0fb15SPaul Saab 			s = prchar(c);
517a5f0fb15SPaul Saab 			a = binattr;
518a5f0fb15SPaul Saab 
519a5f0fb15SPaul Saab 			/*
520a5f0fb15SPaul Saab 			 * Make sure we can get the entire representation
521a5f0fb15SPaul Saab 			 * of the character on this line.
522a5f0fb15SPaul Saab 			 */
523a5f0fb15SPaul Saab 			if (column + (int) strlen(s) +
524a5f0fb15SPaul Saab 			    attr_swidth(a) + attr_ewidth(a) > sc_width)
525a5f0fb15SPaul Saab 				return (1);
526a5f0fb15SPaul Saab 
527a5f0fb15SPaul Saab 			for ( ;  *s != 0;  s++)
528a5f0fb15SPaul Saab 				STOREC(*s, a);
529a5f0fb15SPaul Saab 		}
530a5f0fb15SPaul Saab 	} else
531a5f0fb15SPaul Saab 	{
532a5f0fb15SPaul Saab 		STOREC(c, AT_NORMAL);
533a5f0fb15SPaul Saab 	}
534a5f0fb15SPaul Saab 
535a5f0fb15SPaul Saab 	return (0);
536a5f0fb15SPaul Saab }
537a5f0fb15SPaul Saab 
538a5f0fb15SPaul Saab /*
539a5f0fb15SPaul Saab  * Terminate the line in the line buffer.
540a5f0fb15SPaul Saab  */
541a5f0fb15SPaul Saab 	public void
542a5f0fb15SPaul Saab pdone(endline)
543a5f0fb15SPaul Saab 	int endline;
544a5f0fb15SPaul Saab {
545a5f0fb15SPaul Saab 	if (pendc && (pendc != '\r' || !endline))
546a5f0fb15SPaul Saab 		/*
547a5f0fb15SPaul Saab 		 * If we had a pending character, put it in the buffer.
548a5f0fb15SPaul Saab 		 * But discard a pending CR if we are at end of line
549a5f0fb15SPaul Saab 		 * (that is, discard the CR in a CR/LF sequence).
550a5f0fb15SPaul Saab 		 */
551a5f0fb15SPaul Saab 		(void) do_append(pendc, pendpos);
552a5f0fb15SPaul Saab 
553a5f0fb15SPaul Saab 	/*
554a5f0fb15SPaul Saab 	 * Make sure we've shifted the line, if we need to.
555a5f0fb15SPaul Saab 	 */
556a5f0fb15SPaul Saab 	if (cshift < hshift)
557a5f0fb15SPaul Saab 		pshift(hshift - cshift);
558a5f0fb15SPaul Saab 
559a5f0fb15SPaul Saab 	/*
560a5f0fb15SPaul Saab 	 * Add a newline if necessary,
561a5f0fb15SPaul Saab 	 * and append a '\0' to the end of the line.
562a5f0fb15SPaul Saab 	 */
563a5f0fb15SPaul Saab 	if (column < sc_width || !auto_wrap || ignaw || ctldisp == OPT_ON)
564a5f0fb15SPaul Saab 	{
565a5f0fb15SPaul Saab 		linebuf[curr] = '\n';
566a5f0fb15SPaul Saab 		attr[curr] = AT_NORMAL;
567a5f0fb15SPaul Saab 		curr++;
568a5f0fb15SPaul Saab 	}
569a5f0fb15SPaul Saab 	linebuf[curr] = '\0';
570a5f0fb15SPaul Saab 	attr[curr] = AT_NORMAL;
571a5f0fb15SPaul Saab 	/*
572a5f0fb15SPaul Saab 	 * If we are done with this line, reset the current shift.
573a5f0fb15SPaul Saab 	 */
574a5f0fb15SPaul Saab 	if (endline)
575a5f0fb15SPaul Saab 		cshift = 0;
576a5f0fb15SPaul Saab }
577a5f0fb15SPaul Saab 
578a5f0fb15SPaul Saab /*
579a5f0fb15SPaul Saab  * Get a character from the current line.
580a5f0fb15SPaul Saab  * Return the character as the function return value,
581a5f0fb15SPaul Saab  * and the character attribute in *ap.
582a5f0fb15SPaul Saab  */
583a5f0fb15SPaul Saab 	public int
584a5f0fb15SPaul Saab gline(i, ap)
585a5f0fb15SPaul Saab 	register int i;
586a5f0fb15SPaul Saab 	register int *ap;
587a5f0fb15SPaul Saab {
588a5f0fb15SPaul Saab 	char *s;
589a5f0fb15SPaul Saab 
590a5f0fb15SPaul Saab 	if (is_null_line)
591a5f0fb15SPaul Saab 	{
592a5f0fb15SPaul Saab 		/*
593a5f0fb15SPaul Saab 		 * If there is no current line, we pretend the line is
594a5f0fb15SPaul Saab 		 * either "~" or "", depending on the "twiddle" flag.
595a5f0fb15SPaul Saab 		 */
596a5f0fb15SPaul Saab 		*ap = AT_BOLD;
597a5f0fb15SPaul Saab 		s = (twiddle) ? "~\n" : "\n";
598a5f0fb15SPaul Saab 		return (s[i]);
599a5f0fb15SPaul Saab 	}
600a5f0fb15SPaul Saab 
601a5f0fb15SPaul Saab 	*ap = attr[i];
602a5f0fb15SPaul Saab 	return (linebuf[i] & 0377);
603a5f0fb15SPaul Saab }
604a5f0fb15SPaul Saab 
605a5f0fb15SPaul Saab /*
606a5f0fb15SPaul Saab  * Indicate that there is no current line.
607a5f0fb15SPaul Saab  */
608a5f0fb15SPaul Saab 	public void
609a5f0fb15SPaul Saab null_line()
610a5f0fb15SPaul Saab {
611a5f0fb15SPaul Saab 	is_null_line = 1;
612a5f0fb15SPaul Saab 	cshift = 0;
613a5f0fb15SPaul Saab }
614a5f0fb15SPaul Saab 
615a5f0fb15SPaul Saab /*
616a5f0fb15SPaul Saab  * Analogous to forw_line(), but deals with "raw lines":
617a5f0fb15SPaul Saab  * lines which are not split for screen width.
618a5f0fb15SPaul Saab  * {{ This is supposed to be more efficient than forw_line(). }}
619a5f0fb15SPaul Saab  */
620a5f0fb15SPaul Saab 	public POSITION
621a5f0fb15SPaul Saab forw_raw_line(curr_pos, linep)
622a5f0fb15SPaul Saab 	POSITION curr_pos;
623a5f0fb15SPaul Saab 	char **linep;
624a5f0fb15SPaul Saab {
625a5f0fb15SPaul Saab 	register char *p;
626a5f0fb15SPaul Saab 	register int c;
627a5f0fb15SPaul Saab 	POSITION new_pos;
628a5f0fb15SPaul Saab 
629a5f0fb15SPaul Saab 	if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
630a5f0fb15SPaul Saab 		(c = ch_forw_get()) == EOI)
631a5f0fb15SPaul Saab 		return (NULL_POSITION);
632a5f0fb15SPaul Saab 
633a5f0fb15SPaul Saab 	p = linebuf;
634a5f0fb15SPaul Saab 
635a5f0fb15SPaul Saab 	for (;;)
636a5f0fb15SPaul Saab 	{
637a5f0fb15SPaul Saab 		if (c == '\n' || c == EOI)
638a5f0fb15SPaul Saab 		{
639a5f0fb15SPaul Saab 			new_pos = ch_tell();
640a5f0fb15SPaul Saab 			break;
641a5f0fb15SPaul Saab 		}
642a5f0fb15SPaul Saab 		if (p >= &linebuf[sizeof(linebuf)-1])
643a5f0fb15SPaul Saab 		{
644a5f0fb15SPaul Saab 			/*
645a5f0fb15SPaul Saab 			 * Overflowed the input buffer.
646a5f0fb15SPaul Saab 			 * Pretend the line ended here.
647a5f0fb15SPaul Saab 			 * {{ The line buffer is supposed to be big
648a5f0fb15SPaul Saab 			 *    enough that this never happens. }}
649a5f0fb15SPaul Saab 			 */
650a5f0fb15SPaul Saab 			new_pos = ch_tell() - 1;
651a5f0fb15SPaul Saab 			break;
652a5f0fb15SPaul Saab 		}
653a5f0fb15SPaul Saab 		*p++ = c;
654a5f0fb15SPaul Saab 		c = ch_forw_get();
655a5f0fb15SPaul Saab 	}
656a5f0fb15SPaul Saab 	*p = '\0';
657a5f0fb15SPaul Saab 	if (linep != NULL)
658a5f0fb15SPaul Saab 		*linep = linebuf;
659a5f0fb15SPaul Saab 	return (new_pos);
660a5f0fb15SPaul Saab }
661a5f0fb15SPaul Saab 
662a5f0fb15SPaul Saab /*
663a5f0fb15SPaul Saab  * Analogous to back_line(), but deals with "raw lines".
664a5f0fb15SPaul Saab  * {{ This is supposed to be more efficient than back_line(). }}
665a5f0fb15SPaul Saab  */
666a5f0fb15SPaul Saab 	public POSITION
667a5f0fb15SPaul Saab back_raw_line(curr_pos, linep)
668a5f0fb15SPaul Saab 	POSITION curr_pos;
669a5f0fb15SPaul Saab 	char **linep;
670a5f0fb15SPaul Saab {
671a5f0fb15SPaul Saab 	register char *p;
672a5f0fb15SPaul Saab 	register int c;
673a5f0fb15SPaul Saab 	POSITION new_pos;
674a5f0fb15SPaul Saab 
675a5f0fb15SPaul Saab 	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
676a5f0fb15SPaul Saab 		ch_seek(curr_pos-1))
677a5f0fb15SPaul Saab 		return (NULL_POSITION);
678a5f0fb15SPaul Saab 
679a5f0fb15SPaul Saab 	p = &linebuf[sizeof(linebuf)];
680a5f0fb15SPaul Saab 	*--p = '\0';
681a5f0fb15SPaul Saab 
682a5f0fb15SPaul Saab 	for (;;)
683a5f0fb15SPaul Saab 	{
684a5f0fb15SPaul Saab 		c = ch_back_get();
685a5f0fb15SPaul Saab 		if (c == '\n')
686a5f0fb15SPaul Saab 		{
687a5f0fb15SPaul Saab 			/*
688a5f0fb15SPaul Saab 			 * This is the newline ending the previous line.
689a5f0fb15SPaul Saab 			 * We have hit the beginning of the line.
690a5f0fb15SPaul Saab 			 */
691a5f0fb15SPaul Saab 			new_pos = ch_tell() + 1;
692a5f0fb15SPaul Saab 			break;
693a5f0fb15SPaul Saab 		}
694a5f0fb15SPaul Saab 		if (c == EOI)
695a5f0fb15SPaul Saab 		{
696a5f0fb15SPaul Saab 			/*
697a5f0fb15SPaul Saab 			 * We have hit the beginning of the file.
698a5f0fb15SPaul Saab 			 * This must be the first line in the file.
699a5f0fb15SPaul Saab 			 * This must, of course, be the beginning of the line.
700a5f0fb15SPaul Saab 			 */
701a5f0fb15SPaul Saab 			new_pos = ch_zero();
702a5f0fb15SPaul Saab 			break;
703a5f0fb15SPaul Saab 		}
704a5f0fb15SPaul Saab 		if (p <= linebuf)
705a5f0fb15SPaul Saab 		{
706a5f0fb15SPaul Saab 			/*
707a5f0fb15SPaul Saab 			 * Overflowed the input buffer.
708a5f0fb15SPaul Saab 			 * Pretend the line ended here.
709a5f0fb15SPaul Saab 			 */
710a5f0fb15SPaul Saab 			new_pos = ch_tell() + 1;
711a5f0fb15SPaul Saab 			break;
712a5f0fb15SPaul Saab 		}
713a5f0fb15SPaul Saab 		*--p = c;
714a5f0fb15SPaul Saab 	}
715a5f0fb15SPaul Saab 	if (linep != NULL)
716a5f0fb15SPaul Saab 		*linep = p;
717a5f0fb15SPaul Saab 	return (new_pos);
718a5f0fb15SPaul Saab }
719