Bump to 608
[platform/upstream/less.git] / forwback.c
1 /*
2  * Copyright (C) 1984-2022  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 first_time = 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 hshift;
30 extern int auto_wrap;
31 extern int plusoption;
32 extern int forw_scroll;
33 extern int back_scroll;
34 extern int ignore_eoi;
35 extern int clear_bg;
36 extern int final_attr;
37 extern int header_lines;
38 extern int header_cols;
39 #if HILITE_SEARCH
40 extern int size_linebuf;
41 extern int hilite_search;
42 extern int status_col;
43 #endif
44 #if TAGS
45 extern char *tagoption;
46 #endif
47
48 /*
49  * Sound the bell to indicate user is trying to move past end of file.
50  */
51         static void
52 eof_bell(VOID_PARAM)
53 {
54 #if HAVE_TIME
55         static time_type last_eof_bell = 0;
56         time_type now = get_time();
57         if (now == last_eof_bell) /* max once per second */
58                 return;
59         last_eof_bell = now;
60 #endif
61         if (quiet == NOT_QUIET)
62                 bell();
63         else
64                 vbell();
65 }
66
67 /*
68  * Check to see if the end of file is currently displayed.
69  */
70         public int
71 eof_displayed(VOID_PARAM)
72 {
73         POSITION pos;
74
75         if (ignore_eoi)
76                 return (0);
77
78         if (ch_length() == NULL_POSITION)
79                 /*
80                  * If the file length is not known,
81                  * we can't possibly be displaying EOF.
82                  */
83                 return (0);
84
85         /*
86          * If the bottom line is empty, we are at EOF.
87          * If the bottom line ends at the file length,
88          * we must be just at EOF.
89          */
90         pos = position(BOTTOM_PLUS_ONE);
91         return (pos == NULL_POSITION || pos == ch_length());
92 }
93
94 /*
95  * Check to see if the entire file is currently displayed.
96  */
97         public int
98 entire_file_displayed(VOID_PARAM)
99 {
100         POSITION pos;
101
102         /* Make sure last line of file is displayed. */
103         if (!eof_displayed())
104                 return (0);
105
106         /* Make sure first line of file is displayed. */
107         pos = position(0);
108         return (pos == NULL_POSITION || pos == 0);
109 }
110
111 /*
112  * If the screen is "squished", repaint it.
113  * "Squished" means the first displayed line is not at the top
114  * of the screen; this can happen when we display a short file
115  * for the first time.
116  */
117         public void
118 squish_check(VOID_PARAM)
119 {
120         if (!squished)
121                 return;
122         squished = 0;
123         repaint();
124 }
125
126 /*
127  * Read the first pfx columns of the next line.
128  * If skipeol==0 stop there, otherwise read and discard chars to end of line.
129  */
130         static POSITION
131 forw_line_pfx(pos, pfx, skipeol)
132         POSITION pos;
133         int pfx;
134         int skipeol;
135 {
136         int save_sc_width = sc_width;
137         int save_auto_wrap = auto_wrap;
138         int save_hshift = hshift;
139         /* Set fake sc_width to force only pfx chars to be read. */
140         sc_width = pfx + line_pfx_width();
141         auto_wrap = 0;
142         hshift = 0;
143         pos = forw_line_seg(pos, skipeol, FALSE, FALSE);
144         sc_width = save_sc_width;
145         auto_wrap = save_auto_wrap;
146         hshift = save_hshift;
147         return pos;
148 }
149
150 /*
151  * Set header text color.
152  * Underline last line of headers, but not at beginning of file
153  * (where there is no gap between the last header line and the next line).
154  */
155         static void
156 set_attr_header(ln)
157         int ln;
158 {
159         set_attr_line(AT_COLOR_HEADER);
160         if (ln+1 == header_lines && position(0) != ch_zero())
161                 set_attr_line(AT_UNDERLINE);
162 }
163
164 /*
165  * Display file headers, overlaying text already drawn
166  * at top and left of screen.
167  */
168         public int
169 overlay_header(VOID_PARAM)
170 {
171         POSITION pos = ch_zero(); /* header lines are at beginning of file */
172         int ln;
173         int moved = FALSE;
174
175         if (header_lines > 0)
176         {
177                 /* Draw header_lines lines from start of file at top of screen. */
178                 home();
179                 for (ln = 0; ln < header_lines; ++ln)
180                 {
181                         pos = forw_line(pos);
182                         set_attr_header(ln);
183                         clear_eol();
184                         put_line();
185                 }
186                 moved = TRUE;
187         }
188         if (header_cols > 0)
189         {
190                 /* Draw header_cols columns at left of each line. */
191                 home();
192                 pos = ch_zero();
193                 for (ln = 0; ln < sc_height-1; ++ln)
194                 {
195                         if (ln >= header_lines) /* switch from header lines to normal lines */
196                                 pos = position(ln);
197                         if (pos == NULL_POSITION)
198                                 putchr('\n');
199                         else 
200                         {
201                                 /* Need skipeol for all header lines except the last one. */
202                                 pos = forw_line_pfx(pos, header_cols, ln+1 < header_lines);
203                                 set_attr_header(ln);
204                                 put_line();
205                         }
206                 }
207                 moved = TRUE;
208         }
209         if (moved)
210                 lower_left();
211         return moved;
212 }
213
214 /*
215  * Display n lines, scrolling forward, 
216  * starting at position pos in the input file.
217  * "force" means display the n lines even if we hit end of file.
218  * "only_last" means display only the last screenful if n > screen size.
219  * "nblank" is the number of blank lines to draw before the first
220  *   real line.  If nblank > 0, the pos must be NULL_POSITION.
221  *   The first real line after the blanks will start at ch_zero().
222  */
223         public void
224 forw(n, pos, force, only_last, nblank)
225         int n;
226         POSITION pos;
227         int force;
228         int only_last;
229         int nblank;
230 {
231         int nlines = 0;
232         int do_repaint;
233
234         squish_check();
235
236         /*
237          * do_repaint tells us not to display anything till the end, 
238          * then just repaint the entire screen.
239          * We repaint if we are supposed to display only the last 
240          * screenful and the request is for more than a screenful.
241          * Also if the request exceeds the forward scroll limit
242          * (but not if the request is for exactly a screenful, since
243          * repainting itself involves scrolling forward a screenful).
244          */
245         do_repaint = (only_last && n > sc_height-1) || 
246                 (forw_scroll >= 0 && n > forw_scroll && n != sc_height-1);
247
248 #if HILITE_SEARCH
249         if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) {
250                 prep_hilite(pos, pos + 4*size_linebuf, ignore_eoi ? 1 : -1);
251                 pos = next_unfiltered(pos);
252         }
253 #endif
254
255         if (!do_repaint)
256         {
257                 if (top_scroll && n >= sc_height - 1 && pos != ch_length())
258                 {
259                         /*
260                          * Start a new screen.
261                          * {{ This is not really desirable if we happen
262                          *    to hit eof in the middle of this screen,
263                          *    but we don't yet know if that will happen. }}
264                          */
265                         pos_clear();
266                         add_forw_pos(pos);
267                         force = 1;
268                         clear();
269                         home();
270                 }
271
272                 if (pos != position(BOTTOM_PLUS_ONE) || empty_screen())
273                 {
274                         /*
275                          * This is not contiguous with what is
276                          * currently displayed.  Clear the screen image 
277                          * (position table) and start a new screen.
278                          */
279                         pos_clear();
280                         add_forw_pos(pos);
281                         force = 1;
282                         if (top_scroll)
283                         {
284                                 clear();
285                                 home();
286                         } else if (!first_time && !is_filtering())
287                         {
288                                 putstr("...skipping...\n");
289                         }
290                 }
291         }
292
293         while (--n >= 0)
294         {
295                 /*
296                  * Read the next line of input.
297                  */
298                 if (nblank > 0)
299                 {
300                         /*
301                          * Still drawing blanks; don't get a line 
302                          * from the file yet.
303                          * If this is the last blank line, get ready to
304                          * read a line starting at ch_zero() next time.
305                          */
306                         if (--nblank == 0)
307                                 pos = ch_zero();
308                 } else
309                 {
310                         /* 
311                          * Get the next line from the file.
312                          */
313                         pos = forw_line(pos);
314 #if HILITE_SEARCH
315                         pos = next_unfiltered(pos);
316 #endif
317                         if (pos == NULL_POSITION)
318                         {
319                                 /*
320                                  * End of file: stop here unless the top line 
321                                  * is still empty, or "force" is true.
322                                  * Even if force is true, stop when the last
323                                  * line in the file reaches the top of screen.
324                                  */
325                                 if (!force && position(TOP) != NULL_POSITION)
326                                         break;
327                                 if (!empty_lines(0, 0) && 
328                                     !empty_lines(1, 1) &&
329                                      empty_lines(2, sc_height-1))
330                                         break;
331                         }
332                 }
333                 /*
334                  * Add the position of the next line to the position table.
335                  * Display the current line on the screen.
336                  */
337                 add_forw_pos(pos);
338                 nlines++;
339                 if (do_repaint)
340                         continue;
341                 /*
342                  * If this is the first screen displayed and
343                  * we hit an early EOF (i.e. before the requested
344                  * number of lines), we "squish" the display down
345                  * at the bottom of the screen.
346                  * But don't do this if a + option or a -t option
347                  * was given.  These options can cause us to
348                  * start the display after the beginning of the file,
349                  * and it is not appropriate to squish in that case.
350                  */
351                 if (first_time && pos == NULL_POSITION && !top_scroll && 
352 #if TAGS
353                     tagoption == NULL &&
354 #endif
355                     !plusoption)
356                 {
357                         squished = 1;
358                         continue;
359                 }
360                 put_line();
361 #if 0
362                 /* {{ 
363                  * Can't call clear_eol here.  The cursor might be at end of line
364                  * on an ignaw terminal, so clear_eol would clear the last char
365                  * of the current line instead of all of the next line.
366                  * If we really need to do this on clear_bg terminals, we need
367                  * to find a better way.
368                  * }}
369                  */
370                 if (clear_bg && apply_at_specials(final_attr) != AT_NORMAL)
371                 {
372                         /*
373                          * Writing the last character on the last line
374                          * of the display may have scrolled the screen.
375                          * If we were in standout mode, clear_bg terminals 
376                          * will fill the new line with the standout color.
377                          * Now we're in normal mode again, so clear the line.
378                          */
379                         clear_eol();
380                 }
381 #endif
382                 forw_prompt = 1;
383         }
384
385         if (header_lines > 0)
386         {
387                 /*
388                  * Don't allow ch_zero to appear on screen except at top of screen.
389                  * Otherwise duplicate header lines may be displayed.
390                  */
391                 if (onscreen(ch_zero()) > 0)
392                 {
393                         jump_loc(ch_zero(), 0); /* {{ yuck }} */
394                         return;
395                 }
396         }
397         if (nlines == 0 && !ignore_eoi)
398                 eof_bell();
399         else if (do_repaint)
400                 repaint();
401         else
402         {
403                 overlay_header();
404                 /* lower_left(); {{ considered harmful? }} */
405         }
406         first_time = 0;
407         (void) currline(BOTTOM);
408 }
409
410 /*
411  * Display n lines, scrolling backward.
412  */
413         public void
414 back(n, pos, force, only_last)
415         int n;
416         POSITION pos;
417         int force;
418         int only_last;
419 {
420         int nlines = 0;
421         int do_repaint;
422
423         squish_check();
424         do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1) || header_lines > 0);
425 #if HILITE_SEARCH
426         if (hilite_search == OPT_ONPLUS || is_filtering() || status_col) {
427                 prep_hilite((pos < 3*size_linebuf) ?  0 : pos - 3*size_linebuf, pos, -1);
428         }
429 #endif
430         while (--n >= 0)
431         {
432                 /*
433                  * Get the previous line of input.
434                  */
435 #if HILITE_SEARCH
436                 pos = prev_unfiltered(pos);
437 #endif
438
439                 pos = back_line(pos);
440                 if (pos == NULL_POSITION)
441                 {
442                         /*
443                          * Beginning of file: stop here unless "force" is true.
444                          */
445                         if (!force)
446                                 break;
447                 }
448                 /*
449                  * Add the position of the previous line to the position table.
450                  * Display the line on the screen.
451                  */
452                 add_back_pos(pos);
453                 nlines++;
454                 if (!do_repaint)
455                 {
456                         home();
457                         add_line();
458                         put_line();
459                 }
460         }
461         if (nlines == 0)
462                 eof_bell();
463         else if (do_repaint)
464                 repaint();
465         else
466         {
467                 overlay_header();
468                 lower_left();
469         }
470         (void) currline(BOTTOM);
471 }
472
473 /*
474  * Display n more lines, forward.
475  * Start just after the line currently displayed at the bottom of the screen.
476  */
477         public void
478 forward(n, force, only_last)
479         int n;
480         int force;
481         int only_last;
482 {
483         POSITION pos;
484
485         if (get_quit_at_eof() && eof_displayed() && !(ch_getflags() & CH_HELPFILE))
486         {
487                 /*
488                  * If the -e flag is set and we're trying to go
489                  * forward from end-of-file, go on to the next file.
490                  */
491                 if (edit_next(1))
492                         quit(QUIT_OK);
493                 return;
494         }
495
496         pos = position(BOTTOM_PLUS_ONE);
497         if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1)))
498         {
499                 if (ignore_eoi)
500                 {
501                         /*
502                          * ignore_eoi is to support A_F_FOREVER.
503                          * Back up until there is a line at the bottom
504                          * of the screen.
505                          */
506                         if (empty_screen())
507                                 pos = ch_zero();
508                         else
509                         {
510                                 do
511                                 {
512                                         back(1, position(TOP), 1, 0);
513                                         pos = position(BOTTOM_PLUS_ONE);
514                                 } while (pos == NULL_POSITION);
515                         }
516                 } else
517                 {
518                         eof_bell();
519                         return;
520                 }
521         }
522         forw(n, pos, force, only_last, 0);
523 }
524
525 /*
526  * Display n more lines, backward.
527  * Start just before the line currently displayed at the top of the screen.
528  */
529         public void
530 backward(n, force, only_last)
531         int n;
532         int force;
533         int only_last;
534 {
535         POSITION pos;
536
537         pos = position(TOP);
538         if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0))
539         {
540                 eof_bell();
541                 return;   
542         }
543         back(n, pos, force, only_last);
544 }
545
546 /*
547  * Get the backwards scroll limit.
548  * Must call this function instead of just using the value of
549  * back_scroll, because the default case depends on sc_height and
550  * top_scroll, as well as back_scroll.
551  */
552         public int
553 get_back_scroll(VOID_PARAM)
554 {
555         if (no_back_scroll)
556                 return (0);
557         if (back_scroll >= 0)
558                 return (back_scroll);
559         if (top_scroll)
560                 return (sc_height - 2);
561         return (10000); /* infinity */
562 }
563
564 /*
565  * Will the entire file fit on one screen?
566  */
567         public int
568 get_one_screen(VOID_PARAM)
569 {
570         int nlines;
571         POSITION pos = ch_zero();
572
573         for (nlines = 0;  nlines < sc_height;  nlines++)
574         {
575                 pos = forw_line(pos);
576                 if (pos == NULL_POSITION) break;
577         }
578         return (nlines < sc_height);
579 }