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