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