xref: /freebsd/contrib/less/forwback.c (revision ab2043b81eaba0d7d7769b4a58b2b6d17bc464a3)
1 /* $FreeBSD$ */
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 
20 public int screen_trashed;
21 public int squished;
22 public int no_back_scroll = 0;
23 public int forw_prompt;
24 
25 extern int sigs;
26 extern int top_scroll;
27 extern int quiet;
28 extern int sc_width, sc_height;
29 extern int less_is_more;
30 extern int plusoption;
31 extern int forw_scroll;
32 extern int back_scroll;
33 extern int ignore_eoi;
34 extern int clear_bg;
35 extern int final_attr;
36 extern int oldbot;
37 #if TAGS
38 extern 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
45 eof_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
57 eof_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
84 entire_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
104 squish_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
122 forw(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
291 back(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
344 forward(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
396 backward(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
419 get_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