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