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