forwback.c revision 267654
1/* $FreeBSD: releng/9.3/contrib/less/forwback.c 240121 2012-09-04 23:24:00Z delphij $ */
2/*
3 * Copyright (C) 1984-2012  Mark Nudelman
4 *
5 * You may distribute under the terms of either the GNU General Public
6 * License or the Less License, as specified in the README file.
7 *
8 * For more information, see the README file.
9 */
10
11
12/*
13 * Primitives for displaying the file on the screen,
14 * scrolling either forward or backward.
15 */
16
17#include "less.h"
18#include "position.h"
19
20public int screen_trashed;
21public int squished;
22public int no_back_scroll = 0;
23public int forw_prompt;
24
25extern int sigs;
26extern int top_scroll;
27extern int quiet;
28extern int sc_width, sc_height;
29extern int less_is_more;
30extern int plusoption;
31extern int forw_scroll;
32extern int back_scroll;
33extern int ignore_eoi;
34extern int clear_bg;
35extern int final_attr;
36extern int oldbot;
37#if TAGS
38extern char *tagoption;
39#endif
40
41/*
42 * Sound the bell to indicate user is trying to move past end of file.
43 */
44	static void
45eof_bell()
46{
47	if (quiet == NOT_QUIET)
48		bell();
49	else
50		vbell();
51}
52
53/*
54 * Check to see if the end of file is currently displayed.
55 */
56	public int
57eof_displayed()
58{
59	POSITION pos;
60
61	if (ignore_eoi)
62		return (0);
63
64	if (ch_length() == NULL_POSITION)
65		/*
66		 * If the file length is not known,
67		 * we can't possibly be displaying EOF.
68		 */
69		return (0);
70
71	/*
72	 * If the bottom line is empty, we are at EOF.
73	 * If the bottom line ends at the file length,
74	 * we must be just at EOF.
75	 */
76	pos = position(BOTTOM_PLUS_ONE);
77	return (pos == NULL_POSITION || pos == ch_length());
78}
79
80/*
81 * Check to see if the entire file is currently displayed.
82 */
83	public int
84entire_file_displayed()
85{
86	POSITION pos;
87
88	/* Make sure last line of file is displayed. */
89	if (!eof_displayed())
90		return (0);
91
92	/* Make sure first line of file is displayed. */
93	pos = position(0);
94	return (pos == NULL_POSITION || pos == 0);
95}
96
97/*
98 * If the screen is "squished", repaint it.
99 * "Squished" means the first displayed line is not at the top
100 * of the screen; this can happen when we display a short file
101 * for the first time.
102 */
103	public void
104squish_check()
105{
106	if (!squished)
107		return;
108	squished = 0;
109	repaint();
110}
111
112/*
113 * Display n lines, scrolling forward,
114 * starting at position pos in the input file.
115 * "force" means display the n lines even if we hit end of file.
116 * "only_last" means display only the last screenful if n > screen size.
117 * "nblank" is the number of blank lines to draw before the first
118 *   real line.  If nblank > 0, the pos must be NULL_POSITION.
119 *   The first real line after the blanks will start at ch_zero().
120 */
121	public void
122forw(n, pos, force, only_last, nblank)
123	register int n;
124	POSITION pos;
125	int force;
126	int only_last;
127	int nblank;
128{
129	int eof = 0;
130	int nlines = 0;
131	int do_repaint;
132	static int first_time = 1;
133
134	squish_check();
135
136	/*
137	 * do_repaint tells us not to display anything till the end,
138	 * then just repaint the entire screen.
139	 * We repaint if we are supposed to display only the last
140	 * screenful and the request is for more than a screenful.
141	 * Also if the request exceeds the forward scroll limit
142	 * (but not if the request is for exactly a screenful, since
143	 * repainting itself involves scrolling forward a screenful).
144	 */
145	do_repaint = (only_last && n > sc_height-1) ||
146		(forw_scroll >= 0 && n > forw_scroll && n != sc_height-1);
147
148	if (!do_repaint)
149	{
150		if (top_scroll && n >= sc_height - 1 && pos != ch_length())
151		{
152			/*
153			 * Start a new screen.
154			 * {{ This is not really desirable if we happen
155			 *    to hit eof in the middle of this screen,
156			 *    but we don't yet know if that will happen. }}
157			 */
158			pos_clear();
159			add_forw_pos(pos);
160			force = 1;
161			if (less_is_more == 0) {
162				clear();
163				home();
164			}
165		}
166
167		if (pos != position(BOTTOM_PLUS_ONE) || empty_screen())
168		{
169			/*
170			 * This is not contiguous with what is
171			 * currently displayed.  Clear the screen image
172			 * (position table) and start a new screen.
173			 */
174			pos_clear();
175			add_forw_pos(pos);
176			force = 1;
177			if (top_scroll)
178			{
179				clear();
180				home();
181			} else if (!first_time)
182			{
183				putstr("...skipping...\n");
184			}
185		}
186	}
187
188	while (--n >= 0)
189	{
190		/*
191		 * Read the next line of input.
192		 */
193		if (nblank > 0)
194		{
195			/*
196			 * Still drawing blanks; don't get a line
197			 * from the file yet.
198			 * If this is the last blank line, get ready to
199			 * read a line starting at ch_zero() next time.
200			 */
201			if (--nblank == 0)
202				pos = ch_zero();
203		} else
204		{
205			/*
206			 * Get the next line from the file.
207			 */
208			pos = forw_line(pos);
209			if (pos == NULL_POSITION)
210			{
211				/*
212				 * End of file: stop here unless the top line
213				 * is still empty, or "force" is true.
214				 * Even if force is true, stop when the last
215				 * line in the file reaches the top of screen.
216				 */
217				eof = 1;
218				if (!force && position(TOP) != NULL_POSITION)
219					break;
220				if (!empty_lines(0, 0) &&
221				    !empty_lines(1, 1) &&
222				     empty_lines(2, sc_height-1))
223					break;
224			}
225		}
226		/*
227		 * Add the position of the next line to the position table.
228		 * Display the current line on the screen.
229		 */
230		add_forw_pos(pos);
231		nlines++;
232		if (do_repaint)
233			continue;
234		/*
235		 * If this is the first screen displayed and
236		 * we hit an early EOF (i.e. before the requested
237		 * number of lines), we "squish" the display down
238		 * at the bottom of the screen.
239		 * But don't do this if a + option or a -t option
240		 * was given.  These options can cause us to
241		 * start the display after the beginning of the file,
242		 * and it is not appropriate to squish in that case.
243		 */
244		if ((first_time || less_is_more) &&
245		    pos == NULL_POSITION && !top_scroll &&
246#if TAGS
247		    tagoption == NULL &&
248#endif
249		    !plusoption)
250		{
251			squished = 1;
252			continue;
253		}
254		put_line();
255#if 0
256		/* {{
257		 * Can't call clear_eol here.  The cursor might be at end of line
258		 * on an ignaw terminal, so clear_eol would clear the last char
259		 * of the current line instead of all of the next line.
260		 * If we really need to do this on clear_bg terminals, we need
261		 * to find a better way.
262		 * }}
263		 */
264		if (clear_bg && apply_at_specials(final_attr) != AT_NORMAL)
265		{
266			/*
267			 * Writing the last character on the last line
268			 * of the display may have scrolled the screen.
269			 * If we were in standout mode, clear_bg terminals
270			 * will fill the new line with the standout color.
271			 * Now we're in normal mode again, so clear the line.
272			 */
273			clear_eol();
274		}
275#endif
276		forw_prompt = 1;
277	}
278
279	if (nlines == 0)
280		eof_bell();
281	else if (do_repaint)
282		repaint();
283	first_time = 0;
284	(void) currline(BOTTOM);
285}
286
287/*
288 * Display n lines, scrolling backward.
289 */
290	public void
291back(n, pos, force, only_last)
292	register int n;
293	POSITION pos;
294	int force;
295	int only_last;
296{
297	int nlines = 0;
298	int do_repaint;
299
300	squish_check();
301	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
302	while (--n >= 0)
303	{
304		/*
305		 * Get the previous line of input.
306		 */
307		pos = back_line(pos);
308		if (pos == NULL_POSITION)
309		{
310			/*
311			 * Beginning of file: stop here unless "force" is true.
312			 */
313			if (!force)
314				break;
315		}
316		/*
317		 * Add the position of the previous line to the position table.
318		 * Display the line on the screen.
319		 */
320		add_back_pos(pos);
321		nlines++;
322		if (!do_repaint)
323		{
324			home();
325			add_line();
326			put_line();
327		}
328	}
329
330	if (nlines == 0)
331		eof_bell();
332	else if (do_repaint)
333		repaint();
334	else if (!oldbot)
335		lower_left();
336	(void) currline(BOTTOM);
337}
338
339/*
340 * Display n more lines, forward.
341 * Start just after the line currently displayed at the bottom of the screen.
342 */
343	public void
344forward(n, force, only_last)
345	int n;
346	int force;
347	int only_last;
348{
349	POSITION pos;
350
351	if (get_quit_at_eof() && eof_displayed() && !(ch_getflags() & CH_HELPFILE))
352	{
353		/*
354		 * If the -e flag is set and we're trying to go
355		 * forward from end-of-file, go on to the next file.
356		 */
357		if (edit_next(1))
358			quit(QUIT_OK);
359		return;
360	}
361
362	pos = position(BOTTOM_PLUS_ONE);
363	if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
364	{
365		if (ignore_eoi)
366		{
367			/*
368			 * ignore_eoi is to support A_F_FOREVER.
369			 * Back up until there is a line at the bottom
370			 * of the screen.
371			 */
372			if (empty_screen())
373				pos = ch_zero();
374			else
375			{
376				do
377				{
378					back(1, position(TOP), 1, 0);
379					pos = position(BOTTOM_PLUS_ONE);
380				} while (pos == NULL_POSITION);
381			}
382		} else
383		{
384			eof_bell();
385			return;
386		}
387	}
388	forw(n, pos, force, only_last, 0);
389}
390
391/*
392 * Display n more lines, backward.
393 * Start just before the line currently displayed at the top of the screen.
394 */
395	public void
396backward(n, force, only_last)
397	int n;
398	int force;
399	int only_last;
400{
401	POSITION pos;
402
403	pos = position(TOP);
404	if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
405	{
406		eof_bell();
407		return;
408	}
409	back(n, pos, force, only_last);
410}
411
412/*
413 * Get the backwards scroll limit.
414 * Must call this function instead of just using the value of
415 * back_scroll, because the default case depends on sc_height and
416 * top_scroll, as well as back_scroll.
417 */
418	public int
419get_back_scroll()
420{
421	if (no_back_scroll)
422		return (0);
423	if (back_scroll >= 0)
424		return (back_scroll);
425	if (top_scroll)
426		return (sc_height - 2);
427	return (10000); /* infinity */
428}
429