line.c revision 60786
160786Sps/*
260786Sps * Copyright (C) 1984-2000  Mark Nudelman
360786Sps *
460786Sps * You may distribute under the terms of either the GNU General Public
560786Sps * License or the Less License, as specified in the README file.
660786Sps *
760786Sps * For more information about less, or for information on how to
860786Sps * contact the author, 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"
1960786Sps
2060786Sps#define IS_CONT(c)  (((c) & 0xC0) == 0x80)
2160786Sps
2260786Sps/* Buffer which holds the current output line */
2360786Spspublic char linebuf[LINEBUF_SIZE];
2460786Spspublic int size_linebuf = sizeof(linebuf);
2560786Sps
2660786Spspublic int cshift;		/* Current left-shift of output line buffer */
2760786Spspublic int hshift;		/* Desired left-shift of output line buffer */
2860786Sps
2960786Spsstatic char attr[LINEBUF_SIZE];	/* Extension of linebuf to hold attributes */
3060786Spsstatic int curr;		/* Index into linebuf */
3160786Spsstatic int column;		/* Printable length, accounting for
3260786Sps				   backspaces, etc. */
3360786Spsstatic int overstrike;		/* Next char should overstrike previous char */
3460786Spsstatic int is_null_line;	/* There is no current line */
3560786Spsstatic char pendc;
3660786Spsstatic POSITION pendpos;
3760786Spsstatic char *end_ansi_chars;
3860786Sps
3960786Spsstatic int do_append();
4060786Sps
4160786Spsextern int bs_mode;
4260786Spsextern int tabstop;
4360786Spsextern int linenums;
4460786Spsextern int ctldisp;
4560786Spsextern int twiddle;
4660786Spsextern int binattr;
4760786Spsextern int auto_wrap, ignaw;
4860786Spsextern int bo_s_width, bo_e_width;
4960786Spsextern int ul_s_width, ul_e_width;
5060786Spsextern int bl_s_width, bl_e_width;
5160786Spsextern int so_s_width, so_e_width;
5260786Spsextern int sc_width, sc_height;
5360786Spsextern int utf_mode;
5460786Sps
5560786Sps/*
5660786Sps * Initialize from environment variables.
5760786Sps */
5860786Sps	public void
5960786Spsinit_line()
6060786Sps{
6160786Sps	end_ansi_chars = lgetenv("LESSANSIENDCHARS");
6260786Sps	if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
6360786Sps		end_ansi_chars = "m";
6460786Sps}
6560786Sps
6660786Sps/*
6760786Sps * Rewind the line buffer.
6860786Sps */
6960786Sps	public void
7060786Spsprewind()
7160786Sps{
7260786Sps	curr = 0;
7360786Sps	column = 0;
7460786Sps	overstrike = 0;
7560786Sps	is_null_line = 0;
7660786Sps	pendc = '\0';
7760786Sps}
7860786Sps
7960786Sps/*
8060786Sps * Insert the line number (of the given position) into the line buffer.
8160786Sps */
8260786Sps	public void
8360786Spsplinenum(pos)
8460786Sps	POSITION pos;
8560786Sps{
8660786Sps	register int lno;
8760786Sps	register int i;
8860786Sps	register int n;
8960786Sps
9060786Sps	/*
9160786Sps	 * We display the line number at the start of each line
9260786Sps	 * only if the -N option is set.
9360786Sps	 */
9460786Sps	if (linenums != OPT_ONPLUS)
9560786Sps		return;
9660786Sps
9760786Sps	/*
9860786Sps	 * Get the line number and put it in the current line.
9960786Sps	 * {{ Note: since find_linenum calls forw_raw_line,
10060786Sps	 *    it may seek in the input file, requiring the caller
10160786Sps	 *    of plinenum to re-seek if necessary. }}
10260786Sps	 */
10360786Sps	lno = find_linenum(pos);
10460786Sps
10560786Sps	sprintf(&linebuf[curr], "%6d", lno);
10660786Sps	n = strlen(&linebuf[curr]);
10760786Sps	column += n;
10860786Sps	for (i = 0;  i < n;  i++)
10960786Sps		attr[curr++] = 0;
11060786Sps
11160786Sps	/*
11260786Sps	 * Append enough spaces to bring us to the next tab stop.
11360786Sps	 * {{ We could avoid this at the cost of adding some
11460786Sps	 *    complication to the tab stop logic in pappend(). }}
11560786Sps	 */
11660786Sps	if (tabstop == 0)
11760786Sps		tabstop = 1;
11860786Sps	do
11960786Sps	{
12060786Sps		linebuf[curr] = ' ';
12160786Sps		attr[curr++] = AT_NORMAL;
12260786Sps		column++;
12360786Sps	} while (((column + cshift) % tabstop) != 0);
12460786Sps}
12560786Sps
12660786Sps/*
12760786Sps *
12860786Sps */
12960786Sps	static int
13060786Spsutf_len(char *s, int len)
13160786Sps{
13260786Sps	int ulen = 0;
13360786Sps
13460786Sps	while (*s != '\0' && len > 0)
13560786Sps	{
13660786Sps		if (!IS_CONT(*s))
13760786Sps			len--;
13860786Sps		s++;
13960786Sps		ulen++;
14060786Sps	}
14160786Sps	while (IS_CONT(*s))
14260786Sps	{
14360786Sps		s++;
14460786Sps		ulen++;
14560786Sps	}
14660786Sps	return (ulen);
14760786Sps}
14860786Sps
14960786Sps/*
15060786Sps * Shift the input line left.
15160786Sps * This means discarding N printable chars at the start of the buffer.
15260786Sps */
15360786Sps	static void
15460786Spspshift(shift)
15560786Sps	int shift;
15660786Sps{
15760786Sps	int i;
15860786Sps	int real_shift;
15960786Sps
16060786Sps	if (shift > column)
16160786Sps		shift = column;
16260786Sps	if (shift > curr)
16360786Sps		shift = curr;
16460786Sps
16560786Sps	if (!utf_mode)
16660786Sps		real_shift = shift;
16760786Sps	else
16860786Sps	{
16960786Sps		real_shift = utf_len(linebuf, shift);
17060786Sps		if (real_shift > curr)
17160786Sps			real_shift = curr;
17260786Sps	}
17360786Sps	for (i = 0;  i < curr - real_shift;  i++)
17460786Sps	{
17560786Sps		linebuf[i] = linebuf[i + real_shift];
17660786Sps		attr[i] = attr[i + real_shift];
17760786Sps	}
17860786Sps	column -= shift;
17960786Sps	curr -= real_shift;
18060786Sps	cshift += shift;
18160786Sps}
18260786Sps
18360786Sps/*
18460786Sps * Return the printing width of the start (enter) sequence
18560786Sps * for a given character attribute.
18660786Sps */
18760786Sps	static int
18860786Spsattr_swidth(a)
18960786Sps	int a;
19060786Sps{
19160786Sps	switch (a)
19260786Sps	{
19360786Sps	case AT_BOLD:		return (bo_s_width);
19460786Sps	case AT_UNDERLINE:	return (ul_s_width);
19560786Sps	case AT_BLINK:		return (bl_s_width);
19660786Sps	case AT_STANDOUT:	return (so_s_width);
19760786Sps	}
19860786Sps	return (0);
19960786Sps}
20060786Sps
20160786Sps/*
20260786Sps * Return the printing width of the end (exit) sequence
20360786Sps * for a given character attribute.
20460786Sps */
20560786Sps	static int
20660786Spsattr_ewidth(a)
20760786Sps	int a;
20860786Sps{
20960786Sps	switch (a)
21060786Sps	{
21160786Sps	case AT_BOLD:		return (bo_e_width);
21260786Sps	case AT_UNDERLINE:	return (ul_e_width);
21360786Sps	case AT_BLINK:		return (bl_e_width);
21460786Sps	case AT_STANDOUT:	return (so_e_width);
21560786Sps	}
21660786Sps	return (0);
21760786Sps}
21860786Sps
21960786Sps/*
22060786Sps * Return the printing width of a given character and attribute,
22160786Sps * if the character were added to the current position in the line buffer.
22260786Sps * Adding a character with a given attribute may cause an enter or exit
22360786Sps * attribute sequence to be inserted, so this must be taken into account.
22460786Sps */
22560786Sps	static int
22660786Spspwidth(c, a)
22760786Sps	int c;
22860786Sps	int a;
22960786Sps{
23060786Sps	register int w;
23160786Sps
23260786Sps	if (utf_mode && IS_CONT(c))
23360786Sps		return (0);
23460786Sps
23560786Sps	if (c == '\b')
23660786Sps		/*
23760786Sps		 * Backspace moves backwards one position.
23860786Sps		 */
23960786Sps		return (-1);
24060786Sps
24160786Sps	if (control_char(c))
24260786Sps		/*
24360786Sps		 * Control characters do unpredicatable things,
24460786Sps		 * so we don't even try to guess; say it doesn't move.
24560786Sps		 * This can only happen if the -r flag is in effect.
24660786Sps		 */
24760786Sps		return (0);
24860786Sps
24960786Sps	/*
25060786Sps	 * Other characters take one space,
25160786Sps	 * plus the width of any attribute enter/exit sequence.
25260786Sps	 */
25360786Sps	w = 1;
25460786Sps	if (curr > 0 && attr[curr-1] != a)
25560786Sps		w += attr_ewidth(attr[curr-1]);
25660786Sps	if (a && (curr == 0 || attr[curr-1] != a))
25760786Sps		w += attr_swidth(a);
25860786Sps	return (w);
25960786Sps}
26060786Sps
26160786Sps/*
26260786Sps * Delete the previous character in the line buffer.
26360786Sps */
26460786Sps	static void
26560786Spsbackc()
26660786Sps{
26760786Sps	curr--;
26860786Sps	column -= pwidth(linebuf[curr], attr[curr]);
26960786Sps}
27060786Sps
27160786Sps/*
27260786Sps * Are we currently within a recognized ANSI escape sequence?
27360786Sps */
27460786Sps	static int
27560786Spsin_ansi_esc_seq()
27660786Sps{
27760786Sps	int i;
27860786Sps
27960786Sps	/*
28060786Sps	 * Search backwards for either an ESC (which means we ARE in a seq);
28160786Sps	 * or an end char (which means we're NOT in a seq).
28260786Sps	 */
28360786Sps	for (i = curr-1;  i >= 0;  i--)
28460786Sps	{
28560786Sps		if (linebuf[i] == ESC)
28660786Sps			return (1);
28760786Sps		if (strchr(end_ansi_chars, linebuf[i]) != NULL)
28860786Sps			return (0);
28960786Sps	}
29060786Sps	return (0);
29160786Sps}
29260786Sps
29360786Sps/*
29460786Sps * Append a character and attribute to the line buffer.
29560786Sps */
29660786Sps	static int
29760786Spsstorec(c, a, pos)
29860786Sps	int c;
29960786Sps	int a;
30060786Sps	POSITION pos;
30160786Sps{
30260786Sps	register int w;
30360786Sps
30460786Sps#if HILITE_SEARCH
30560786Sps	if (is_hilited(pos, pos+1, 0))
30660786Sps		/*
30760786Sps		 * This character should be highlighted.
30860786Sps		 * Override the attribute passed in.
30960786Sps		 */
31060786Sps		a = AT_STANDOUT;
31160786Sps#endif
31260786Sps	if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
31360786Sps		w = 0;
31460786Sps	else
31560786Sps		w = pwidth(c, a);
31660786Sps	if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
31760786Sps		/*
31860786Sps		 * Won't fit on screen.
31960786Sps		 */
32060786Sps		return (1);
32160786Sps
32260786Sps	if (curr >= sizeof(linebuf)-2)
32360786Sps		/*
32460786Sps		 * Won't fit in line buffer.
32560786Sps		 */
32660786Sps		return (1);
32760786Sps
32860786Sps	/*
32960786Sps	 * Special handling for "magic cookie" terminals.
33060786Sps	 * If an attribute enter/exit sequence has a printing width > 0,
33160786Sps	 * and the sequence is adjacent to a space, delete the space.
33260786Sps	 * We just mark the space as invisible, to avoid having too
33360786Sps	 * many spaces deleted.
33460786Sps	 * {{ Note that even if the attribute width is > 1, we
33560786Sps	 *    delete only one space.  It's not worth trying to do more.
33660786Sps	 *    It's hardly worth doing this much. }}
33760786Sps	 */
33860786Sps	if (curr > 0 && a != AT_NORMAL &&
33960786Sps		linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL &&
34060786Sps		attr_swidth(a) > 0)
34160786Sps	{
34260786Sps		/*
34360786Sps		 * We are about to append an enter-attribute sequence
34460786Sps		 * just after a space.  Delete the space.
34560786Sps		 */
34660786Sps		attr[curr-1] = AT_INVIS;
34760786Sps		column--;
34860786Sps	} else if (curr > 0 && attr[curr-1] != AT_NORMAL &&
34960786Sps		attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL &&
35060786Sps		attr_ewidth(attr[curr-1]) > 0)
35160786Sps	{
35260786Sps		/*
35360786Sps		 * We are about to append a space just after an
35460786Sps		 * exit-attribute sequence.  Delete the space.
35560786Sps		 */
35660786Sps		a = AT_INVIS;
35760786Sps		column--;
35860786Sps	}
35960786Sps	/* End of magic cookie handling. */
36060786Sps
36160786Sps	linebuf[curr] = c;
36260786Sps	attr[curr] = a;
36360786Sps	column += w;
36460786Sps	return (0);
36560786Sps}
36660786Sps
36760786Sps/*
36860786Sps * Append a character to the line buffer.
36960786Sps * Expand tabs into spaces, handle underlining, boldfacing, etc.
37060786Sps * Returns 0 if ok, 1 if couldn't fit in buffer.
37160786Sps */
37260786Sps	public int
37360786Spspappend(c, pos)
37460786Sps	register int c;
37560786Sps	POSITION pos;
37660786Sps{
37760786Sps	int r;
37860786Sps
37960786Sps	if (pendc)
38060786Sps	{
38160786Sps		if (do_append(pendc, pendpos))
38260786Sps			/*
38360786Sps			 * Oops.  We've probably lost the char which
38460786Sps			 * was in pendc, since caller won't back up.
38560786Sps			 */
38660786Sps			return (1);
38760786Sps		pendc = '\0';
38860786Sps	}
38960786Sps
39060786Sps	if (c == '\r' && bs_mode == BS_SPECIAL)
39160786Sps	{
39260786Sps		/*
39360786Sps		 * Don't put the CR into the buffer until we see
39460786Sps		 * the next char.  If the next char is a newline,
39560786Sps		 * discard the CR.
39660786Sps		 */
39760786Sps		pendc = c;
39860786Sps		pendpos = pos;
39960786Sps		return (0);
40060786Sps	}
40160786Sps
40260786Sps	r = do_append(c, pos);
40360786Sps	/*
40460786Sps	 * If we need to shift the line, do it.
40560786Sps	 * But wait until we get to at least the middle of the screen,
40660786Sps	 * so shifting it doesn't affect the chars we're currently
40760786Sps	 * pappending.  (Bold & underline can get messed up otherwise.)
40860786Sps	 */
40960786Sps	if (cshift < hshift && column > sc_width / 2)
41060786Sps		pshift(hshift - cshift);
41160786Sps	return (r);
41260786Sps}
41360786Sps
41460786Sps	static int
41560786Spsdo_append(c, pos)
41660786Sps	int c;
41760786Sps	POSITION pos;
41860786Sps{
41960786Sps	register char *s;
42060786Sps	register int a;
42160786Sps
42260786Sps#define	STOREC(c,a) \
42360786Sps	if (storec((c),(a),pos)) return (1); else curr++
42460786Sps
42560786Sps	if (c == '\b')
42660786Sps	{
42760786Sps		switch (bs_mode)
42860786Sps		{
42960786Sps		case BS_NORMAL:
43060786Sps			STOREC(c, AT_NORMAL);
43160786Sps			break;
43260786Sps		case BS_CONTROL:
43360786Sps			goto do_control_char;
43460786Sps		case BS_SPECIAL:
43560786Sps			if (curr == 0)
43660786Sps				break;
43760786Sps			backc();
43860786Sps			overstrike = 1;
43960786Sps			break;
44060786Sps		}
44160786Sps	} else if (overstrike)
44260786Sps	{
44360786Sps		/*
44460786Sps		 * Overstrike the character at the current position
44560786Sps		 * in the line buffer.  This will cause either
44660786Sps		 * underline (if a "_" is overstruck),
44760786Sps		 * bold (if an identical character is overstruck),
44860786Sps		 * or just deletion of the character in the buffer.
44960786Sps		 */
45060786Sps		overstrike = 0;
45160786Sps		if ((char)c == linebuf[curr])
45260786Sps			STOREC(linebuf[curr], AT_BOLD);
45360786Sps		else if (c == '_')
45460786Sps			STOREC(linebuf[curr], AT_UNDERLINE);
45560786Sps		else if (linebuf[curr] == '_')
45660786Sps			STOREC(c, AT_UNDERLINE);
45760786Sps		else if (control_char(c))
45860786Sps			goto do_control_char;
45960786Sps		else
46060786Sps			STOREC(c, AT_NORMAL);
46160786Sps	} else if (c == '\t')
46260786Sps	{
46360786Sps		/*
46460786Sps		 * Expand a tab into spaces.
46560786Sps		 */
46660786Sps		if (tabstop == 0)
46760786Sps			tabstop = 1;
46860786Sps		switch (bs_mode)
46960786Sps		{
47060786Sps		case BS_CONTROL:
47160786Sps			goto do_control_char;
47260786Sps		case BS_NORMAL:
47360786Sps		case BS_SPECIAL:
47460786Sps			do
47560786Sps			{
47660786Sps				STOREC(' ', AT_NORMAL);
47760786Sps			} while (((column + cshift) % tabstop) != 0);
47860786Sps			break;
47960786Sps		}
48060786Sps	} else if (control_char(c))
48160786Sps	{
48260786Sps	do_control_char:
48360786Sps		if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && c == ESC))
48460786Sps		{
48560786Sps			/*
48660786Sps			 * Output as a normal character.
48760786Sps			 */
48860786Sps			STOREC(c, AT_NORMAL);
48960786Sps		} else
49060786Sps		{
49160786Sps			/*
49260786Sps			 * Convert to printable representation.
49360786Sps			 */
49460786Sps			s = prchar(c);
49560786Sps			a = binattr;
49660786Sps
49760786Sps			/*
49860786Sps			 * Make sure we can get the entire representation
49960786Sps			 * of the character on this line.
50060786Sps			 */
50160786Sps			if (column + (int) strlen(s) +
50260786Sps			    attr_swidth(a) + attr_ewidth(a) > sc_width)
50360786Sps				return (1);
50460786Sps
50560786Sps			for ( ;  *s != 0;  s++)
50660786Sps				STOREC(*s, a);
50760786Sps		}
50860786Sps	} else
50960786Sps	{
51060786Sps		STOREC(c, AT_NORMAL);
51160786Sps	}
51260786Sps
51360786Sps	return (0);
51460786Sps}
51560786Sps
51660786Sps/*
51760786Sps * Terminate the line in the line buffer.
51860786Sps */
51960786Sps	public void
52060786Spspdone(endline)
52160786Sps	int endline;
52260786Sps{
52360786Sps	if (pendc && (pendc != '\r' || !endline))
52460786Sps		/*
52560786Sps		 * If we had a pending character, put it in the buffer.
52660786Sps		 * But discard a pending CR if we are at end of line
52760786Sps		 * (that is, discard the CR in a CR/LF sequence).
52860786Sps		 */
52960786Sps		(void) do_append(pendc, pendpos);
53060786Sps
53160786Sps	/*
53260786Sps	 * Make sure we've shifted the line, if we need to.
53360786Sps	 */
53460786Sps	if (cshift < hshift)
53560786Sps		pshift(hshift - cshift);
53660786Sps
53760786Sps	/*
53860786Sps	 * Add a newline if necessary,
53960786Sps	 * and append a '\0' to the end of the line.
54060786Sps	 */
54160786Sps	if (column < sc_width || !auto_wrap || ignaw || ctldisp == OPT_ON)
54260786Sps	{
54360786Sps		linebuf[curr] = '\n';
54460786Sps		attr[curr] = AT_NORMAL;
54560786Sps		curr++;
54660786Sps	}
54760786Sps	linebuf[curr] = '\0';
54860786Sps	attr[curr] = AT_NORMAL;
54960786Sps	/*
55060786Sps	 * If we are done with this line, reset the current shift.
55160786Sps	 */
55260786Sps	if (endline)
55360786Sps		cshift = 0;
55460786Sps}
55560786Sps
55660786Sps/*
55760786Sps * Get a character from the current line.
55860786Sps * Return the character as the function return value,
55960786Sps * and the character attribute in *ap.
56060786Sps */
56160786Sps	public int
56260786Spsgline(i, ap)
56360786Sps	register int i;
56460786Sps	register int *ap;
56560786Sps{
56660786Sps	char *s;
56760786Sps
56860786Sps	if (is_null_line)
56960786Sps	{
57060786Sps		/*
57160786Sps		 * If there is no current line, we pretend the line is
57260786Sps		 * either "~" or "", depending on the "twiddle" flag.
57360786Sps		 */
57460786Sps		*ap = AT_BOLD;
57560786Sps		s = (twiddle) ? "~\n" : "\n";
57660786Sps		return (s[i]);
57760786Sps	}
57860786Sps
57960786Sps	*ap = attr[i];
58060786Sps	return (linebuf[i] & 0377);
58160786Sps}
58260786Sps
58360786Sps/*
58460786Sps * Indicate that there is no current line.
58560786Sps */
58660786Sps	public void
58760786Spsnull_line()
58860786Sps{
58960786Sps	is_null_line = 1;
59060786Sps	cshift = 0;
59160786Sps}
59260786Sps
59360786Sps/*
59460786Sps * Analogous to forw_line(), but deals with "raw lines":
59560786Sps * lines which are not split for screen width.
59660786Sps * {{ This is supposed to be more efficient than forw_line(). }}
59760786Sps */
59860786Sps	public POSITION
59960786Spsforw_raw_line(curr_pos, linep)
60060786Sps	POSITION curr_pos;
60160786Sps	char **linep;
60260786Sps{
60360786Sps	register char *p;
60460786Sps	register int c;
60560786Sps	POSITION new_pos;
60660786Sps
60760786Sps	if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
60860786Sps		(c = ch_forw_get()) == EOI)
60960786Sps		return (NULL_POSITION);
61060786Sps
61160786Sps	p = linebuf;
61260786Sps
61360786Sps	for (;;)
61460786Sps	{
61560786Sps		if (c == '\n' || c == EOI)
61660786Sps		{
61760786Sps			new_pos = ch_tell();
61860786Sps			break;
61960786Sps		}
62060786Sps		if (p >= &linebuf[sizeof(linebuf)-1])
62160786Sps		{
62260786Sps			/*
62360786Sps			 * Overflowed the input buffer.
62460786Sps			 * Pretend the line ended here.
62560786Sps			 * {{ The line buffer is supposed to be big
62660786Sps			 *    enough that this never happens. }}
62760786Sps			 */
62860786Sps			new_pos = ch_tell() - 1;
62960786Sps			break;
63060786Sps		}
63160786Sps		*p++ = c;
63260786Sps		c = ch_forw_get();
63360786Sps	}
63460786Sps	*p = '\0';
63560786Sps	if (linep != NULL)
63660786Sps		*linep = linebuf;
63760786Sps	return (new_pos);
63860786Sps}
63960786Sps
64060786Sps/*
64160786Sps * Analogous to back_line(), but deals with "raw lines".
64260786Sps * {{ This is supposed to be more efficient than back_line(). }}
64360786Sps */
64460786Sps	public POSITION
64560786Spsback_raw_line(curr_pos, linep)
64660786Sps	POSITION curr_pos;
64760786Sps	char **linep;
64860786Sps{
64960786Sps	register char *p;
65060786Sps	register int c;
65160786Sps	POSITION new_pos;
65260786Sps
65360786Sps	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
65460786Sps		ch_seek(curr_pos-1))
65560786Sps		return (NULL_POSITION);
65660786Sps
65760786Sps	p = &linebuf[sizeof(linebuf)];
65860786Sps	*--p = '\0';
65960786Sps
66060786Sps	for (;;)
66160786Sps	{
66260786Sps		c = ch_back_get();
66360786Sps		if (c == '\n')
66460786Sps		{
66560786Sps			/*
66660786Sps			 * This is the newline ending the previous line.
66760786Sps			 * We have hit the beginning of the line.
66860786Sps			 */
66960786Sps			new_pos = ch_tell() + 1;
67060786Sps			break;
67160786Sps		}
67260786Sps		if (c == EOI)
67360786Sps		{
67460786Sps			/*
67560786Sps			 * We have hit the beginning of the file.
67660786Sps			 * This must be the first line in the file.
67760786Sps			 * This must, of course, be the beginning of the line.
67860786Sps			 */
67960786Sps			new_pos = ch_zero();
68060786Sps			break;
68160786Sps		}
68260786Sps		if (p <= linebuf)
68360786Sps		{
68460786Sps			/*
68560786Sps			 * Overflowed the input buffer.
68660786Sps			 * Pretend the line ended here.
68760786Sps			 */
68860786Sps			new_pos = ch_tell() + 1;
68960786Sps			break;
69060786Sps		}
69160786Sps		*--p = c;
69260786Sps	}
69360786Sps	if (linep != NULL)
69460786Sps		*linep = p;
69560786Sps	return (new_pos);
69660786Sps}
697