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