line.c revision 63128
1157114Sscottl/*
2157114Sscottl * Copyright (C) 1984-2000  Mark Nudelman
3157114Sscottl *
4157114Sscottl * You may distribute under the terms of either the GNU General Public
5157114Sscottl * License or the Less License, as specified in the README file.
6157114Sscottl *
7157114Sscottl * For more information about less, or for information on how to
8157114Sscottl * contact the author, see the README file.
9157114Sscottl */
10157114Sscottl
11157114Sscottl
12157114Sscottl/*
13157114Sscottl * Routines to manipulate the "line buffer".
14157114Sscottl * The line buffer holds a line of output as it is being built
15157114Sscottl * in preparation for output to the screen.
16157114Sscottl */
17157114Sscottl
18157114Sscottl#include "less.h"
19157114Sscottl
20157114Sscottl#define IS_CONT(c)  (((c) & 0xC0) == 0x80)
21157114Sscottl#define LINENUM_WIDTH   8       /* Chars to use for line number */
22157114Sscottl
23157114Sscottl/* Buffer which holds the current output line */
24157114Sscottlpublic char linebuf[LINEBUF_SIZE];
25157114Sscottlpublic int size_linebuf = sizeof(linebuf);
26171980Sscottl
27171980Sscottlpublic int cshift;		/* Current left-shift of output line buffer */
28171980Sscottlpublic int hshift;		/* Desired left-shift of output line buffer */
29171980Sscottl
30171980Sscottlstatic char attr[LINEBUF_SIZE];	/* Extension of linebuf to hold attributes */
31171980Sscottlstatic int curr;		/* Index into linebuf */
32171980Sscottlstatic int column;		/* Printable length, accounting for
33171980Sscottl				   backspaces, etc. */
34171980Sscottlstatic int overstrike;		/* Next char should overstrike previous char */
35171980Sscottlstatic int is_null_line;	/* There is no current line */
36171980Sscottlstatic int lmargin;		/* Left margin */
37171980Sscottlstatic char pendc;
38171980Sscottlstatic POSITION pendpos;
39171980Sscottlstatic char *end_ansi_chars;
40171980Sscottl
41171980Sscottlstatic int do_append();
42171980Sscottl
43171980Sscottlextern int bs_mode;
44171980Sscottlextern int tabstop;
45171980Sscottlextern int linenums;
46171980Sscottlextern int ctldisp;
47171980Sscottlextern int twiddle;
48171980Sscottlextern int binattr;
49171980Sscottlextern int status_col;
50171980Sscottlextern int auto_wrap, ignaw;
51171980Sscottlextern int bo_s_width, bo_e_width;
52157114Sscottlextern int ul_s_width, ul_e_width;
53157114Sscottlextern int bl_s_width, bl_e_width;
54157114Sscottlextern int so_s_width, so_e_width;
55157114Sscottlextern int sc_width, sc_height;
56157114Sscottlextern int utf_mode;
57157114Sscottlextern POSITION start_attnpos;
58157114Sscottlextern POSITION end_attnpos;
59157114Sscottl
60157114Sscottl/*
61157114Sscottl * Initialize from environment variables.
62157114Sscottl */
63157114Sscottl	public void
64157114Sscottlinit_line()
65157114Sscottl{
66157114Sscottl	end_ansi_chars = lgetenv("LESSANSIENDCHARS");
67233711Sambrisko	if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
68157114Sscottl		end_ansi_chars = "m";
69157114Sscottl}
70157114Sscottl
71157114Sscottl/*
72157114Sscottl * Rewind the line buffer.
73157114Sscottl */
74157114Sscottl	public void
75157114Sscottlprewind()
76157114Sscottl{
77157114Sscottl	curr = 0;
78157114Sscottl	column = 0;
79157114Sscottl	overstrike = 0;
80157114Sscottl	is_null_line = 0;
81157114Sscottl	pendc = '\0';
82157114Sscottl	lmargin = 0;
83157114Sscottl	if (status_col)
84157114Sscottl		lmargin += 1;
85171980Sscottl	if (linenums == OPT_ONPLUS)
86233711Sambrisko		lmargin += LINENUM_WIDTH+1;
87233711Sambrisko}
88233711Sambrisko
89247369Ssmh/*
90233711Sambrisko * Insert the line number (of the given position) into the line buffer.
91233711Sambrisko */
92233711Sambrisko	public void
93233711Sambriskoplinenum(pos)
94233711Sambrisko	POSITION pos;
95171980Sscottl{
96171980Sscottl	register int lno;
97171980Sscottl	register int i;
98171980Sscottl
99171980Sscottl	if (linenums == OPT_ONPLUS)
100171980Sscottl	{
101233711Sambrisko		/*
102171980Sscottl		 * Get the line number and put it in the current line.
103171980Sscottl		 * {{ Note: since find_linenum calls forw_raw_line,
104171980Sscottl		 *    it may seek in the input file, requiring the caller
105233711Sambrisko		 *    of plinenum to re-seek if necessary. }}
106233711Sambrisko		 * {{ Since forw_raw_line modifies linebuf, we must
107233711Sambrisko		 *    do this first, before storing anything in linebuf. }}
108233711Sambrisko		 */
109233711Sambrisko		lno = find_linenum(pos);
110184897Sambrisko	}
111184897Sambrisko
112184897Sambrisko	/*
113184897Sambrisko	 * Display a status column if the -J option is set.
114184897Sambrisko	 */
115184897Sambrisko	if (status_col)
116233711Sambrisko	{
117233711Sambrisko		linebuf[curr] = ' ';
118233711Sambrisko		if (start_attnpos != NULL_POSITION &&
119233711Sambrisko		    pos >= start_attnpos && pos < end_attnpos)
120233711Sambrisko			attr[curr] = AT_STANDOUT;
121233711Sambrisko		else
122233711Sambrisko			attr[curr] = 0;
123233711Sambrisko		curr++;
124157114Sscottl		column++;
125157114Sscottl	}
126157114Sscottl	/*
127233711Sambrisko	 * Display the line number at the start of each line
128233711Sambrisko	 * if the -N option is set.
129233711Sambrisko	 */
130233711Sambrisko	if (linenums == OPT_ONPLUS)
131157114Sscottl	{
132157114Sscottl		sprintf(&linebuf[curr], "%*d", LINENUM_WIDTH, lno);
133157114Sscottl		column += LINENUM_WIDTH;
134157114Sscottl		for (i = 0;  i < LINENUM_WIDTH;  i++)
135157114Sscottl			attr[curr++] = 0;
136157114Sscottl	}
137157114Sscottl	/*
138157114Sscottl	 * Append enough spaces to bring us to the lmargin.
139157114Sscottl	 */
140157114Sscottl	while (column < lmargin)
141224041Sjhb	{
142157114Sscottl		linebuf[curr] = ' ';
143157114Sscottl		attr[curr++] = AT_NORMAL;
144157114Sscottl		column++;
145157114Sscottl	}
146157114Sscottl}
147157114Sscottl
148233711Sambrisko/*
149233711Sambrisko *
150233711Sambrisko */
151157114Sscottl	static int
152233711Sambriskoutf_len(char *s, int len)
153233711Sambrisko{
154233711Sambrisko	int ulen = 0;
155233711Sambrisko
156233711Sambrisko	while (*s != '\0' && len > 0)
157233711Sambrisko	{
158233711Sambrisko		if (!IS_CONT(*s))
159157114Sscottl			len--;
160157114Sscottl		s++;
161157114Sscottl		ulen++;
162157114Sscottl	}
163157114Sscottl	while (IS_CONT(*s))
164157114Sscottl	{
165157114Sscottl		s++;
166157114Sscottl		ulen++;
167224041Sjhb	}
168157114Sscottl	return (ulen);
169233711Sambrisko}
170233711Sambrisko
171233711Sambrisko/*
172233711Sambrisko * Shift the input line left.
173233711Sambrisko * This means discarding N printable chars at the start of the buffer.
174233711Sambrisko */
175157114Sscottl	static void
176157114Sscottlpshift(shift)
177157114Sscottl	int shift;
178157114Sscottl{
179157114Sscottl	int i;
180157114Sscottl	int real_shift;
181157114Sscottl
182157114Sscottl	if (shift > column - lmargin)
183157114Sscottl		shift = column - lmargin;
184157114Sscottl	if (shift > curr - lmargin)
185157114Sscottl		shift = curr - lmargin;
186157114Sscottl
187157114Sscottl	if (!utf_mode)
188157114Sscottl		real_shift = shift;
189157114Sscottl	else
190157114Sscottl	{
191233711Sambrisko		real_shift = utf_len(linebuf + lmargin, shift);
192163398Sscottl		if (real_shift > curr)
193163398Sscottl			real_shift = curr;
194157114Sscottl	}
195255806Ssbruno	for (i = 0;  i < curr - real_shift;  i++)
196255806Ssbruno	{
197157114Sscottl		linebuf[lmargin + i] = linebuf[lmargin + i + real_shift];
198157114Sscottl		attr[lmargin + i] = attr[lmargin + i + real_shift];
199157114Sscottl	}
200157114Sscottl	column -= shift;
201196200Sscottl	curr -= real_shift;
202196200Sscottl	cshift += shift;
203196200Sscottl}
204196200Sscottl
205196200Sscottl/*
206196200Sscottl * Return the printing width of the start (enter) sequence
207196200Sscottl * for a given character attribute.
208196200Sscottl */
209196200Sscottl	static int
210196200Sscottlattr_swidth(a)
211196200Sscottl	int a;
212233711Sambrisko{
213196200Sscottl	switch (a)
214196200Sscottl	{
215196200Sscottl	case AT_BOLD:		return (bo_s_width);
216196200Sscottl	case AT_UNDERLINE:	return (ul_s_width);
217196200Sscottl	case AT_BLINK:		return (bl_s_width);
218196200Sscottl	case AT_STANDOUT:	return (so_s_width);
219196200Sscottl	}
220196200Sscottl	return (0);
221196200Sscottl}
222233711Sambrisko
223233711Sambrisko/*
224159811Sps * Return the printing width of the end (exit) sequence
225159811Sps * for a given character attribute.
226157114Sscottl */
227159811Sps	static int
228196200Sscottlattr_ewidth(a)
229171821Sjhb	int a;
230163398Sscottl{
231163398Sscottl	switch (a)
232163398Sscottl	{
233196200Sscottl	case AT_BOLD:		return (bo_e_width);
234233711Sambrisko	case AT_UNDERLINE:	return (ul_e_width);
235251516Ssbruno	case AT_BLINK:		return (bl_e_width);
236251516Ssbruno	case AT_STANDOUT:	return (so_e_width);
237251516Ssbruno	}
238184897Sambrisko	return (0);
239251516Ssbruno}
240196200Sscottl
241196200Sscottl/*
242196200Sscottl * Return the printing width of a given character and attribute,
243249257Smarkj * if the character were added to the current position in the line buffer.
244249257Smarkj * Adding a character with a given attribute may cause an enter or exit
245249257Smarkj * attribute sequence to be inserted, so this must be taken into account.
246157114Sscottl */
247157114Sscottl	static int
248157114Sscottlpwidth(c, a)
249157114Sscottl	int c;
250157114Sscottl	int a;
251157114Sscottl{
252157114Sscottl	register int w;
253157114Sscottl
254157114Sscottl	if (utf_mode && IS_CONT(c))
255157114Sscottl		return (0);
256157114Sscottl
257157114Sscottl	if (c == '\b')
258157114Sscottl		/*
259158737Sambrisko		 * Backspace moves backwards one position.
260157114Sscottl		 */
261157114Sscottl		return (-1);
262157114Sscottl
263157114Sscottl	if (control_char(c))
264157114Sscottl		/*
265157114Sscottl		 * Control characters do unpredicatable things,
266157114Sscottl		 * so we don't even try to guess; say it doesn't move.
267157114Sscottl		 * This can only happen if the -r flag is in effect.
268157114Sscottl		 */
269157114Sscottl		return (0);
270157114Sscottl
271233711Sambrisko	/*
272247369Ssmh	 * Other characters take one space,
273247369Ssmh	 * plus the width of any attribute enter/exit sequence.
274247369Ssmh	 */
275247369Ssmh	w = 1;
276247369Ssmh	if (curr > 0 && attr[curr-1] != a)
277247369Ssmh		w += attr_ewidth(attr[curr-1]);
278247369Ssmh	if (a && (curr == 0 || attr[curr-1] != a))
279157114Sscottl		w += attr_swidth(a);
280233711Sambrisko	return (w);
281233711Sambrisko}
282233711Sambrisko
283233711Sambrisko/*
284233711Sambrisko * Delete the previous character in the line buffer.
285233711Sambrisko */
286233711Sambrisko	static void
287233711Sambriskobackc()
288233711Sambrisko{
289233711Sambrisko	curr--;
290233711Sambrisko	column -= pwidth(linebuf[curr], attr[curr]);
291233711Sambrisko}
292233711Sambrisko
293233711Sambrisko/*
294233711Sambrisko * Are we currently within a recognized ANSI escape sequence?
295233711Sambrisko */
296233711Sambrisko	static int
297233711Sambriskoin_ansi_esc_seq()
298233711Sambrisko{
299233711Sambrisko	int i;
300233711Sambrisko
301233711Sambrisko	/*
302233711Sambrisko	 * Search backwards for either an ESC (which means we ARE in a seq);
303233711Sambrisko	 * or an end char (which means we're NOT in a seq).
304233711Sambrisko	 */
305233711Sambrisko	for (i = curr-1;  i >= 0;  i--)
306233711Sambrisko	{
307233711Sambrisko		if (linebuf[i] == ESC)
308233711Sambrisko			return (1);
309157114Sscottl		if (strchr(end_ansi_chars, linebuf[i]) != NULL)
310157114Sscottl			return (0);
311157114Sscottl	}
312157114Sscottl	return (0);
313157114Sscottl}
314157114Sscottl
315157114Sscottl/*
316157114Sscottl * Append a character and attribute to the line buffer.
317157114Sscottl */
318157114Sscottl	static int
319157114Sscottlstorec(c, a, pos)
320157114Sscottl	int c;
321157114Sscottl	int a;
322157114Sscottl	POSITION pos;
323157114Sscottl{
324157114Sscottl	register int w;
325157114Sscottl
326157114Sscottl#if HILITE_SEARCH
327157114Sscottl	if (is_hilited(pos, pos+1, 0))
328157114Sscottl		/*
329157114Sscottl		 * This character should be highlighted.
330157114Sscottl		 * Override the attribute passed in.
331157114Sscottl		 */
332157114Sscottl		a = AT_STANDOUT;
333157114Sscottl#endif
334157114Sscottl	if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
335157114Sscottl		w = 0;
336157114Sscottl	else
337157114Sscottl		w = pwidth(c, a);
338157114Sscottl	if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
339157114Sscottl		/*
340157114Sscottl		 * Won't fit on screen.
341157114Sscottl		 */
342157114Sscottl		return (1);
343157114Sscottl
344157114Sscottl	if (curr >= sizeof(linebuf)-2)
345157114Sscottl		/*
346157114Sscottl		 * Won't fit in line buffer.
347157114Sscottl		 */
348157114Sscottl		return (1);
349157114Sscottl
350157114Sscottl	/*
351157114Sscottl	 * Special handling for "magic cookie" terminals.
352157114Sscottl	 * If an attribute enter/exit sequence has a printing width > 0,
353157114Sscottl	 * and the sequence is adjacent to a space, delete the space.
354157114Sscottl	 * We just mark the space as invisible, to avoid having too
355157114Sscottl	 * many spaces deleted.
356157114Sscottl	 * {{ Note that even if the attribute width is > 1, we
357157114Sscottl	 *    delete only one space.  It's not worth trying to do more.
358157114Sscottl	 *    It's hardly worth doing this much. }}
359157114Sscottl	 */
360157114Sscottl	if (curr > 0 && a != AT_NORMAL &&
361157114Sscottl		linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL &&
362157114Sscottl		attr_swidth(a) > 0)
363157114Sscottl	{
364157114Sscottl		/*
365157114Sscottl		 * We are about to append an enter-attribute sequence
366157114Sscottl		 * just after a space.  Delete the space.
367157114Sscottl		 */
368196200Sscottl		attr[curr-1] = AT_INVIS;
369196200Sscottl		column--;
370196200Sscottl	} else if (curr > 0 && attr[curr-1] != AT_NORMAL &&
371157114Sscottl		attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL &&
372157114Sscottl		attr_ewidth(attr[curr-1]) > 0)
373157114Sscottl	{
374157114Sscottl		/*
375157114Sscottl		 * We are about to append a space just after an
376157114Sscottl		 * exit-attribute sequence.  Delete the space.
377157114Sscottl		 */
378157114Sscottl		a = AT_INVIS;
379157114Sscottl		column--;
380157114Sscottl	}
381157114Sscottl	/* End of magic cookie handling. */
382157114Sscottl
383157114Sscottl	linebuf[curr] = c;
384157114Sscottl	attr[curr] = a;
385157114Sscottl	column += w;
386157114Sscottl	return (0);
387157114Sscottl}
388157114Sscottl
389157114Sscottl/*
390157114Sscottl * Append a character to the line buffer.
391157114Sscottl * Expand tabs into spaces, handle underlining, boldfacing, etc.
392157114Sscottl * Returns 0 if ok, 1 if couldn't fit in buffer.
393157114Sscottl */
394157114Sscottl	public int
395157114Sscottlpappend(c, pos)
396157114Sscottl	register int c;
397158737Sambrisko	POSITION pos;
398158737Sambrisko{
399158737Sambrisko	int r;
400158737Sambrisko
401158737Sambrisko	if (pendc)
402158737Sambrisko	{
403158737Sambrisko		if (do_append(pendc, pendpos))
404158737Sambrisko			/*
405158737Sambrisko			 * Oops.  We've probably lost the char which
406158737Sambrisko			 * was in pendc, since caller won't back up.
407158737Sambrisko			 */
408158737Sambrisko			return (1);
409158737Sambrisko		pendc = '\0';
410158737Sambrisko	}
411158737Sambrisko
412158737Sambrisko	if (c == '\r' && bs_mode == BS_SPECIAL)
413158737Sambrisko	{
414158737Sambrisko		/*
415158737Sambrisko		 * Don't put the CR into the buffer until we see
416158737Sambrisko		 * the next char.  If the next char is a newline,
417158737Sambrisko		 * discard the CR.
418157114Sscottl		 */
419157114Sscottl		pendc = c;
420233711Sambrisko		pendpos = pos;
421233711Sambrisko		return (0);
422233711Sambrisko	}
423235014Sambrisko
424233711Sambrisko	r = do_append(c, pos);
425163398Sscottl	/*
426163398Sscottl	 * If we need to shift the line, do it.
427163398Sscottl	 * But wait until we get to at least the middle of the screen,
428163398Sscottl	 * so shifting it doesn't affect the chars we're currently
429163398Sscottl	 * pappending.  (Bold & underline can get messed up otherwise.)
430163398Sscottl	 */
431163398Sscottl	if (cshift < hshift && column > sc_width / 2)
432163398Sscottl		pshift(hshift - cshift);
433163398Sscottl	return (r);
434196200Sscottl}
435163398Sscottl
436196200Sscottl	static int
437196200Sscottldo_append(c, pos)
438196200Sscottl	int c;
439196200Sscottl	POSITION pos;
440196200Sscottl{
441196200Sscottl	register char *s;
442196200Sscottl	register int a;
443196200Sscottl
444196200Sscottl#define	STOREC(c,a) \
445196200Sscottl	if (storec((c),(a),pos)) return (1); else curr++
446163398Sscottl
447163398Sscottl	if (c == '\b')
448163398Sscottl	{
449163398Sscottl		switch (bs_mode)
450163398Sscottl		{
451163398Sscottl		case BS_NORMAL:
452233711Sambrisko			STOREC(c, AT_NORMAL);
453233711Sambrisko			break;
454233711Sambrisko		case BS_CONTROL:
455233711Sambrisko			goto do_control_char;
456233711Sambrisko		case BS_SPECIAL:
457233711Sambrisko			if (curr == 0)
458233711Sambrisko				break;
459233711Sambrisko			backc();
460233711Sambrisko			overstrike = 1;
461157114Sscottl			break;
462157114Sscottl		}
463157114Sscottl	} else if (overstrike)
464157114Sscottl	{
465157114Sscottl		/*
466157114Sscottl		 * Overstrike the character at the current position
467157114Sscottl		 * in the line buffer.  This will cause either
468157114Sscottl		 * underline (if a "_" is overstruck),
469157114Sscottl		 * bold (if an identical character is overstruck),
470157114Sscottl		 * or just deletion of the character in the buffer.
471196200Sscottl		 */
472157114Sscottl		overstrike = 0;
473157114Sscottl		if ((char)c == linebuf[curr])
474157114Sscottl			STOREC(linebuf[curr], AT_BOLD);
475157114Sscottl		else if (c == '_')
476247369Ssmh			STOREC(linebuf[curr], AT_UNDERLINE);
477247369Ssmh		else if (linebuf[curr] == '_')
478157114Sscottl			STOREC(c, AT_UNDERLINE);
479157114Sscottl		else if (control_char(c))
480157114Sscottl			goto do_control_char;
481157114Sscottl		else
482157114Sscottl			STOREC(c, AT_NORMAL);
483157114Sscottl	} else if (c == '\t')
484157114Sscottl	{
485157114Sscottl		/*
486157114Sscottl		 * Expand a tab into spaces.
487157114Sscottl		 */
488157114Sscottl		if (tabstop == 0)
489157114Sscottl			tabstop = 1;
490157114Sscottl		switch (bs_mode)
491157114Sscottl		{
492157114Sscottl		case BS_CONTROL:
493233711Sambrisko			goto do_control_char;
494233711Sambrisko		case BS_NORMAL:
495233711Sambrisko		case BS_SPECIAL:
496233711Sambrisko			do
497233711Sambrisko			{
498233711Sambrisko				STOREC(' ', AT_NORMAL);
499157114Sscottl			} while (((column + cshift - lmargin) % tabstop) != 0);
500233711Sambrisko			break;
501233711Sambrisko		}
502233711Sambrisko	} else if (control_char(c))
503157114Sscottl	{
504157114Sscottl	do_control_char:
505157114Sscottl		if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && c == ESC))
506157114Sscottl		{
507157114Sscottl			/*
508157114Sscottl			 * Output as a normal character.
509157114Sscottl			 */
510157114Sscottl			STOREC(c, AT_NORMAL);
511157114Sscottl		} else
512157114Sscottl		{
513157114Sscottl			/*
514157114Sscottl			 * Convert to printable representation.
515157114Sscottl			 */
516233711Sambrisko			s = prchar(c);
517233711Sambrisko			a = binattr;
518233711Sambrisko
519233711Sambrisko			/*
520157114Sscottl			 * Make sure we can get the entire representation
521157114Sscottl			 * of the character on this line.
522175897Sambrisko			 */
523175897Sambrisko			if (column + (int) strlen(s) +
524157114Sscottl			    attr_swidth(a) + attr_ewidth(a) > sc_width)
525157114Sscottl				return (1);
526157114Sscottl
527157114Sscottl			for ( ;  *s != 0;  s++)
528157114Sscottl				STOREC(*s, a);
529157114Sscottl		}
530157114Sscottl	} else
531157114Sscottl	{
532157114Sscottl		STOREC(c, AT_NORMAL);
533157114Sscottl	}
534233711Sambrisko
535233711Sambrisko	return (0);
536233711Sambrisko}
537233711Sambrisko
538233711Sambrisko/*
539233711Sambrisko * Terminate the line in the line buffer.
540157114Sscottl */
541157114Sscottl	public void
542233711Sambriskopdone(endline)
543233711Sambrisko	int endline;
544233711Sambrisko{
545233711Sambrisko	if (pendc && (pendc != '\r' || !endline))
546233711Sambrisko		/*
547233711Sambrisko		 * If we had a pending character, put it in the buffer.
548233711Sambrisko		 * But discard a pending CR if we are at end of line
549233711Sambrisko		 * (that is, discard the CR in a CR/LF sequence).
550233711Sambrisko		 */
551233711Sambrisko		(void) do_append(pendc, pendpos);
552233711Sambrisko
553233711Sambrisko	/*
554233711Sambrisko	 * Make sure we've shifted the line, if we need to.
555233711Sambrisko	 */
556233711Sambrisko	if (cshift < hshift)
557157114Sscottl		pshift(hshift - cshift);
558157114Sscottl
559157114Sscottl	/*
560157114Sscottl	 * Add a newline if necessary,
561157114Sscottl	 * and append a '\0' to the end of the line.
562157114Sscottl	 */
563157114Sscottl	if (column < sc_width || !auto_wrap || ignaw || ctldisp == OPT_ON)
564157114Sscottl	{
565157114Sscottl		linebuf[curr] = '\n';
566157114Sscottl		attr[curr] = AT_NORMAL;
567157114Sscottl		curr++;
568157114Sscottl	}
569157114Sscottl	linebuf[curr] = '\0';
570157114Sscottl	attr[curr] = AT_NORMAL;
571157114Sscottl	/*
572157114Sscottl	 * If we are done with this line, reset the current shift.
573157114Sscottl	 */
574157114Sscottl	if (endline)
575157114Sscottl		cshift = 0;
576157114Sscottl}
577157114Sscottl
578157114Sscottl/*
579157114Sscottl * Get a character from the current line.
580157114Sscottl * Return the character as the function return value,
581157114Sscottl * and the character attribute in *ap.
582157114Sscottl */
583157114Sscottl	public int
584157114Sscottlgline(i, ap)
585157114Sscottl	register int i;
586157114Sscottl	register int *ap;
587233711Sambrisko{
588233711Sambrisko	char *s;
589157114Sscottl
590157114Sscottl	if (is_null_line)
591233711Sambrisko	{
592157114Sscottl		/*
593157114Sscottl		 * If there is no current line, we pretend the line is
594157114Sscottl		 * either "~" or "", depending on the "twiddle" flag.
595157114Sscottl		 */
596157114Sscottl		*ap = AT_BOLD;
597157114Sscottl		s = (twiddle) ? "~\n" : "\n";
598157114Sscottl		return (s[i]);
599157114Sscottl	}
600157114Sscottl
601157114Sscottl	*ap = attr[i];
602157114Sscottl	return (linebuf[i] & 0377);
603157114Sscottl}
604157114Sscottl
605157114Sscottl/*
606157114Sscottl * Indicate that there is no current line.
607157114Sscottl */
608157114Sscottl	public void
609157114Sscottlnull_line()
610157114Sscottl{
611157114Sscottl	is_null_line = 1;
612157114Sscottl	cshift = 0;
613157114Sscottl}
614157114Sscottl
615157114Sscottl/*
616233711Sambrisko * Analogous to forw_line(), but deals with "raw lines":
617157114Sscottl * lines which are not split for screen width.
618157114Sscottl * {{ This is supposed to be more efficient than forw_line(). }}
619157114Sscottl */
620157114Sscottl	public POSITION
621157114Sscottlforw_raw_line(curr_pos, linep)
622157114Sscottl	POSITION curr_pos;
623157114Sscottl	char **linep;
624157114Sscottl{
625157114Sscottl	register char *p;
626157114Sscottl	register int c;
627157114Sscottl	POSITION new_pos;
628157114Sscottl
629157114Sscottl	if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
630157114Sscottl		(c = ch_forw_get()) == EOI)
631157114Sscottl		return (NULL_POSITION);
632157114Sscottl
633157114Sscottl	p = linebuf;
634157114Sscottl
635157114Sscottl	for (;;)
636157114Sscottl	{
637157114Sscottl		if (c == '\n' || c == EOI)
638157114Sscottl		{
639157114Sscottl			new_pos = ch_tell();
640157114Sscottl			break;
641157114Sscottl		}
642157114Sscottl		if (p >= &linebuf[sizeof(linebuf)-1])
643157114Sscottl		{
644157114Sscottl			/*
645157114Sscottl			 * Overflowed the input buffer.
646157114Sscottl			 * Pretend the line ended here.
647157114Sscottl			 * {{ The line buffer is supposed to be big
648157114Sscottl			 *    enough that this never happens. }}
649157114Sscottl			 */
650157114Sscottl			new_pos = ch_tell() - 1;
651157114Sscottl			break;
652157114Sscottl		}
653157114Sscottl		*p++ = c;
654157114Sscottl		c = ch_forw_get();
655157114Sscottl	}
656157114Sscottl	*p = '\0';
657157114Sscottl	if (linep != NULL)
658157114Sscottl		*linep = linebuf;
659157114Sscottl	return (new_pos);
660157114Sscottl}
661157114Sscottl
662157114Sscottl/*
663157114Sscottl * Analogous to back_line(), but deals with "raw lines".
664157114Sscottl * {{ This is supposed to be more efficient than back_line(). }}
665157114Sscottl */
666233711Sambrisko	public POSITION
667233711Sambriskoback_raw_line(curr_pos, linep)
668233711Sambrisko	POSITION curr_pos;
669233711Sambrisko	char **linep;
670233711Sambrisko{
671233711Sambrisko	register char *p;
672233711Sambrisko	register int c;
673233711Sambrisko	POSITION new_pos;
674233711Sambrisko
675233711Sambrisko	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
676233711Sambrisko		ch_seek(curr_pos-1))
677233711Sambrisko		return (NULL_POSITION);
678233711Sambrisko
679233711Sambrisko	p = &linebuf[sizeof(linebuf)];
680233711Sambrisko	*--p = '\0';
681233711Sambrisko
682233711Sambrisko	for (;;)
683233711Sambrisko	{
684233711Sambrisko		c = ch_back_get();
685233711Sambrisko		if (c == '\n')
686233711Sambrisko		{
687233711Sambrisko			/*
688233711Sambrisko			 * This is the newline ending the previous line.
689233711Sambrisko			 * We have hit the beginning of the line.
690233711Sambrisko			 */
691233711Sambrisko			new_pos = ch_tell() + 1;
692233711Sambrisko			break;
693233711Sambrisko		}
694233711Sambrisko		if (c == EOI)
695233711Sambrisko		{
696233711Sambrisko			/*
697233711Sambrisko			 * We have hit the beginning of the file.
698233711Sambrisko			 * This must be the first line in the file.
699233711Sambrisko			 * This must, of course, be the beginning of the line.
700233711Sambrisko			 */
701233711Sambrisko			new_pos = ch_zero();
702233711Sambrisko			break;
703233711Sambrisko		}
704233711Sambrisko		if (p <= linebuf)
705233711Sambrisko		{
706233711Sambrisko			/*
707233711Sambrisko			 * Overflowed the input buffer.
708233711Sambrisko			 * Pretend the line ended here.
709233711Sambrisko			 */
710233711Sambrisko			new_pos = ch_tell() + 1;
711233711Sambrisko			break;
712233711Sambrisko		}
713233711Sambrisko		*--p = c;
714233711Sambrisko	}
715233711Sambrisko	if (linep != NULL)
716233711Sambrisko		*linep = p;
717233711Sambrisko	return (new_pos);
718233711Sambrisko}
719233711Sambrisko