160812Sps/* $FreeBSD: stable/10/contrib/less/forwback.c 330571 2018-03-07 06:39:00Z delphij $ */
2238730Sdelphij/*
3330571Sdelphij * Copyright (C) 1984-2017  Mark Nudelman
4238730Sdelphij *
5238730Sdelphij * You may distribute under the terms of either the GNU General Public
6238730Sdelphij * License or the Less License, as specified in the README file.
7238730Sdelphij *
8238730Sdelphij * For more information, see the README file.
9238730Sdelphij */
1060786Sps
1160786Sps
1260786Sps/*
1360786Sps * Primitives for displaying the file on the screen,
1460786Sps * scrolling either forward or backward.
1560786Sps */
1660786Sps
1760786Sps#include "less.h"
1860786Sps#include "position.h"
1960786Sps
2060786Spspublic int screen_trashed;
2160786Spspublic int squished;
2260786Spspublic int no_back_scroll = 0;
23170259Sdelphijpublic int forw_prompt;
24294286Sdelphijpublic int same_pos_bell = 1;
2560786Sps
2660786Spsextern int sigs;
2760786Spsextern int top_scroll;
2860786Spsextern int quiet;
2960786Spsextern int sc_width, sc_height;
30170259Sdelphijextern int less_is_more;
3160786Spsextern int plusoption;
3260786Spsextern int forw_scroll;
3360786Spsextern int back_scroll;
3460786Spsextern int ignore_eoi;
3560786Spsextern int clear_bg;
3660786Spsextern int final_attr;
37170259Sdelphijextern int oldbot;
38294286Sdelphij#if HILITE_SEARCH
39294286Sdelphijextern int size_linebuf;
40294286Sdelphijextern int hilite_search;
41294286Sdelphijextern int status_col;
42294286Sdelphij#endif
4360786Sps#if TAGS
4460786Spsextern char *tagoption;
4560786Sps#endif
4660786Sps
4760786Sps/*
4860786Sps * Sound the bell to indicate user is trying to move past end of file.
4960786Sps */
5060786Sps	static void
5160786Spseof_bell()
5260786Sps{
5360786Sps	if (quiet == NOT_QUIET)
5460786Sps		bell();
5560786Sps	else
5660786Sps		vbell();
5760786Sps}
5860786Sps
5960786Sps/*
60191930Sdelphij * Check to see if the end of file is currently displayed.
6160786Sps */
62191930Sdelphij	public int
63191930Sdelphijeof_displayed()
6460786Sps{
6560786Sps	POSITION pos;
6660786Sps
6760786Sps	if (ignore_eoi)
68191930Sdelphij		return (0);
69191930Sdelphij
70191930Sdelphij	if (ch_length() == NULL_POSITION)
71191930Sdelphij		/*
72191930Sdelphij		 * If the file length is not known,
73191930Sdelphij		 * we can't possibly be displaying EOF.
74191930Sdelphij		 */
75191930Sdelphij		return (0);
76191930Sdelphij
7760786Sps	/*
7860786Sps	 * If the bottom line is empty, we are at EOF.
7960786Sps	 * If the bottom line ends at the file length,
8060786Sps	 * we must be just at EOF.
8160786Sps	 */
8260786Sps	pos = position(BOTTOM_PLUS_ONE);
83191930Sdelphij	return (pos == NULL_POSITION || pos == ch_length());
8460786Sps}
8560786Sps
8660786Sps/*
87191930Sdelphij * Check to see if the entire file is currently displayed.
88191930Sdelphij */
89191930Sdelphij	public int
90191930Sdelphijentire_file_displayed()
91191930Sdelphij{
92191930Sdelphij	POSITION pos;
93191930Sdelphij
94191930Sdelphij	/* Make sure last line of file is displayed. */
95191930Sdelphij	if (!eof_displayed())
96191930Sdelphij		return (0);
97191930Sdelphij
98191930Sdelphij	/* Make sure first line of file is displayed. */
99191930Sdelphij	pos = position(0);
100191930Sdelphij	return (pos == NULL_POSITION || pos == 0);
101191930Sdelphij}
102191930Sdelphij
103191930Sdelphij/*
10460786Sps * If the screen is "squished", repaint it.
10560786Sps * "Squished" means the first displayed line is not at the top
10660786Sps * of the screen; this can happen when we display a short file
10760786Sps * for the first time.
10860786Sps */
109170259Sdelphij	public void
11060786Spssquish_check()
11160786Sps{
11260786Sps	if (!squished)
11360786Sps		return;
11460786Sps	squished = 0;
11560786Sps	repaint();
11660786Sps}
11760786Sps
11860786Sps/*
11960786Sps * Display n lines, scrolling forward,
12060786Sps * starting at position pos in the input file.
12160786Sps * "force" means display the n lines even if we hit end of file.
12260786Sps * "only_last" means display only the last screenful if n > screen size.
12360786Sps * "nblank" is the number of blank lines to draw before the first
12460786Sps *   real line.  If nblank > 0, the pos must be NULL_POSITION.
12560786Sps *   The first real line after the blanks will start at ch_zero().
12660786Sps */
12760786Sps	public void
12860786Spsforw(n, pos, force, only_last, nblank)
129330571Sdelphij	int n;
13060786Sps	POSITION pos;
13160786Sps	int force;
13260786Sps	int only_last;
13360786Sps	int nblank;
13460786Sps{
13560786Sps	int nlines = 0;
13660786Sps	int do_repaint;
13760786Sps	static int first_time = 1;
13860786Sps
13960786Sps	squish_check();
14060786Sps
14160786Sps	/*
14260786Sps	 * do_repaint tells us not to display anything till the end,
14360786Sps	 * then just repaint the entire screen.
14460786Sps	 * We repaint if we are supposed to display only the last
14560786Sps	 * screenful and the request is for more than a screenful.
14660786Sps	 * Also if the request exceeds the forward scroll limit
14760786Sps	 * (but not if the request is for exactly a screenful, since
14860786Sps	 * repainting itself involves scrolling forward a screenful).
14960786Sps	 */
15060786Sps	do_repaint = (only_last && n > sc_height-1) ||
15160786Sps		(forw_scroll >= 0 && n > forw_scroll && n != sc_height-1);
15260786Sps
153294286Sdelphij#if HILITE_SEARCH
154294286Sdelphij	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) {
155294286Sdelphij		prep_hilite(pos, pos + 4*size_linebuf, ignore_eoi ? 1 : -1);
156294286Sdelphij		pos = next_unfiltered(pos);
157294286Sdelphij	}
158294286Sdelphij#endif
159294286Sdelphij
16060786Sps	if (!do_repaint)
16160786Sps	{
16260786Sps		if (top_scroll && n >= sc_height - 1 && pos != ch_length())
16360786Sps		{
16460786Sps			/*
16560786Sps			 * Start a new screen.
16660786Sps			 * {{ This is not really desirable if we happen
16760786Sps			 *    to hit eof in the middle of this screen,
16860786Sps			 *    but we don't yet know if that will happen. }}
16960786Sps			 */
17060786Sps			pos_clear();
17160786Sps			add_forw_pos(pos);
17260786Sps			force = 1;
173170259Sdelphij			if (less_is_more == 0) {
174170259Sdelphij				clear();
17560812Sps				home();
17660812Sps			}
17760786Sps		}
17860786Sps
17960786Sps		if (pos != position(BOTTOM_PLUS_ONE) || empty_screen())
18060786Sps		{
18160786Sps			/*
18260786Sps			 * This is not contiguous with what is
18360786Sps			 * currently displayed.  Clear the screen image
18460786Sps			 * (position table) and start a new screen.
18560786Sps			 */
18660786Sps			pos_clear();
18760786Sps			add_forw_pos(pos);
18860786Sps			force = 1;
18960786Sps			if (top_scroll)
19060786Sps			{
191170259Sdelphij				clear();
19260786Sps				home();
19360786Sps			} else if (!first_time)
19460786Sps			{
19560786Sps				putstr("...skipping...\n");
19660786Sps			}
19760786Sps		}
19860786Sps	}
19960786Sps
20060786Sps	while (--n >= 0)
20160786Sps	{
20260786Sps		/*
20360786Sps		 * Read the next line of input.
20460786Sps		 */
20560786Sps		if (nblank > 0)
20660786Sps		{
20760786Sps			/*
20860786Sps			 * Still drawing blanks; don't get a line
20960786Sps			 * from the file yet.
21060786Sps			 * If this is the last blank line, get ready to
21160786Sps			 * read a line starting at ch_zero() next time.
21260786Sps			 */
21360786Sps			if (--nblank == 0)
21460786Sps				pos = ch_zero();
21560786Sps		} else
21660786Sps		{
21760786Sps			/*
21860786Sps			 * Get the next line from the file.
21960786Sps			 */
22060786Sps			pos = forw_line(pos);
221294286Sdelphij#if HILITE_SEARCH
222294286Sdelphij			pos = next_unfiltered(pos);
223294286Sdelphij#endif
22460786Sps			if (pos == NULL_POSITION)
22560786Sps			{
22660786Sps				/*
22760786Sps				 * End of file: stop here unless the top line
22860786Sps				 * is still empty, or "force" is true.
22960786Sps				 * Even if force is true, stop when the last
23060786Sps				 * line in the file reaches the top of screen.
23160786Sps				 */
23260786Sps				if (!force && position(TOP) != NULL_POSITION)
23360786Sps					break;
23460786Sps				if (!empty_lines(0, 0) &&
23560786Sps				    !empty_lines(1, 1) &&
23660786Sps				     empty_lines(2, sc_height-1))
23760786Sps					break;
23860786Sps			}
23960786Sps		}
24060786Sps		/*
24160786Sps		 * Add the position of the next line to the position table.
24260786Sps		 * Display the current line on the screen.
24360786Sps		 */
24460786Sps		add_forw_pos(pos);
24560786Sps		nlines++;
24660786Sps		if (do_repaint)
24760786Sps			continue;
24860786Sps		/*
24960786Sps		 * If this is the first screen displayed and
25060786Sps		 * we hit an early EOF (i.e. before the requested
25160786Sps		 * number of lines), we "squish" the display down
25260786Sps		 * at the bottom of the screen.
25360786Sps		 * But don't do this if a + option or a -t option
25460786Sps		 * was given.  These options can cause us to
25560786Sps		 * start the display after the beginning of the file,
25660786Sps		 * and it is not appropriate to squish in that case.
25760786Sps		 */
258170259Sdelphij		if ((first_time || less_is_more) &&
25960816Sps		    pos == NULL_POSITION && !top_scroll &&
26060786Sps#if TAGS
26160786Sps		    tagoption == NULL &&
26260786Sps#endif
26360786Sps		    !plusoption)
26460786Sps		{
26560786Sps			squished = 1;
26660786Sps			continue;
26760786Sps		}
26860786Sps		put_line();
269170259Sdelphij#if 0
270170259Sdelphij		/* {{
271170259Sdelphij		 * Can't call clear_eol here.  The cursor might be at end of line
272170259Sdelphij		 * on an ignaw terminal, so clear_eol would clear the last char
273170259Sdelphij		 * of the current line instead of all of the next line.
274170259Sdelphij		 * If we really need to do this on clear_bg terminals, we need
275170259Sdelphij		 * to find a better way.
276170259Sdelphij		 * }}
277170259Sdelphij		 */
278161478Sdelphij		if (clear_bg && apply_at_specials(final_attr) != AT_NORMAL)
27960786Sps		{
28060786Sps			/*
28160786Sps			 * Writing the last character on the last line
28260786Sps			 * of the display may have scrolled the screen.
28360786Sps			 * If we were in standout mode, clear_bg terminals
28460786Sps			 * will fill the new line with the standout color.
28560786Sps			 * Now we're in normal mode again, so clear the line.
28660786Sps			 */
28760786Sps			clear_eol();
28860786Sps		}
289170259Sdelphij#endif
290170259Sdelphij		forw_prompt = 1;
29160786Sps	}
29260786Sps
293330571Sdelphij	if (nlines == 0 && !ignore_eoi && same_pos_bell)
29460786Sps		eof_bell();
29560786Sps	else if (do_repaint)
29660786Sps		repaint();
29760786Sps	first_time = 0;
29860786Sps	(void) currline(BOTTOM);
29960786Sps}
30060786Sps
30160786Sps/*
30260786Sps * Display n lines, scrolling backward.
30360786Sps */
30460786Sps	public void
30560786Spsback(n, pos, force, only_last)
306330571Sdelphij	int n;
30760786Sps	POSITION pos;
30860786Sps	int force;
30960786Sps	int only_last;
31060786Sps{
31160786Sps	int nlines = 0;
31260786Sps	int do_repaint;
31360786Sps
31460786Sps	squish_check();
31560786Sps	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
316294286Sdelphij#if HILITE_SEARCH
317294286Sdelphij	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) {
318294286Sdelphij		prep_hilite((pos < 3*size_linebuf) ?  0 : pos - 3*size_linebuf, pos, -1);
319294286Sdelphij	}
320294286Sdelphij#endif
32160786Sps	while (--n >= 0)
32260786Sps	{
32360786Sps		/*
32460786Sps		 * Get the previous line of input.
32560786Sps		 */
326294286Sdelphij#if HILITE_SEARCH
327294286Sdelphij		pos = prev_unfiltered(pos);
328294286Sdelphij#endif
329294286Sdelphij
33060786Sps		pos = back_line(pos);
33160786Sps		if (pos == NULL_POSITION)
33260786Sps		{
33360786Sps			/*
33460786Sps			 * Beginning of file: stop here unless "force" is true.
33560786Sps			 */
33660786Sps			if (!force)
33760786Sps				break;
33860786Sps		}
33960786Sps		/*
34060786Sps		 * Add the position of the previous line to the position table.
34160786Sps		 * Display the line on the screen.
34260786Sps		 */
34360786Sps		add_back_pos(pos);
34460786Sps		nlines++;
34560786Sps		if (!do_repaint)
34660786Sps		{
34760786Sps			home();
34860786Sps			add_line();
34960786Sps			put_line();
35060786Sps		}
35160786Sps	}
35260786Sps
353294286Sdelphij	if (nlines == 0 && same_pos_bell)
35460786Sps		eof_bell();
35560786Sps	else if (do_repaint)
35660786Sps		repaint();
357170259Sdelphij	else if (!oldbot)
358170259Sdelphij		lower_left();
35960786Sps	(void) currline(BOTTOM);
36060786Sps}
36160786Sps
36260786Sps/*
36360786Sps * Display n more lines, forward.
36460786Sps * Start just after the line currently displayed at the bottom of the screen.
36560786Sps */
36660786Sps	public void
36760786Spsforward(n, force, only_last)
36860786Sps	int n;
36960786Sps	int force;
37060786Sps	int only_last;
37160786Sps{
37260786Sps	POSITION pos;
37360786Sps
374191930Sdelphij	if (get_quit_at_eof() && eof_displayed() && !(ch_getflags() & CH_HELPFILE))
37560786Sps	{
37660786Sps		/*
37760786Sps		 * If the -e flag is set and we're trying to go
37860786Sps		 * forward from end-of-file, go on to the next file.
37960786Sps		 */
38060786Sps		if (edit_next(1))
38160786Sps			quit(QUIT_OK);
38260786Sps		return;
38360786Sps	}
38460786Sps
38560786Sps	pos = position(BOTTOM_PLUS_ONE);
38660786Sps	if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
38760786Sps	{
38860786Sps		if (ignore_eoi)
38960786Sps		{
39060786Sps			/*
39160786Sps			 * ignore_eoi is to support A_F_FOREVER.
39260786Sps			 * Back up until there is a line at the bottom
39360786Sps			 * of the screen.
39460786Sps			 */
39560786Sps			if (empty_screen())
39660786Sps				pos = ch_zero();
39760786Sps			else
39860786Sps			{
39960786Sps				do
40060786Sps				{
40160786Sps					back(1, position(TOP), 1, 0);
40260786Sps					pos = position(BOTTOM_PLUS_ONE);
40360786Sps				} while (pos == NULL_POSITION);
40460786Sps			}
40560786Sps		} else
40660786Sps		{
40760786Sps			eof_bell();
40860786Sps			return;
40960786Sps		}
41060786Sps	}
41160786Sps	forw(n, pos, force, only_last, 0);
41260786Sps}
41360786Sps
41460786Sps/*
41560786Sps * Display n more lines, backward.
41660786Sps * Start just before the line currently displayed at the top of the screen.
41760786Sps */
41860786Sps	public void
41960786Spsbackward(n, force, only_last)
42060786Sps	int n;
42160786Sps	int force;
42260786Sps	int only_last;
42360786Sps{
42460786Sps	POSITION pos;
42560786Sps
42660786Sps	pos = position(TOP);
42760786Sps	if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
42860786Sps	{
42960786Sps		eof_bell();
43060786Sps		return;
43160786Sps	}
43260786Sps	back(n, pos, force, only_last);
43360786Sps}
43460786Sps
43560786Sps/*
43660786Sps * Get the backwards scroll limit.
43760786Sps * Must call this function instead of just using the value of
43860786Sps * back_scroll, because the default case depends on sc_height and
43960786Sps * top_scroll, as well as back_scroll.
44060786Sps */
44160786Sps	public int
44260786Spsget_back_scroll()
44360786Sps{
44460786Sps	if (no_back_scroll)
44560786Sps		return (0);
44660786Sps	if (back_scroll >= 0)
44760786Sps		return (back_scroll);
44860786Sps	if (top_scroll)
44960786Sps		return (sc_height - 2);
45060786Sps	return (10000); /* infinity */
45160786Sps}
452330571Sdelphij
453330571Sdelphij/*
454330571Sdelphij * Return number of displayable lines in the file.
455330571Sdelphij * Stop counting at screen height + 1.
456330571Sdelphij */
457330571Sdelphij	public int
458330571Sdelphijget_line_count()
459330571Sdelphij{
460330571Sdelphij	int nlines;
461330571Sdelphij	POSITION pos = ch_zero();
462330571Sdelphij
463330571Sdelphij	for (nlines = 0;  nlines <= sc_height;  nlines++)
464330571Sdelphij	{
465330571Sdelphij		pos = forw_line(pos);
466330571Sdelphij		if (pos == NULL_POSITION) break;
467330571Sdelphij	}
468330571Sdelphij	return nlines;
469330571Sdelphij}
470