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