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