1238730Sdelphij/*
2330571Sdelphij * Copyright (C) 1984-2017  Mark Nudelman
3238730Sdelphij *
4238730Sdelphij * You may distribute under the terms of either the GNU General Public
5238730Sdelphij * License or the Less License, as specified in the README file.
6238730Sdelphij *
7238730Sdelphij * For more information, see the README file.
8238730Sdelphij */
960786Sps
1060786Sps/*
1160786Sps * High level routines dealing with getting lines of input
1260786Sps * from the file being viewed.
1360786Sps *
1460786Sps * When we speak of "lines" here, we mean PRINTABLE lines;
1560786Sps * lines processed with respect to the screen width.
1660786Sps * We use the term "raw line" to refer to lines simply
1760786Sps * delimited by newlines; not processed with respect to screen width.
1860786Sps */
1960786Sps
2060786Sps#include "less.h"
2160786Sps
2260786Spsextern int squeeze;
2360786Spsextern int chopline;
2463128Spsextern int hshift;
2560786Spsextern int quit_if_one_screen;
2660786Spsextern int sigs;
2760786Spsextern int ignore_eoi;
28161475Sdelphijextern int status_col;
2960786Spsextern POSITION start_attnpos;
3060786Spsextern POSITION end_attnpos;
3160786Sps#if HILITE_SEARCH
3260786Spsextern int hilite_search;
3360786Spsextern int size_linebuf;
3460786Sps#endif
3560786Sps
3660786Sps/*
3760786Sps * Get the next line.
3860786Sps * A "current" position is passed and a "new" position is returned.
3960786Sps * The current position is the position of the first character of
4060786Sps * a line.  The new position is the position of the first character
4160786Sps * of the NEXT line.  The line obtained is the line starting at curr_pos.
4260786Sps */
4360786Sps	public POSITION
4460786Spsforw_line(curr_pos)
4560786Sps	POSITION curr_pos;
4660786Sps{
47161475Sdelphij	POSITION base_pos;
4860786Sps	POSITION new_pos;
49330571Sdelphij	int c;
5060786Sps	int blankline;
5160786Sps	int endline;
52330571Sdelphij	int chopped;
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)
63294286Sdelphij	{
6460786Sps		/*
6560786Sps		 * If we are ignoring EOI (command F), only prepare
6660786Sps		 * one line ahead, to avoid getting stuck waiting for
6760786Sps		 * slow data without displaying the data we already have.
6860786Sps		 * If we're not ignoring EOI, we *could* do the same, but
6960786Sps		 * for efficiency we prepare several lines ahead at once.
7060786Sps		 */
7160786Sps		prep_hilite(curr_pos, curr_pos + 3*size_linebuf,
7260786Sps				ignore_eoi ? 1 : -1);
73294286Sdelphij		curr_pos = next_unfiltered(curr_pos);
74294286Sdelphij	}
7560786Sps#endif
7660786Sps	if (ch_seek(curr_pos))
7760786Sps	{
7860786Sps		null_line();
7960786Sps		return (NULL_POSITION);
8060786Sps	}
8160786Sps
82191930Sdelphij	/*
83191930Sdelphij	 * Step back to the beginning of the line.
84191930Sdelphij	 */
85161475Sdelphij	base_pos = curr_pos;
86161475Sdelphij	for (;;)
87161475Sdelphij	{
88161475Sdelphij		if (ABORT_SIGS())
89161475Sdelphij		{
90161475Sdelphij			null_line();
91161475Sdelphij			return (NULL_POSITION);
92161475Sdelphij		}
93161475Sdelphij		c = ch_back_get();
94161475Sdelphij		if (c == EOI)
95161475Sdelphij			break;
96161475Sdelphij		if (c == '\n')
97161475Sdelphij		{
98161475Sdelphij			(void) ch_forw_get();
99161475Sdelphij			break;
100161475Sdelphij		}
101161475Sdelphij		--base_pos;
102161475Sdelphij	}
10360786Sps
104191930Sdelphij	/*
105191930Sdelphij	 * Read forward again to the position we should start at.
106191930Sdelphij	 */
107161475Sdelphij 	prewind();
108161475Sdelphij	plinenum(base_pos);
109161475Sdelphij	(void) ch_seek(base_pos);
110191930Sdelphij	new_pos = base_pos;
111191930Sdelphij	while (new_pos < curr_pos)
112161475Sdelphij	{
113161475Sdelphij		if (ABORT_SIGS())
114161475Sdelphij		{
115161475Sdelphij			null_line();
116161475Sdelphij			return (NULL_POSITION);
117161475Sdelphij		}
118161475Sdelphij		c = ch_forw_get();
119191930Sdelphij		backchars = pappend(c, new_pos);
120191930Sdelphij		new_pos++;
121161475Sdelphij		if (backchars > 0)
122161475Sdelphij		{
123161475Sdelphij			pshift_all();
124191930Sdelphij			new_pos -= backchars;
125161475Sdelphij			while (--backchars >= 0)
126161475Sdelphij				(void) ch_back_get();
127161475Sdelphij		}
128161475Sdelphij	}
129161475Sdelphij	(void) pflushmbc();
130161475Sdelphij	pshift_all();
131161475Sdelphij
132191930Sdelphij	/*
133191930Sdelphij	 * Read the first character to display.
134191930Sdelphij	 */
13560786Sps	c = ch_forw_get();
13660786Sps	if (c == EOI)
13760786Sps	{
13860786Sps		null_line();
13960786Sps		return (NULL_POSITION);
14060786Sps	}
14160786Sps	blankline = (c == '\n' || c == '\r');
14260786Sps
143191930Sdelphij	/*
144191930Sdelphij	 * Read each character in the line and append to the line buffer.
145191930Sdelphij	 */
146330571Sdelphij	chopped = FALSE;
14760786Sps	for (;;)
14860786Sps	{
14960786Sps		if (ABORT_SIGS())
15060786Sps		{
15160786Sps			null_line();
15260786Sps			return (NULL_POSITION);
15360786Sps		}
15460786Sps		if (c == '\n' || c == EOI)
15560786Sps		{
15660786Sps			/*
15760786Sps			 * End of the line.
15860786Sps			 */
159161475Sdelphij			backchars = pflushmbc();
16060786Sps			new_pos = ch_tell();
161161475Sdelphij			if (backchars > 0 && !chopline && hshift == 0)
162161475Sdelphij			{
163161475Sdelphij				new_pos -= backchars + 1;
164161475Sdelphij				endline = FALSE;
165161475Sdelphij			} else
166161475Sdelphij				endline = TRUE;
16760786Sps			break;
16860786Sps		}
169161475Sdelphij		if (c != '\r')
170161475Sdelphij			blankline = 0;
17160786Sps
17260786Sps		/*
17360786Sps		 * Append the char to the line and get the next char.
17460786Sps		 */
175161475Sdelphij		backchars = pappend(c, ch_tell()-1);
176161475Sdelphij		if (backchars > 0)
17760786Sps		{
17860786Sps			/*
17960786Sps			 * The char won't fit in the line; the line
18060786Sps			 * is too long to print in the screen width.
18160786Sps			 * End the line here.
18260786Sps			 */
18363128Sps			if (chopline || hshift > 0)
18460786Sps			{
18560786Sps				do
18660786Sps				{
187221715Sdelphij					if (ABORT_SIGS())
188221715Sdelphij					{
189221715Sdelphij						null_line();
190221715Sdelphij						return (NULL_POSITION);
191221715Sdelphij					}
19260786Sps					c = ch_forw_get();
19360786Sps				} while (c != '\n' && c != EOI);
19460786Sps				new_pos = ch_tell();
19560786Sps				endline = TRUE;
19660786Sps				quit_if_one_screen = FALSE;
197330571Sdelphij				chopped = TRUE;
19860786Sps			} else
19960786Sps			{
200161475Sdelphij				new_pos = ch_tell() - backchars;
20160786Sps				endline = FALSE;
20260786Sps			}
20360786Sps			break;
20460786Sps		}
20560786Sps		c = ch_forw_get();
20660786Sps	}
20760786Sps
208330571Sdelphij	pdone(endline, chopped, 1);
209191930Sdelphij
210191930Sdelphij#if HILITE_SEARCH
211191930Sdelphij	if (is_filtered(base_pos))
212191930Sdelphij	{
213191930Sdelphij		/*
214191930Sdelphij		 * We don't want to display this line.
215191930Sdelphij		 * Get the next line.
216191930Sdelphij		 */
217191930Sdelphij		curr_pos = new_pos;
218191930Sdelphij		goto get_forw_line;
219191930Sdelphij	}
220191930Sdelphij
221191930Sdelphij	if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
222191930Sdelphij		set_status_col('*');
223191930Sdelphij#endif
224191930Sdelphij
22560786Sps	if (squeeze && blankline)
22660786Sps	{
22760786Sps		/*
22860786Sps		 * This line is blank.
22960786Sps		 * Skip down to the last contiguous blank line
23060786Sps		 * and pretend it is the one which we are returning.
23160786Sps		 */
23260786Sps		while ((c = ch_forw_get()) == '\n' || c == '\r')
23360786Sps			if (ABORT_SIGS())
23460786Sps			{
23560786Sps				null_line();
23660786Sps				return (NULL_POSITION);
23760786Sps			}
23860786Sps		if (c != EOI)
23960786Sps			(void) ch_back_get();
24060786Sps		new_pos = ch_tell();
24160786Sps	}
24260786Sps
24360786Sps	return (new_pos);
24460786Sps}
24560786Sps
24660786Sps/*
24760786Sps * Get the previous line.
24860786Sps * A "current" position is passed and a "new" position is returned.
24960786Sps * The current position is the position of the first character of
25060786Sps * a line.  The new position is the position of the first character
25160786Sps * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
25260786Sps */
25360786Sps	public POSITION
25460786Spsback_line(curr_pos)
25560786Sps	POSITION curr_pos;
25660786Sps{
257191930Sdelphij	POSITION new_pos, begin_new_pos, base_pos;
25860786Sps	int c;
25960786Sps	int endline;
260330571Sdelphij	int chopped;
261161475Sdelphij	int backchars;
26260786Sps
263191930Sdelphijget_back_line:
26460786Sps	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
26560786Sps	{
26660786Sps		null_line();
26760786Sps		return (NULL_POSITION);
26860786Sps	}
26960786Sps#if HILITE_SEARCH
270191930Sdelphij	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
27160786Sps		prep_hilite((curr_pos < 3*size_linebuf) ?
27260786Sps				0 : curr_pos - 3*size_linebuf, curr_pos, -1);
27360786Sps#endif
27460786Sps	if (ch_seek(curr_pos-1))
27560786Sps	{
27660786Sps		null_line();
27760786Sps		return (NULL_POSITION);
27860786Sps	}
27960786Sps
28060786Sps	if (squeeze)
28160786Sps	{
28260786Sps		/*
28360786Sps		 * Find out if the "current" line was blank.
28460786Sps		 */
285191930Sdelphij		(void) ch_forw_get();    /* Skip the newline */
286191930Sdelphij		c = ch_forw_get();       /* First char of "current" line */
287191930Sdelphij		(void) ch_back_get();    /* Restore our position */
28860786Sps		(void) ch_back_get();
28960786Sps
29060786Sps		if (c == '\n' || c == '\r')
29160786Sps		{
29260786Sps			/*
29360786Sps			 * The "current" line was blank.
29460786Sps			 * Skip over any preceding blank lines,
29560786Sps			 * since we skipped them in forw_line().
29660786Sps			 */
29760786Sps			while ((c = ch_back_get()) == '\n' || c == '\r')
29860786Sps				if (ABORT_SIGS())
29960786Sps				{
30060786Sps					null_line();
30160786Sps					return (NULL_POSITION);
30260786Sps				}
30360786Sps			if (c == EOI)
30460786Sps			{
30560786Sps				null_line();
30660786Sps				return (NULL_POSITION);
30760786Sps			}
30860786Sps			(void) ch_forw_get();
30960786Sps		}
31060786Sps	}
31160786Sps
31260786Sps	/*
31360786Sps	 * Scan backwards until we hit the beginning of the line.
31460786Sps	 */
31560786Sps	for (;;)
31660786Sps	{
31760786Sps		if (ABORT_SIGS())
31860786Sps		{
31960786Sps			null_line();
32060786Sps			return (NULL_POSITION);
32160786Sps		}
32260786Sps		c = ch_back_get();
32360786Sps		if (c == '\n')
32460786Sps		{
32560786Sps			/*
32660786Sps			 * This is the newline ending the previous line.
32760786Sps			 * We have hit the beginning of the line.
32860786Sps			 */
329191930Sdelphij			base_pos = ch_tell() + 1;
33060786Sps			break;
33160786Sps		}
33260786Sps		if (c == EOI)
33360786Sps		{
33460786Sps			/*
33560786Sps			 * We have hit the beginning of the file.
33660786Sps			 * This must be the first line in the file.
33760786Sps			 * This must, of course, be the beginning of the line.
33860786Sps			 */
339191930Sdelphij			base_pos = ch_tell();
34060786Sps			break;
34160786Sps		}
34260786Sps	}
34360786Sps
34460786Sps	/*
34560786Sps	 * Now scan forwards from the beginning of this line.
34660786Sps	 * We keep discarding "printable lines" (based on screen width)
34760786Sps	 * until we reach the curr_pos.
34860786Sps	 *
34960786Sps	 * {{ This algorithm is pretty inefficient if the lines
35060786Sps	 *    are much longer than the screen width,
35160786Sps	 *    but I don't know of any better way. }}
35260786Sps	 */
353191930Sdelphij	new_pos = base_pos;
35460786Sps	if (ch_seek(new_pos))
35560786Sps	{
35660786Sps		null_line();
35760786Sps		return (NULL_POSITION);
35860786Sps	}
35960786Sps	endline = FALSE;
360161475Sdelphij	prewind();
361161475Sdelphij	plinenum(new_pos);
36260786Sps    loop:
36360786Sps	begin_new_pos = new_pos;
36460786Sps	(void) ch_seek(new_pos);
365330571Sdelphij	chopped = FALSE;
36660786Sps
36760786Sps	do
36860786Sps	{
36960786Sps		c = ch_forw_get();
37060786Sps		if (c == EOI || ABORT_SIGS())
37160786Sps		{
37260786Sps			null_line();
37360786Sps			return (NULL_POSITION);
37460786Sps		}
37560786Sps		new_pos++;
37660786Sps		if (c == '\n')
37760786Sps		{
378161475Sdelphij			backchars = pflushmbc();
379161475Sdelphij			if (backchars > 0 && !chopline && hshift == 0)
380161475Sdelphij			{
381161475Sdelphij				backchars++;
382161475Sdelphij				goto shift;
383161475Sdelphij			}
38460786Sps			endline = TRUE;
38560786Sps			break;
38660786Sps		}
387161475Sdelphij		backchars = pappend(c, ch_tell()-1);
388161475Sdelphij		if (backchars > 0)
38960786Sps		{
39060786Sps			/*
39160786Sps			 * Got a full printable line, but we haven't
39260786Sps			 * reached our curr_pos yet.  Discard the line
39360786Sps			 * and start a new one.
39460786Sps			 */
39563128Sps			if (chopline || hshift > 0)
39660786Sps			{
39760786Sps				endline = TRUE;
398330571Sdelphij				chopped = TRUE;
39960786Sps				quit_if_one_screen = FALSE;
40060786Sps				break;
40160786Sps			}
402161475Sdelphij		shift:
403161475Sdelphij			pshift_all();
404161475Sdelphij			while (backchars-- > 0)
405161475Sdelphij			{
406161475Sdelphij				(void) ch_back_get();
407161475Sdelphij				new_pos--;
408161475Sdelphij			}
40960786Sps			goto loop;
41060786Sps		}
41160786Sps	} while (new_pos < curr_pos);
41260786Sps
413330571Sdelphij	pdone(endline, chopped, 0);
41460786Sps
415191930Sdelphij#if HILITE_SEARCH
416191930Sdelphij	if (is_filtered(base_pos))
417191930Sdelphij	{
418191930Sdelphij		/*
419191930Sdelphij		 * We don't want to display this line.
420191930Sdelphij		 * Get the previous line.
421191930Sdelphij		 */
422191930Sdelphij		curr_pos = begin_new_pos;
423191930Sdelphij		goto get_back_line;
424191930Sdelphij	}
425191930Sdelphij
426237613Sdelphij	if (status_col && curr_pos > 0 && is_hilited(base_pos, curr_pos-1, 1, NULL))
427191930Sdelphij		set_status_col('*');
428191930Sdelphij#endif
429191930Sdelphij
43060786Sps	return (begin_new_pos);
43160786Sps}
43260786Sps
43360786Sps/*
43460786Sps * Set attnpos.
43560786Sps */
43660786Sps	public void
43760786Spsset_attnpos(pos)
43860786Sps	POSITION pos;
43960786Sps{
44060786Sps	int c;
44160786Sps
44260786Sps	if (pos != NULL_POSITION)
44360786Sps	{
44460786Sps		if (ch_seek(pos))
44560786Sps			return;
44660786Sps		for (;;)
44760786Sps		{
44860786Sps			c = ch_forw_get();
44960786Sps			if (c == EOI)
45060786Sps				break;
451294286Sdelphij			if (c == '\n' || c == '\r')
452294286Sdelphij			{
453294286Sdelphij				(void) ch_back_get();
454294286Sdelphij				break;
455294286Sdelphij			}
45660786Sps			pos++;
45760786Sps		}
458294286Sdelphij		end_attnpos = pos;
459294286Sdelphij		for (;;)
460294286Sdelphij		{
461294286Sdelphij			c = ch_back_get();
462294286Sdelphij			if (c == EOI || c == '\n' || c == '\r')
463294286Sdelphij				break;
464294286Sdelphij			pos--;
465294286Sdelphij		}
46660786Sps	}
46760786Sps	start_attnpos = pos;
46860786Sps}
469