xref: /openbsd/usr.bin/less/forwback.c (revision fd84ef7e)
1 /*	$OpenBSD: forwback.c,v 1.4 2001/11/19 19:02:14 mpech Exp $	*/
2 
3 /*
4  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice in the documentation and/or other materials provided with
14  *    the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 
30 /*
31  * Primitives for displaying the file on the screen,
32  * scrolling either forward or backward.
33  */
34 
35 #include "less.h"
36 #include "position.h"
37 
38 public int hit_eof;	/* Keeps track of how many times we hit end of file */
39 public int screen_trashed;
40 public int squished;
41 
42 extern int sigs;
43 extern int top_scroll;
44 extern int quiet;
45 extern int sc_width, sc_height;
46 extern int quit_at_eof;
47 extern int less_mode;
48 extern int plusoption;
49 extern int forw_scroll;
50 extern int back_scroll;
51 extern int need_clr;
52 extern int ignore_eoi;
53 #if TAGS
54 extern char *tagoption;
55 #endif
56 
57 /*
58  * Sound the bell to indicate user is trying to move past end of file.
59  */
60 	static void
61 eof_bell()
62 {
63 	if (quiet == NOT_QUIET)
64 		bell();
65 	else
66 		vbell();
67 }
68 
69 /*
70  * Check to see if the end of file is currently "displayed".
71  */
72 	static void
73 eof_check()
74 {
75 	POSITION pos;
76 
77 	if (ignore_eoi)
78 		return;
79 	if (ABORT_SIGS())
80 		return;
81 	/*
82 	 * If the bottom line is empty, we are at EOF.
83 	 * If the bottom line ends at the file length,
84 	 * we must be just at EOF.
85 	 */
86 	pos = position(BOTTOM_PLUS_ONE);
87 	if (pos == NULL_POSITION || pos == ch_length())
88 		hit_eof++;
89 }
90 
91 /*
92  * If the screen is "squished", repaint it.
93  * "Squished" means the first displayed line is not at the top
94  * of the screen; this can happen when we display a short file
95  * for the first time.
96  */
97 	static void
98 squish_check()
99 {
100 	if (!squished)
101 		return;
102 	squished = 0;
103 	repaint();
104 }
105 
106 /*
107  * Display n lines, scrolling forward,
108  * starting at position pos in the input file.
109  * "force" means display the n lines even if we hit end of file.
110  * "only_last" means display only the last screenful if n > screen size.
111  * "nblank" is the number of blank lines to draw before the first
112  *   real line.  If nblank > 0, the pos must be NULL_POSITION.
113  *   The first real line after the blanks will start at ch_zero().
114  */
115 	public void
116 forw(n, pos, force, only_last, nblank)
117 	int n;
118 	POSITION pos;
119 	int force;
120 	int only_last;
121 	int nblank;
122 {
123 	int eof = 0;
124 	int nlines = 0;
125 	int do_repaint;
126 	static int first_time = 1;
127 
128 	squish_check();
129 
130 	/*
131 	 * do_repaint tells us not to display anything till the end,
132 	 * then just repaint the entire screen.
133 	 * We repaint if we are supposed to display only the last
134 	 * screenful and the request is for more than a screenful.
135 	 * Also if the request exceeds the forward scroll limit
136 	 * (but not if the request is for exactly a screenful, since
137 	 * repainting itself involves scrolling forward a screenful).
138 	 */
139 	do_repaint = (only_last && n > sc_height-1) ||
140 		(forw_scroll >= 0 && n > forw_scroll && n != sc_height-1);
141 
142 	if (!do_repaint)
143 	{
144 		if (top_scroll && n >= sc_height - 1 && pos != ch_length())
145 		{
146 			/*
147 			 * Start a new screen.
148 			 * {{ This is not really desirable if we happen
149 			 *    to hit eof in the middle of this screen,
150 			 *    but we don't yet know if that will happen. }}
151 			 */
152 			if (top_scroll == OPT_ONPLUS || first_time)
153 				clear();
154 			home();
155 			force = 1;
156 		} else
157 		{
158 			clear_bot();
159 		}
160 
161 		if (pos != position(BOTTOM_PLUS_ONE) || empty_screen())
162 		{
163 			/*
164 			 * This is not contiguous with what is
165 			 * currently displayed.  Clear the screen image
166 			 * (position table) and start a new screen.
167 			 */
168 			pos_clear();
169 			add_forw_pos(pos);
170 			force = 1;
171 			if (top_scroll)
172 			{
173 				if (top_scroll == OPT_ONPLUS)
174 					clear();
175 				home();
176 			} else if (!first_time)
177 			{
178 				putstr("...skipping...\n");
179 			}
180 		}
181 	}
182 
183 	while (--n >= 0)
184 	{
185 		/*
186 		 * Read the next line of input.
187 		 */
188 		if (nblank > 0)
189 		{
190 			/*
191 			 * Still drawing blanks; don't get a line
192 			 * from the file yet.
193 			 * If this is the last blank line, get ready to
194 			 * read a line starting at ch_zero() next time.
195 			 */
196 			if (--nblank == 0)
197 				pos = ch_zero();
198 		} else
199 		{
200 			/*
201 			 * Get the next line from the file.
202 			 */
203 			pos = forw_line(pos);
204 			if (pos == NULL_POSITION)
205 			{
206 				/*
207 				 * End of file: stop here unless the top line
208 				 * is still empty, or "force" is true.
209 				 */
210 				eof = 1;
211 				if (!force && position(TOP) != NULL_POSITION)
212 					break;
213 			}
214 		}
215 		/*
216 		 * Add the position of the next line to the position table.
217 		 * Display the current line on the screen.
218 		 */
219 		add_forw_pos(pos);
220 		nlines++;
221 		if (do_repaint)
222 			continue;
223 		/*
224 		 * If this is the first screen displayed and
225 		 * we hit an early EOF (i.e. before the requested
226 		 * number of lines), we "squish" the display down
227 		 * at the bottom of the screen.
228 		 * But don't do this if a + option or a -t option
229 		 * was given.  These options can cause us to
230 		 * start the display after the beginning of the file,
231 		 * and it is not appropriate to squish in that case.
232 		 */
233 		if (first_time && pos == NULL_POSITION && !top_scroll &&
234 #if TAGS
235 		    tagoption == NULL &&
236 #endif
237 		    !plusoption)
238 		{
239 			squished = 1;
240 			continue;
241 		}
242 		if (top_scroll == 1)
243 			clear_eol();
244 		put_line();
245 	}
246 
247 	if (ignore_eoi)
248 		hit_eof = 0;
249 	else if (eof && !ABORT_SIGS())
250 		hit_eof++;
251 	else
252 		eof_check();
253 	if (nlines == 0)
254 		eof_bell();
255 	else if (do_repaint)
256 		repaint();
257 	first_time = 0;
258 	(void) currline(BOTTOM);
259 }
260 
261 /*
262  * Display n lines, scrolling backward.
263  */
264 	public void
265 back(n, pos, force, only_last)
266 	int n;
267 	POSITION pos;
268 	int force;
269 	int only_last;
270 {
271 	int nlines = 0;
272 	int do_repaint;
273 
274 	squish_check();
275 	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
276 	hit_eof = 0;
277 	while (--n >= 0)
278 	{
279 		/*
280 		 * Get the previous line of input.
281 		 */
282 		pos = back_line(pos);
283 		if (pos == NULL_POSITION)
284 		{
285 			/*
286 			 * Beginning of file: stop here unless "force" is true.
287 			 */
288 			if (!force)
289 				break;
290 		}
291 		/*
292 		 * Add the position of the previous line to the position table.
293 		 * Display the line on the screen.
294 		 */
295 		add_back_pos(pos);
296 		nlines++;
297 		if (!do_repaint)
298 		{
299 			home();
300 			add_line();
301 			put_line();
302 		}
303 	}
304 
305 	eof_check();
306 	if (nlines == 0)
307 		eof_bell();
308 	else if (do_repaint)
309 		repaint();
310 	(void) currline(BOTTOM);
311 }
312 
313 /*
314  * Display n more lines, forward.
315  * Start just after the line currently displayed at the bottom of the screen.
316  */
317 	public void
318 forward(n, force, only_last)
319 	int n;
320 	int force;
321 	int only_last;
322 {
323 	POSITION pos;
324 
325 	if (quit_at_eof && hit_eof)
326 	{
327 		/*
328 		 * If the -e flag is set and we're trying to go
329 		 * forward from end-of-file, go on to the next file.
330 		 */
331 		if (edit_next(1))
332 			quit(QUIT_OK);
333 		return;
334 	}
335 
336 	pos = position(BOTTOM_PLUS_ONE);
337 	if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
338 	{
339 		if (ignore_eoi)
340 		{
341 			/*
342 			 * ignore_eoi is to support A_F_FOREVER.
343 			 * Back up until there is a line at the bottom
344 			 * of the screen.
345 			 */
346 			if (empty_screen())
347 				pos = ch_zero();
348 			else
349 			{
350 				do
351 				{
352 					back(1, position(TOP), 1, 0);
353 					pos = position(BOTTOM_PLUS_ONE);
354 				} while (pos == NULL_POSITION);
355 			}
356 		} else {
357 			eof_bell();
358 			hit_eof++;
359 			return;
360 		}
361 	}
362 	forw(n, pos, force, only_last, 0);
363 }
364 
365 /*
366  * Display n more lines, backward.
367  * Start just before the line currently displayed at the top of the screen.
368  */
369 	public void
370 backward(n, force, only_last)
371 	int n;
372 	int force;
373 	int only_last;
374 {
375 	POSITION pos;
376 
377 	pos = position(TOP);
378 	if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
379 	{
380 		eof_bell();
381 		return;
382 	}
383 	back(n, pos, force, only_last);
384 }
385 
386 /*
387  * Get the backwards scroll limit.
388  * Must call this function instead of just using the value of
389  * back_scroll, because the default case depends on sc_height and
390  * top_scroll, as well as back_scroll.
391  */
392 	public int
393 get_back_scroll()
394 {
395 	if (back_scroll >= 0)
396 		return (back_scroll);
397 	if (top_scroll)
398 		return (sc_height - 2);
399 	return (10000); /* infinity */
400 }
401