xref: /netbsd-src/external/bsd/less/dist/forwback.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: forwback.c,v 1.3 2011/07/03 20:14:12 tron Exp $	*/
2 
3 /*
4  * Copyright (C) 1984-2011  Mark Nudelman
5  *
6  * You may distribute under the terms of either the GNU General Public
7  * License or the Less License, as specified in the README file.
8  *
9  * For more information about less, or for information on how to
10  * contact the author, see the README file.
11  */
12 
13 
14 /*
15  * Primitives for displaying the file on the screen,
16  * scrolling either forward or backward.
17  */
18 
19 #include "less.h"
20 #include "position.h"
21 
22 public int screen_trashed;
23 public int squished;
24 public int no_back_scroll = 0;
25 public int forw_prompt;
26 
27 extern int sigs;
28 extern int top_scroll;
29 extern int quiet;
30 extern int sc_width, sc_height;
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 extern int oldbot;
39 #if TAGS
40 extern char *tagoption;
41 #endif
42 
43 static void eof_bell __P((void));
44 static void eof_check __P((void));
45 
46 /*
47  * Sound the bell to indicate user is trying to move past end of file.
48  */
49 	static void
50 eof_bell()
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()
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()
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()
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 	register int n;
129 	POSITION pos;
130 	int force;
131 	int only_last;
132 	int nblank;
133 {
134 	int eof = 0;
135 	int nlines = 0;
136 	int do_repaint;
137 	static int first_time = 1;
138 
139 	squish_check();
140 
141 	/*
142 	 * do_repaint tells us not to display anything till the end,
143 	 * then just repaint the entire screen.
144 	 * We repaint if we are supposed to display only the last
145 	 * screenful and the request is for more than a screenful.
146 	 * Also if the request exceeds the forward scroll limit
147 	 * (but not if the request is for exactly a screenful, since
148 	 * repainting itself involves scrolling forward a screenful).
149 	 */
150 	do_repaint = (only_last && n > sc_height-1) ||
151 		(forw_scroll >= 0 && n > forw_scroll && n != sc_height-1);
152 
153 	if (!do_repaint)
154 	{
155 		if (top_scroll && n >= sc_height - 1 && pos != ch_length())
156 		{
157 			/*
158 			 * Start a new screen.
159 			 * {{ This is not really desirable if we happen
160 			 *    to hit eof in the middle of this screen,
161 			 *    but we don't yet know if that will happen. }}
162 			 */
163 			pos_clear();
164 			add_forw_pos(pos);
165 			force = 1;
166 			clear();
167 			home();
168 		}
169 
170 		if (pos != position(BOTTOM_PLUS_ONE) || empty_screen())
171 		{
172 			/*
173 			 * This is not contiguous with what is
174 			 * currently displayed.  Clear the screen image
175 			 * (position table) and start a new screen.
176 			 */
177 			pos_clear();
178 			add_forw_pos(pos);
179 			force = 1;
180 			if (top_scroll)
181 			{
182 				clear();
183 				home();
184 			} else if (!first_time)
185 			{
186 				putstr("...skipping...\n");
187 			}
188 		}
189 	}
190 
191 	while (--n >= 0)
192 	{
193 		/*
194 		 * Read the next line of input.
195 		 */
196 		if (nblank > 0)
197 		{
198 			/*
199 			 * Still drawing blanks; don't get a line
200 			 * from the file yet.
201 			 * If this is the last blank line, get ready to
202 			 * read a line starting at ch_zero() next time.
203 			 */
204 			if (--nblank == 0)
205 				pos = ch_zero();
206 		} else
207 		{
208 			/*
209 			 * Get the next line from the file.
210 			 */
211 			pos = forw_line(pos);
212 			if (pos == NULL_POSITION)
213 			{
214 				/*
215 				 * End of file: stop here unless the top line
216 				 * is still empty, or "force" is true.
217 				 * Even if force is true, stop when the last
218 				 * line in the file reaches the top of screen.
219 				 */
220 				eof = 1;
221 				if (!force && position(TOP) != NULL_POSITION)
222 					break;
223 				if (!empty_lines(0, 0) &&
224 				    !empty_lines(1, 1) &&
225 				     empty_lines(2, sc_height-1))
226 					break;
227 			}
228 		}
229 		/*
230 		 * Add the position of the next line to the position table.
231 		 * Display the current line on the screen.
232 		 */
233 		add_forw_pos(pos);
234 		nlines++;
235 		if (do_repaint)
236 			continue;
237 		/*
238 		 * If this is the first screen displayed and
239 		 * we hit an early EOF (i.e. before the requested
240 		 * number of lines), we "squish" the display down
241 		 * at the bottom of the screen.
242 		 * But don't do this if a + option or a -t option
243 		 * was given.  These options can cause us to
244 		 * start the display after the beginning of the file,
245 		 * and it is not appropriate to squish in that case.
246 		 */
247 		if ((first_time || more_mode) &&
248 		    pos == NULL_POSITION && !top_scroll &&
249 #if TAGS
250 		    tagoption == NULL &&
251 #endif
252 		    !plusoption)
253 		{
254 			squished = 1;
255 			continue;
256 		}
257 		put_line();
258 #if 0
259 		/* {{
260 		 * Can't call clear_eol here.  The cursor might be at end of line
261 		 * on an ignaw terminal, so clear_eol would clear the last char
262 		 * of the current line instead of all of the next line.
263 		 * If we really need to do this on clear_bg terminals, we need
264 		 * to find a better way.
265 		 * }}
266 		 */
267 		if (clear_bg && apply_at_specials(final_attr) != AT_NORMAL)
268 		{
269 			/*
270 			 * Writing the last character on the last line
271 			 * of the display may have scrolled the screen.
272 			 * If we were in standout mode, clear_bg terminals
273 			 * will fill the new line with the standout color.
274 			 * Now we're in normal mode again, so clear the line.
275 			 */
276 			clear_eol();
277 		}
278 #endif
279 		forw_prompt = 1;
280 	}
281 
282 	if (nlines == 0)
283 		eof_bell();
284 	else if (do_repaint)
285 		repaint();
286 	first_time = 0;
287 	(void) currline(BOTTOM);
288 }
289 
290 /*
291  * Display n lines, scrolling backward.
292  */
293 	public void
294 back(n, pos, force, only_last)
295 	register int n;
296 	POSITION pos;
297 	int force;
298 	int only_last;
299 {
300 	int nlines = 0;
301 	int do_repaint;
302 
303 	squish_check();
304 	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
305 	while (--n >= 0)
306 	{
307 		/*
308 		 * Get the previous line of input.
309 		 */
310 		pos = back_line(pos);
311 		if (pos == NULL_POSITION)
312 		{
313 			/*
314 			 * Beginning of file: stop here unless "force" is true.
315 			 */
316 			if (!force)
317 				break;
318 		}
319 		/*
320 		 * Add the position of the previous line to the position table.
321 		 * Display the line on the screen.
322 		 */
323 		add_back_pos(pos);
324 		nlines++;
325 		if (!do_repaint)
326 		{
327 			home();
328 			add_line();
329 			put_line();
330 		}
331 	}
332 
333 	if (nlines == 0)
334 		eof_bell();
335 	else if (do_repaint)
336 		repaint();
337 	else if (!oldbot)
338 		lower_left();
339 	(void) currline(BOTTOM);
340 }
341 
342 /*
343  * Display n more lines, forward.
344  * Start just after the line currently displayed at the bottom of the screen.
345  */
346 	public void
347 forward(n, force, only_last)
348 	int n;
349 	int force;
350 	int only_last;
351 {
352 	POSITION pos;
353 
354 	if (get_quit_at_eof() && eof_displayed() && !(ch_getflags() & CH_HELPFILE))
355 	{
356 		/*
357 		 * If the -e flag is set and we're trying to go
358 		 * forward from end-of-file, go on to the next file.
359 		 */
360 		if (edit_next(1))
361 			quit(QUIT_OK);
362 		return;
363 	}
364 
365 	pos = position(BOTTOM_PLUS_ONE);
366 	if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
367 	{
368 		if (ignore_eoi)
369 		{
370 			/*
371 			 * ignore_eoi is to support A_F_FOREVER.
372 			 * Back up until there is a line at the bottom
373 			 * of the screen.
374 			 */
375 			if (empty_screen())
376 				pos = ch_zero();
377 			else
378 			{
379 				do
380 				{
381 					back(1, position(TOP), 1, 0);
382 					pos = position(BOTTOM_PLUS_ONE);
383 				} while (pos == NULL_POSITION);
384 			}
385 		} else
386 		{
387 			eof_bell();
388 			return;
389 		}
390 	}
391 	forw(n, pos, force, only_last, 0);
392 }
393 
394 /*
395  * Display n more lines, backward.
396  * Start just before the line currently displayed at the top of the screen.
397  */
398 	public void
399 backward(n, force, only_last)
400 	int n;
401 	int force;
402 	int only_last;
403 {
404 	POSITION pos;
405 
406 	pos = position(TOP);
407 	if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
408 	{
409 		eof_bell();
410 		return;
411 	}
412 	back(n, pos, force, only_last);
413 }
414 
415 /*
416  * Get the backwards scroll limit.
417  * Must call this function instead of just using the value of
418  * back_scroll, because the default case depends on sc_height and
419  * top_scroll, as well as back_scroll.
420  */
421 	public int
422 get_back_scroll()
423 {
424 	if (no_back_scroll)
425 		return (0);
426 	if (back_scroll >= 0)
427 		return (back_scroll);
428 	if (top_scroll)
429 		return (sc_height - 2);
430 	return (10000); /* infinity */
431 }
432