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