xref: /freebsd/contrib/less/forwback.c (revision 8ab2f5ecc596131f6ca790d6ae35540c06ed7985)
1 /* $FreeBSD$ */
2 /*
3  * Copyright (C) 1984-2002  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 		/*
130 		 * Forget any current line shift we might have
131 		 * (from the last line of the previous screenful).
132 		 */
133 		extern int cshift;
134 		cshift = 0;
135 
136 		if (top_scroll && n >= sc_height - 1 && pos != ch_length())
137 		{
138 			/*
139 			 * Start a new screen.
140 			 * {{ This is not really desirable if we happen
141 			 *    to hit eof in the middle of this screen,
142 			 *    but we don't yet know if that will happen. }}
143 			 */
144 			pos_clear();
145 			add_forw_pos(pos);
146 			force = 1;
147 			if (more_mode == 0)
148 			{
149 				if (top_scroll == OPT_ONPLUS || first_time)
150 					clear();
151 				home();
152 			}
153 		} else
154 		{
155 			clear_bot();
156 		}
157 
158 		if (pos != position(BOTTOM_PLUS_ONE) || empty_screen())
159 		{
160 			/*
161 			 * This is not contiguous with what is
162 			 * currently displayed.  Clear the screen image
163 			 * (position table) and start a new screen.
164 			 */
165 			pos_clear();
166 			add_forw_pos(pos);
167 			force = 1;
168 			if (top_scroll)
169 			{
170 				if (top_scroll == OPT_ONPLUS)
171 					clear();
172 				home();
173 			} else if (!first_time)
174 			{
175 				putstr("...skipping...\n");
176 			}
177 		}
178 	}
179 
180 	while (--n >= 0)
181 	{
182 		/*
183 		 * Read the next line of input.
184 		 */
185 		if (nblank > 0)
186 		{
187 			/*
188 			 * Still drawing blanks; don't get a line
189 			 * from the file yet.
190 			 * If this is the last blank line, get ready to
191 			 * read a line starting at ch_zero() next time.
192 			 */
193 			if (--nblank == 0)
194 				pos = ch_zero();
195 		} else
196 		{
197 			/*
198 			 * Get the next line from the file.
199 			 */
200 			pos = forw_line(pos);
201 			if (pos == NULL_POSITION)
202 			{
203 				/*
204 				 * End of file: stop here unless the top line
205 				 * is still empty, or "force" is true.
206 				 * Even if force is true, stop when the last
207 				 * line in the file reaches the top of screen.
208 				 */
209 				eof = 1;
210 				if (!force && position(TOP) != NULL_POSITION)
211 					break;
212 				if (!empty_lines(0, 0) &&
213 				    !empty_lines(1, 1) &&
214 				     empty_lines(2, sc_height-1))
215 					break;
216 			}
217 		}
218 		/*
219 		 * Add the position of the next line to the position table.
220 		 * Display the current line on the screen.
221 		 */
222 		add_forw_pos(pos);
223 		nlines++;
224 		if (do_repaint)
225 			continue;
226 		/*
227 		 * If this is the first screen displayed and
228 		 * we hit an early EOF (i.e. before the requested
229 		 * number of lines), we "squish" the display down
230 		 * at the bottom of the screen.
231 		 * But don't do this if a + option or a -t option
232 		 * was given.  These options can cause us to
233 		 * start the display after the beginning of the file,
234 		 * and it is not appropriate to squish in that case.
235 		 */
236 		if ((first_time || more_mode) &&
237 		    pos == NULL_POSITION && !top_scroll &&
238 #if TAGS
239 		    tagoption == NULL &&
240 #endif
241 		    !plusoption)
242 		{
243 			squished = 1;
244 			continue;
245 		}
246 		if (top_scroll == OPT_ON)
247 			clear_eol();
248 		put_line();
249 		if (clear_bg && final_attr != AT_NORMAL)
250 		{
251 			/*
252 			 * Writing the last character on the last line
253 			 * of the display may have scrolled the screen.
254 			 * If we were in standout mode, clear_bg terminals
255 			 * will fill the new line with the standout color.
256 			 * Now we're in normal mode again, so clear the line.
257 			 */
258 			clear_eol();
259 		}
260 	}
261 
262 	if (ignore_eoi)
263 		hit_eof = 0;
264 	else if (eof && !ABORT_SIGS())
265 		hit_eof++;
266 	else
267 		eof_check();
268 	if (nlines == 0)
269 		eof_bell();
270 	else if (do_repaint)
271 		repaint();
272 	first_time = 0;
273 	(void) currline(BOTTOM);
274 }
275 
276 /*
277  * Display n lines, scrolling backward.
278  */
279 	public void
280 back(n, pos, force, only_last)
281 	register int n;
282 	POSITION pos;
283 	int force;
284 	int only_last;
285 {
286 	int nlines = 0;
287 	int do_repaint;
288 
289 	squish_check();
290 	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
291 	hit_eof = 0;
292 	while (--n >= 0)
293 	{
294 		/*
295 		 * Get the previous line of input.
296 		 */
297 		pos = back_line(pos);
298 		if (pos == NULL_POSITION)
299 		{
300 			/*
301 			 * Beginning of file: stop here unless "force" is true.
302 			 */
303 			if (!force)
304 				break;
305 		}
306 		/*
307 		 * Add the position of the previous line to the position table.
308 		 * Display the line on the screen.
309 		 */
310 		add_back_pos(pos);
311 		nlines++;
312 		if (!do_repaint)
313 		{
314 			home();
315 			add_line();
316 			put_line();
317 		}
318 	}
319 
320 	eof_check();
321 	if (nlines == 0)
322 		eof_bell();
323 	else if (do_repaint)
324 		repaint();
325 	(void) currline(BOTTOM);
326 }
327 
328 /*
329  * Display n more lines, forward.
330  * Start just after the line currently displayed at the bottom of the screen.
331  */
332 	public void
333 forward(n, force, only_last)
334 	int n;
335 	int force;
336 	int only_last;
337 {
338 	POSITION pos;
339 
340 	if (quit_at_eof && hit_eof && !(ch_getflags() & CH_HELPFILE))
341 	{
342 		/*
343 		 * If the -e flag is set and we're trying to go
344 		 * forward from end-of-file, go on to the next file.
345 		 */
346 		if (edit_next(1))
347 			quit(QUIT_OK);
348 		return;
349 	}
350 
351 	pos = position(BOTTOM_PLUS_ONE);
352 	if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
353 	{
354 		if (ignore_eoi)
355 		{
356 			/*
357 			 * ignore_eoi is to support A_F_FOREVER.
358 			 * Back up until there is a line at the bottom
359 			 * of the screen.
360 			 */
361 			if (empty_screen())
362 				pos = ch_zero();
363 			else
364 			{
365 				do
366 				{
367 					back(1, position(TOP), 1, 0);
368 					pos = position(BOTTOM_PLUS_ONE);
369 				} while (pos == NULL_POSITION);
370 			}
371 		} else
372 		{
373 			eof_bell();
374 			hit_eof++;
375 			return;
376 		}
377 	}
378 	forw(n, pos, force, only_last, 0);
379 }
380 
381 /*
382  * Display n more lines, backward.
383  * Start just before the line currently displayed at the top of the screen.
384  */
385 	public void
386 backward(n, force, only_last)
387 	int n;
388 	int force;
389 	int only_last;
390 {
391 	POSITION pos;
392 
393 	pos = position(TOP);
394 	if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
395 	{
396 		eof_bell();
397 		return;
398 	}
399 	back(n, pos, force, only_last);
400 }
401 
402 /*
403  * Get the backwards scroll limit.
404  * Must call this function instead of just using the value of
405  * back_scroll, because the default case depends on sc_height and
406  * top_scroll, as well as back_scroll.
407  */
408 	public int
409 get_back_scroll()
410 {
411 	if (no_back_scroll)
412 		return (0);
413 	if (back_scroll >= 0)
414 		return (back_scroll);
415 	if (top_scroll)
416 		return (sc_height - 2);
417 	return (10000); /* infinity */
418 }
419