]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/less/forwback.c
Merge branch 'releng/12.3' into releng-CDN/12.3
[FreeBSD/FreeBSD.git] / contrib / less / forwback.c
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
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
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
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
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
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
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
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
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
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
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 }