160786Sps/*
2240121Sdelphij * Copyright (C) 1984-2012  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 *
7240121Sdelphij * For more information, see the README file.
860786Sps */
960786Sps
1060786Sps
1160786Sps/*
1260786Sps * High level routines dealing with getting lines of input
1360786Sps * from the file being viewed.
1460786Sps *
1560786Sps * When we speak of "lines" here, we mean PRINTABLE lines;
1660786Sps * lines processed with respect to the screen width.
1760786Sps * We use the term "raw line" to refer to lines simply
1860786Sps * delimited by newlines; not processed with respect to screen width.
1960786Sps */
2060786Sps
2160786Sps#include "less.h"
2260786Sps
2360786Spsextern int squeeze;
2460786Spsextern int chopline;
2563128Spsextern int hshift;
2660786Spsextern int quit_if_one_screen;
2760786Spsextern int sigs;
2860786Spsextern int ignore_eoi;
29161475Sdelphijextern int status_col;
3060786Spsextern POSITION start_attnpos;
3160786Spsextern POSITION end_attnpos;
3260786Sps#if HILITE_SEARCH
3360786Spsextern int hilite_search;
3460786Spsextern int size_linebuf;
3560786Sps#endif
3660786Sps
3760786Sps/*
3860786Sps * Get the next line.
3960786Sps * A "current" position is passed and a "new" position is returned.
4060786Sps * The current position is the position of the first character of
4160786Sps * a line.  The new position is the position of the first character
4260786Sps * of the NEXT line.  The line obtained is the line starting at curr_pos.
4360786Sps */
4460786Sps	public POSITION
4560786Spsforw_line(curr_pos)
4660786Sps	POSITION curr_pos;
4760786Sps{
48161475Sdelphij	POSITION base_pos;
4960786Sps	POSITION new_pos;
5060786Sps	register int c;
5160786Sps	int blankline;
5260786Sps	int endline;
53161475Sdelphij	int backchars;
5460786Sps
55191930Sdelphijget_forw_line:
5660786Sps	if (curr_pos == NULL_POSITION)
5760786Sps	{
5860786Sps		null_line();
5960786Sps		return (NULL_POSITION);
6060786Sps	}
6160786Sps#if HILITE_SEARCH
62191930Sdelphij	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
6360786Sps		/*
6460786Sps		 * If we are ignoring EOI (command F), only prepare
6560786Sps		 * one line ahead, to avoid getting stuck waiting for
6660786Sps		 * slow data without displaying the data we already have.
6760786Sps		 * If we're not ignoring EOI, we *could* do the same, but
6860786Sps		 * for efficiency we prepare several lines ahead at once.
6960786Sps		 */
7060786Sps		prep_hilite(curr_pos, curr_pos + 3*size_linebuf,
7160786Sps				ignore_eoi ? 1 : -1);
7260786Sps#endif
7360786Sps	if (ch_seek(curr_pos))
7460786Sps	{
7560786Sps		null_line();
7660786Sps		return (NULL_POSITION);
7760786Sps	}
7860786Sps
79191930Sdelphij	/*
80191930Sdelphij	 * Step back to the beginning of the line.
81191930Sdelphij	 */
82161475Sdelphij	base_pos = curr_pos;
83161475Sdelphij	for (;;)
84161475Sdelphij	{
85161475Sdelphij		if (ABORT_SIGS())
86161475Sdelphij		{
87161475Sdelphij			null_line();
88161475Sdelphij			return (NULL_POSITION);
89161475Sdelphij		}
90161475Sdelphij		c = ch_back_get();
91161475Sdelphij		if (c == EOI)
92161475Sdelphij			break;
93161475Sdelphij		if (c == '\n')
94161475Sdelphij		{
95161475Sdelphij			(void) ch_forw_get();
96161475Sdelphij			break;
97161475Sdelphij		}
98161475Sdelphij		--base_pos;
99161475Sdelphij	}
10060786Sps
101191930Sdelphij	/*
102191930Sdelphij	 * Read forward again to the position we should start at.
103191930Sdelphij	 */
104161475Sdelphij 	prewind();
105161475Sdelphij	plinenum(base_pos);
106161475Sdelphij	(void) ch_seek(base_pos);
107191930Sdelphij	new_pos = base_pos;
108191930Sdelphij	while (new_pos < curr_pos)
109161475Sdelphij	{
110161475Sdelphij		if (ABORT_SIGS())
111161475Sdelphij		{
112161475Sdelphij			null_line();
113161475Sdelphij			return (NULL_POSITION);
114161475Sdelphij		}
115161475Sdelphij		c = ch_forw_get();
116191930Sdelphij		backchars = pappend(c, new_pos);
117191930Sdelphij		new_pos++;
118161475Sdelphij		if (backchars > 0)
119161475Sdelphij		{
120161475Sdelphij			pshift_all();
121191930Sdelphij			new_pos -= backchars;
122161475Sdelphij			while (--backchars >= 0)
123161475Sdelphij				(void) ch_back_get();
124161475Sdelphij		}
125161475Sdelphij	}
126161475Sdelphij	(void) pflushmbc();
127161475Sdelphij	pshift_all();
128161475Sdelphij
129191930Sdelphij	/*
130191930Sdelphij	 * Read the first character to display.
131191930Sdelphij	 */
13260786Sps	c = ch_forw_get();
13360786Sps	if (c == EOI)
13460786Sps	{
13560786Sps		null_line();
13660786Sps		return (NULL_POSITION);
13760786Sps	}
13860786Sps	blankline = (c == '\n' || c == '\r');
13960786Sps
140191930Sdelphij	/*
141191930Sdelphij	 * Read each character in the line and append to the line buffer.
142191930Sdelphij	 */
14360786Sps	for (;;)
14460786Sps	{
14560786Sps		if (ABORT_SIGS())
14660786Sps		{
14760786Sps			null_line();
14860786Sps			return (NULL_POSITION);
14960786Sps		}
15060786Sps		if (c == '\n' || c == EOI)
15160786Sps		{
15260786Sps			/*
15360786Sps			 * End of the line.
15460786Sps			 */
155161475Sdelphij			backchars = pflushmbc();
15660786Sps			new_pos = ch_tell();
157161475Sdelphij			if (backchars > 0 && !chopline && hshift == 0)
158161475Sdelphij			{
159161475Sdelphij				new_pos -= backchars + 1;
160161475Sdelphij				endline = FALSE;
161161475Sdelphij			} else
162161475Sdelphij				endline = TRUE;
16360786Sps			break;
16460786Sps		}
165161475Sdelphij		if (c != '\r')
166161475Sdelphij			blankline = 0;
16760786Sps
16860786Sps		/*
16960786Sps		 * Append the char to the line and get the next char.
17060786Sps		 */
171161475Sdelphij		backchars = pappend(c, ch_tell()-1);
172161475Sdelphij		if (backchars > 0)
17360786Sps		{
17460786Sps			/*
17560786Sps			 * The char won't fit in the line; the line
17660786Sps			 * is too long to print in the screen width.
17760786Sps			 * End the line here.
17860786Sps			 */
17963128Sps			if (chopline || hshift > 0)
18060786Sps			{
18160786Sps				do
18260786Sps				{
183221715Sdelphij					if (ABORT_SIGS())
184221715Sdelphij					{
185221715Sdelphij						null_line();
186221715Sdelphij						return (NULL_POSITION);
187221715Sdelphij					}
18860786Sps					c = ch_forw_get();
18960786Sps				} while (c != '\n' && c != EOI);
19060786Sps				new_pos = ch_tell();
19160786Sps				endline = TRUE;
19260786Sps				quit_if_one_screen = FALSE;
19360786Sps			} else
19460786Sps			{
195161475Sdelphij				new_pos = ch_tell() - backchars;
19660786Sps				endline = FALSE;
19760786Sps			}
19860786Sps			break;
19960786Sps		}
20060786Sps		c = ch_forw_get();
20160786Sps	}
20260786Sps
203195941Sdelphij	pdone(endline, 1);
204191930Sdelphij
205191930Sdelphij#if HILITE_SEARCH
206191930Sdelphij	if (is_filtered(base_pos))
207191930Sdelphij	{
208191930Sdelphij		/*
209191930Sdelphij		 * We don't want to display this line.
210191930Sdelphij		 * Get the next line.
211191930Sdelphij		 */
212191930Sdelphij		curr_pos = new_pos;
213191930Sdelphij		goto get_forw_line;
214191930Sdelphij	}
215191930Sdelphij
216191930Sdelphij	if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
217191930Sdelphij		set_status_col('*');
218191930Sdelphij#endif
219191930Sdelphij
22060786Sps	if (squeeze && blankline)
22160786Sps	{
22260786Sps		/*
22360786Sps		 * This line is blank.
22460786Sps		 * Skip down to the last contiguous blank line
22560786Sps		 * and pretend it is the one which we are returning.
22660786Sps		 */
22760786Sps		while ((c = ch_forw_get()) == '\n' || c == '\r')
22860786Sps			if (ABORT_SIGS())
22960786Sps			{
23060786Sps				null_line();
23160786Sps				return (NULL_POSITION);
23260786Sps			}
23360786Sps		if (c != EOI)
23460786Sps			(void) ch_back_get();
23560786Sps		new_pos = ch_tell();
23660786Sps	}
23760786Sps
23860786Sps	return (new_pos);
23960786Sps}
24060786Sps
24160786Sps/*
24260786Sps * Get the previous line.
24360786Sps * A "current" position is passed and a "new" position is returned.
24460786Sps * The current position is the position of the first character of
24560786Sps * a line.  The new position is the position of the first character
24660786Sps * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
24760786Sps */
24860786Sps	public POSITION
24960786Spsback_line(curr_pos)
25060786Sps	POSITION curr_pos;
25160786Sps{
252191930Sdelphij	POSITION new_pos, begin_new_pos, base_pos;
25360786Sps	int c;
25460786Sps	int endline;
255161475Sdelphij	int backchars;
25660786Sps
257191930Sdelphijget_back_line:
25860786Sps	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
25960786Sps	{
26060786Sps		null_line();
26160786Sps		return (NULL_POSITION);
26260786Sps	}
26360786Sps#if HILITE_SEARCH
264191930Sdelphij	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
26560786Sps		prep_hilite((curr_pos < 3*size_linebuf) ?
26660786Sps				0 : curr_pos - 3*size_linebuf, curr_pos, -1);
26760786Sps#endif
26860786Sps	if (ch_seek(curr_pos-1))
26960786Sps	{
27060786Sps		null_line();
27160786Sps		return (NULL_POSITION);
27260786Sps	}
27360786Sps
27460786Sps	if (squeeze)
27560786Sps	{
27660786Sps		/*
27760786Sps		 * Find out if the "current" line was blank.
27860786Sps		 */
279191930Sdelphij		(void) ch_forw_get();    /* Skip the newline */
280191930Sdelphij		c = ch_forw_get();       /* First char of "current" line */
281191930Sdelphij		(void) ch_back_get();    /* Restore our position */
28260786Sps		(void) ch_back_get();
28360786Sps
28460786Sps		if (c == '\n' || c == '\r')
28560786Sps		{
28660786Sps			/*
28760786Sps			 * The "current" line was blank.
28860786Sps			 * Skip over any preceding blank lines,
28960786Sps			 * since we skipped them in forw_line().
29060786Sps			 */
29160786Sps			while ((c = ch_back_get()) == '\n' || c == '\r')
29260786Sps				if (ABORT_SIGS())
29360786Sps				{
29460786Sps					null_line();
29560786Sps					return (NULL_POSITION);
29660786Sps				}
29760786Sps			if (c == EOI)
29860786Sps			{
29960786Sps				null_line();
30060786Sps				return (NULL_POSITION);
30160786Sps			}
30260786Sps			(void) ch_forw_get();
30360786Sps		}
30460786Sps	}
30560786Sps
30660786Sps	/*
30760786Sps	 * Scan backwards until we hit the beginning of the line.
30860786Sps	 */
30960786Sps	for (;;)
31060786Sps	{
31160786Sps		if (ABORT_SIGS())
31260786Sps		{
31360786Sps			null_line();
31460786Sps			return (NULL_POSITION);
31560786Sps		}
31660786Sps		c = ch_back_get();
31760786Sps		if (c == '\n')
31860786Sps		{
31960786Sps			/*
32060786Sps			 * This is the newline ending the previous line.
32160786Sps			 * We have hit the beginning of the line.
32260786Sps			 */
323191930Sdelphij			base_pos = ch_tell() + 1;
32460786Sps			break;
32560786Sps		}
32660786Sps		if (c == EOI)
32760786Sps		{
32860786Sps			/*
32960786Sps			 * We have hit the beginning of the file.
33060786Sps			 * This must be the first line in the file.
33160786Sps			 * This must, of course, be the beginning of the line.
33260786Sps			 */
333191930Sdelphij			base_pos = ch_tell();
33460786Sps			break;
33560786Sps		}
33660786Sps	}
33760786Sps
33860786Sps	/*
33960786Sps	 * Now scan forwards from the beginning of this line.
34060786Sps	 * We keep discarding "printable lines" (based on screen width)
34160786Sps	 * until we reach the curr_pos.
34260786Sps	 *
34360786Sps	 * {{ This algorithm is pretty inefficient if the lines
34460786Sps	 *    are much longer than the screen width,
34560786Sps	 *    but I don't know of any better way. }}
34660786Sps	 */
347191930Sdelphij	new_pos = base_pos;
34860786Sps	if (ch_seek(new_pos))
34960786Sps	{
35060786Sps		null_line();
35160786Sps		return (NULL_POSITION);
35260786Sps	}
35360786Sps	endline = FALSE;
354161475Sdelphij	prewind();
355161475Sdelphij	plinenum(new_pos);
35660786Sps    loop:
35760786Sps	begin_new_pos = new_pos;
35860786Sps	(void) ch_seek(new_pos);
35960786Sps
36060786Sps	do
36160786Sps	{
36260786Sps		c = ch_forw_get();
36360786Sps		if (c == EOI || ABORT_SIGS())
36460786Sps		{
36560786Sps			null_line();
36660786Sps			return (NULL_POSITION);
36760786Sps		}
36860786Sps		new_pos++;
36960786Sps		if (c == '\n')
37060786Sps		{
371161475Sdelphij			backchars = pflushmbc();
372161475Sdelphij			if (backchars > 0 && !chopline && hshift == 0)
373161475Sdelphij			{
374161475Sdelphij				backchars++;
375161475Sdelphij				goto shift;
376161475Sdelphij			}
37760786Sps			endline = TRUE;
37860786Sps			break;
37960786Sps		}
380161475Sdelphij		backchars = pappend(c, ch_tell()-1);
381161475Sdelphij		if (backchars > 0)
38260786Sps		{
38360786Sps			/*
38460786Sps			 * Got a full printable line, but we haven't
38560786Sps			 * reached our curr_pos yet.  Discard the line
38660786Sps			 * and start a new one.
38760786Sps			 */
38863128Sps			if (chopline || hshift > 0)
38960786Sps			{
39060786Sps				endline = TRUE;
39160786Sps				quit_if_one_screen = FALSE;
39260786Sps				break;
39360786Sps			}
394161475Sdelphij		shift:
395161475Sdelphij			pshift_all();
396161475Sdelphij			while (backchars-- > 0)
397161475Sdelphij			{
398161475Sdelphij				(void) ch_back_get();
399161475Sdelphij				new_pos--;
400161475Sdelphij			}
40160786Sps			goto loop;
40260786Sps		}
40360786Sps	} while (new_pos < curr_pos);
40460786Sps
405195941Sdelphij	pdone(endline, 0);
40660786Sps
407191930Sdelphij#if HILITE_SEARCH
408191930Sdelphij	if (is_filtered(base_pos))
409191930Sdelphij	{
410191930Sdelphij		/*
411191930Sdelphij		 * We don't want to display this line.
412191930Sdelphij		 * Get the previous line.
413191930Sdelphij		 */
414191930Sdelphij		curr_pos = begin_new_pos;
415191930Sdelphij		goto get_back_line;
416191930Sdelphij	}
417191930Sdelphij
418240121Sdelphij	if (status_col && curr_pos > 0 && is_hilited(base_pos, curr_pos-1, 1, NULL))
419191930Sdelphij		set_status_col('*');
420191930Sdelphij#endif
421191930Sdelphij
42260786Sps	return (begin_new_pos);
42360786Sps}
42460786Sps
42560786Sps/*
42660786Sps * Set attnpos.
42760786Sps */
42860786Sps	public void
42960786Spsset_attnpos(pos)
43060786Sps	POSITION pos;
43160786Sps{
43260786Sps	int c;
43360786Sps
43460786Sps	if (pos != NULL_POSITION)
43560786Sps	{
43660786Sps		if (ch_seek(pos))
43760786Sps			return;
43860786Sps		for (;;)
43960786Sps		{
44060786Sps			c = ch_forw_get();
44160786Sps			if (c == EOI)
44260786Sps				return;
44360786Sps			if (c != '\n' && c != '\r')
44460786Sps				break;
44560786Sps			pos++;
44660786Sps		}
44760786Sps	}
44860786Sps	start_attnpos = pos;
44960786Sps	for (;;)
45060786Sps	{
45160786Sps		c = ch_forw_get();
45260786Sps		pos++;
45360786Sps		if (c == EOI || c == '\n' || c == '\r')
45460786Sps			break;
45560786Sps	}
45660786Sps	end_attnpos = pos;
45760786Sps}
458