2 * Copyright (C) 1984-2023 Mark Nudelman
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.
7 * For more information, see the README file.
11 * High level routines dealing with getting lines of input
12 * from the file being viewed.
14 * When we speak of "lines" here, we mean PRINTABLE lines;
15 * lines processed with respect to the screen width.
16 * We use the term "raw line" to refer to lines simply
17 * delimited by newlines; not processed with respect to screen width.
24 extern int quit_if_one_screen
;
26 extern int ignore_eoi
;
27 extern int status_col
;
29 extern POSITION start_attnpos
;
30 extern POSITION end_attnpos
;
32 extern int hilite_search
;
33 extern int size_linebuf
;
38 * Set the status column.
39 * base Position of first char in line.
40 * disp First visible char.
41 * Different than base_pos if line is shifted.
42 * edisp Last visible char.
43 * eol End of line. Normally the newline.
44 * Different than edisp if line is chopped.
46 static void init_status_col(POSITION base_pos
, POSITION disp_pos
, POSITION edisp_pos
, POSITION eol_pos
)
48 int hl_before
= (chop_line() && disp_pos
!= NULL_POSITION
) ?
49 is_hilited_attr(base_pos
, disp_pos
, TRUE
, NULL
) : 0;
50 int hl_after
= (chop_line()) ?
51 is_hilited_attr(edisp_pos
, eol_pos
, TRUE
, NULL
) : 0;
55 if (hl_before
&& hl_after
)
69 attr
= is_hilited_attr(base_pos
, eol_pos
, TRUE
, NULL
);
73 set_status_col(ch
, attr
);
78 * A "current" position is passed and a "new" position is returned.
79 * The current position is the position of the first character of
80 * a line. The new position is the position of the first character
81 * of the NEXT line. The line obtained is the line starting at curr_pos.
83 public POSITION
forw_line_seg(POSITION curr_pos
, int skipeol
, int rscroll
, int nochop
)
97 if (curr_pos
== NULL_POSITION
)
100 return (NULL_POSITION
);
103 if (hilite_search
== OPT_ONPLUS
|| is_filtering() || status_col
)
106 * If we are ignoring EOI (command F), only prepare
107 * one line ahead, to avoid getting stuck waiting for
108 * slow data without displaying the data we already have.
109 * If we're not ignoring EOI, we *could* do the same, but
110 * for efficiency we prepare several lines ahead at once.
112 prep_hilite(curr_pos
, curr_pos
+ 3*size_linebuf
,
113 ignore_eoi
? 1 : -1);
114 curr_pos
= next_unfiltered(curr_pos
);
117 if (ch_seek(curr_pos
))
120 return (NULL_POSITION
);
124 * Step back to the beginning of the line.
132 return (NULL_POSITION
);
139 (void) ch_forw_get();
146 * Read forward again to the position we should start at.
149 plinestart(base_pos
);
150 (void) ch_seek(base_pos
);
152 while (new_pos
< curr_pos
)
157 return (NULL_POSITION
);
160 backchars
= pappend(c
, new_pos
);
165 if (wordwrap
&& (c
== ' ' || c
== '\t'))
171 } while (c
== ' ' || c
== '\t');
174 new_pos
-= backchars
;
175 while (--backchars
>= 0)
176 (void) ch_back_get();
183 * Read the first character to display.
189 return (NULL_POSITION
);
191 blankline
= (c
== '\n' || c
== '\r');
192 wrap_pos
= NULL_POSITION
;
193 skipped_leading
= FALSE
;
196 * Read each character in the line and append to the line buffer.
204 return (NULL_POSITION
);
206 if (c
== '\n' || c
== EOI
)
211 backchars
= pflushmbc();
213 if (backchars
> 0 && (nochop
|| !chop_line()) && hshift
== 0)
215 new_pos
-= backchars
+ 1;
226 * Append the char to the line and get the next char.
228 backchars
= pappend(c
, ch_tell()-1);
232 * The char won't fit in the line; the line
233 * is too long to print in the screen width.
238 /* Read to end of line. */
239 edisp_pos
= ch_tell();
245 return (NULL_POSITION
);
248 } while (c
!= '\n' && c
!= EOI
);
251 quit_if_one_screen
= FALSE
;
256 new_pos
= ch_tell() - backchars
;
260 * We're word-wrapping, so go back to the last space.
261 * However, if it's the space itself that couldn't fit,
262 * simply ignore it and any subsequent spaces.
264 if (c
== ' ' || c
== '\t')
270 } while (c
== ' ' || c
== '\t');
275 } else if (wrap_pos
== NULL_POSITION
)
276 new_pos
= ch_tell() - backchars
;
289 if (c
== ' ' || c
== '\t')
293 wrap_pos
= ch_tell();
297 skipped_leading
= TRUE
;
303 if (blankline
&& show_attn
)
305 /* Add spurious space to carry possible attn hilite. */
306 pappend(' ', ch_tell()-1);
309 pdone(endline
, rscroll
&& chopped
, 1);
312 if (is_filtered(base_pos
))
315 * We don't want to display this line.
322 init_status_col(base_pos
, line_position(), edisp_pos
, new_pos
);
325 if (squeeze
&& blankline
)
328 * This line is blank.
329 * Skip down to the last contiguous blank line
330 * and pretend it is the one which we are returning.
332 while ((c
= ch_forw_get()) == '\n' || c
== '\r')
336 return (NULL_POSITION
);
339 (void) ch_back_get();
346 public POSITION
forw_line(POSITION curr_pos
)
349 return forw_line_seg(curr_pos
, (chop_line() || hshift
> 0), TRUE
, FALSE
);
353 * Get the previous line.
354 * A "current" position is passed and a "new" position is returned.
355 * The current position is the position of the first character of
356 * a line. The new position is the position of the first character
357 * of the PREVIOUS line. The line obtained is the one starting at new_pos.
359 public POSITION
back_line(POSITION curr_pos
)
364 POSITION begin_new_pos
;
373 if (curr_pos
== NULL_POSITION
|| curr_pos
<= ch_zero())
376 return (NULL_POSITION
);
379 if (hilite_search
== OPT_ONPLUS
|| is_filtering() || status_col
)
380 prep_hilite((curr_pos
< 3*size_linebuf
) ?
381 0 : curr_pos
- 3*size_linebuf
, curr_pos
, -1);
383 if (ch_seek(curr_pos
-1))
386 return (NULL_POSITION
);
392 * Find out if the "current" line was blank.
394 (void) ch_forw_get(); /* Skip the newline */
395 c
= ch_forw_get(); /* First char of "current" line */
396 (void) ch_back_get(); /* Restore our position */
397 (void) ch_back_get();
399 if (c
== '\n' || c
== '\r')
402 * The "current" line was blank.
403 * Skip over any preceding blank lines,
404 * since we skipped them in forw_line().
406 while ((c
= ch_back_get()) == '\n' || c
== '\r')
410 return (NULL_POSITION
);
415 return (NULL_POSITION
);
417 (void) ch_forw_get();
422 * Scan backwards until we hit the beginning of the line.
429 return (NULL_POSITION
);
435 * This is the newline ending the previous line.
436 * We have hit the beginning of the line.
438 base_pos
= ch_tell() + 1;
444 * We have hit the beginning of the file.
445 * This must be the first line in the file.
446 * This must, of course, be the beginning of the line.
448 base_pos
= ch_tell();
454 * Now scan forwards from the beginning of this line.
455 * We keep discarding "printable lines" (based on screen width)
456 * until we reach the curr_pos.
458 * {{ This algorithm is pretty inefficient if the lines
459 * are much longer than the screen width,
460 * but I don't know of any better way. }}
463 if (ch_seek(new_pos
))
466 return (NULL_POSITION
);
472 wrap_pos
= NULL_POSITION
;
473 skipped_leading
= FALSE
;
474 begin_new_pos
= new_pos
;
475 (void) ch_seek(new_pos
);
481 if (c
== EOI
|| ABORT_SIGS())
484 return (NULL_POSITION
);
489 backchars
= pflushmbc();
490 if (backchars
> 0 && !chop_line() && hshift
== 0)
499 backchars
= pappend(c
, ch_tell()-1);
503 * Got a full printable line, but we haven't
504 * reached our curr_pos yet. Discard the line
505 * and start a new one.
507 if (chop_line() || hshift
> 0)
511 quit_if_one_screen
= FALSE
;
519 new_pos
-= backchars
;
522 if (c
== ' ' || c
== '\t')
527 if (c
== ' ' || c
== '\t')
542 if (new_pos
>= curr_pos
)
548 if (wrap_pos
== NULL_POSITION
)
549 new_pos
-= backchars
;
558 if (c
== ' ' || c
== '\t')
563 skipped_leading
= TRUE
;
565 if (new_pos
>= curr_pos
)
572 pdone(endline
, chopped
, 0);
575 if (is_filtered(base_pos
))
578 * We don't want to display this line.
579 * Get the previous line.
581 curr_pos
= begin_new_pos
;
585 init_status_col(base_pos
, line_position(), edisp_pos
, new_pos
);
588 return (begin_new_pos
);
594 public void set_attnpos(POSITION pos
)
598 if (pos
!= NULL_POSITION
)
607 if (c
== '\n' || c
== '\r')
609 (void) ch_back_get();
618 if (c
== EOI
|| c
== '\n' || c
== '\r')