1e3b7954bSetheisen /*
226ad794dSshadchin * Copyright (C) 1984-2012 Mark Nudelman
3b8c1323eSnicm * Modified for use with illumos by Garrett D'Amore.
4b8c1323eSnicm * Copyright 2014 Garrett D'Amore <garrett@damore.org>
5e3b7954bSetheisen *
645076018Smillert * You may distribute under the terms of either the GNU General Public
745076018Smillert * License or the Less License, as specified in the README file.
8e3b7954bSetheisen *
926ad794dSshadchin * For more information, see the README file.
10e3b7954bSetheisen */
11e3b7954bSetheisen
12e3b7954bSetheisen /*
13e3b7954bSetheisen * High level routines dealing with getting lines of input
14e3b7954bSetheisen * from the file being viewed.
15e3b7954bSetheisen *
16e3b7954bSetheisen * When we speak of "lines" here, we mean PRINTABLE lines;
17e3b7954bSetheisen * lines processed with respect to the screen width.
18e3b7954bSetheisen * We use the term "raw line" to refer to lines simply
19e3b7954bSetheisen * delimited by newlines; not processed with respect to screen width.
20e3b7954bSetheisen */
21e3b7954bSetheisen
22e3b7954bSetheisen #include "less.h"
23e3b7954bSetheisen
24e3b7954bSetheisen extern int squeeze;
25e3b7954bSetheisen extern int chopline;
2645076018Smillert extern int hshift;
2745076018Smillert extern int quit_if_one_screen;
2845076018Smillert extern int ignore_eoi;
29168565f4Sshadchin extern int status_col;
30171bb95eSnicm extern off_t start_attnpos;
31171bb95eSnicm extern off_t end_attnpos;
32e3b7954bSetheisen extern int hilite_search;
33e3b7954bSetheisen extern int size_linebuf;
34e3b7954bSetheisen
35e3b7954bSetheisen /*
36e3b7954bSetheisen * Get the next line.
37e3b7954bSetheisen * A "current" position is passed and a "new" position is returned.
38e3b7954bSetheisen * The current position is the position of the first character of
39e3b7954bSetheisen * a line. The new position is the position of the first character
40e3b7954bSetheisen * of the NEXT line. The line obtained is the line starting at curr_pos.
41e3b7954bSetheisen */
42171bb95eSnicm off_t
forw_line(off_t curr_pos)43171bb95eSnicm forw_line(off_t curr_pos)
44e3b7954bSetheisen {
45171bb95eSnicm off_t base_pos;
46171bb95eSnicm off_t new_pos;
4738be9996Stedu int c;
48e3b7954bSetheisen int blankline;
49e3b7954bSetheisen int endline;
50168565f4Sshadchin int backchars;
51e3b7954bSetheisen
52168565f4Sshadchin get_forw_line:
53171bb95eSnicm if (curr_pos == -1) {
54e3b7954bSetheisen null_line();
55171bb95eSnicm return (-1);
56e3b7954bSetheisen }
57168565f4Sshadchin if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
5845076018Smillert /*
5945076018Smillert * If we are ignoring EOI (command F), only prepare
6045076018Smillert * one line ahead, to avoid getting stuck waiting for
6145076018Smillert * slow data without displaying the data we already have.
6245076018Smillert * If we're not ignoring EOI, we *could* do the same, but
6345076018Smillert * for efficiency we prepare several lines ahead at once.
6445076018Smillert */
6545076018Smillert prep_hilite(curr_pos, curr_pos + 3*size_linebuf,
6645076018Smillert ignore_eoi ? 1 : -1);
67171bb95eSnicm if (ch_seek(curr_pos)) {
68e3b7954bSetheisen null_line();
69171bb95eSnicm return (-1);
70e3b7954bSetheisen }
71e3b7954bSetheisen
72168565f4Sshadchin /*
73168565f4Sshadchin * Step back to the beginning of the line.
74168565f4Sshadchin */
75168565f4Sshadchin base_pos = curr_pos;
76171bb95eSnicm for (;;) {
77*d65139b4Sderaadt if (abort_sigs()) {
78168565f4Sshadchin null_line();
79171bb95eSnicm return (-1);
80168565f4Sshadchin }
81168565f4Sshadchin c = ch_back_get();
82168565f4Sshadchin if (c == EOI)
83168565f4Sshadchin break;
84171bb95eSnicm if (c == '\n') {
85168565f4Sshadchin (void) ch_forw_get();
86168565f4Sshadchin break;
87168565f4Sshadchin }
88168565f4Sshadchin --base_pos;
89168565f4Sshadchin }
90e3b7954bSetheisen
91168565f4Sshadchin /*
92168565f4Sshadchin * Read forward again to the position we should start at.
93168565f4Sshadchin */
94168565f4Sshadchin prewind();
95168565f4Sshadchin plinenum(base_pos);
96168565f4Sshadchin (void) ch_seek(base_pos);
97168565f4Sshadchin new_pos = base_pos;
98171bb95eSnicm while (new_pos < curr_pos) {
99*d65139b4Sderaadt if (abort_sigs()) {
100168565f4Sshadchin null_line();
101171bb95eSnicm return (-1);
102168565f4Sshadchin }
103168565f4Sshadchin c = ch_forw_get();
104168565f4Sshadchin backchars = pappend(c, new_pos);
105168565f4Sshadchin new_pos++;
106171bb95eSnicm if (backchars > 0) {
107168565f4Sshadchin pshift_all();
108168565f4Sshadchin new_pos -= backchars;
109168565f4Sshadchin while (--backchars >= 0)
110168565f4Sshadchin (void) ch_back_get();
111168565f4Sshadchin }
112168565f4Sshadchin }
113168565f4Sshadchin (void) pflushmbc();
114168565f4Sshadchin pshift_all();
115168565f4Sshadchin
116168565f4Sshadchin /*
117168565f4Sshadchin * Read the first character to display.
118168565f4Sshadchin */
119e3b7954bSetheisen c = ch_forw_get();
120171bb95eSnicm if (c == EOI) {
121e3b7954bSetheisen null_line();
122171bb95eSnicm return (-1);
123e3b7954bSetheisen }
124e3b7954bSetheisen blankline = (c == '\n' || c == '\r');
125e3b7954bSetheisen
126168565f4Sshadchin /*
127168565f4Sshadchin * Read each character in the line and append to the line buffer.
128168565f4Sshadchin */
129171bb95eSnicm for (;;) {
130*d65139b4Sderaadt if (abort_sigs()) {
131e3b7954bSetheisen null_line();
132171bb95eSnicm return (-1);
133e3b7954bSetheisen }
134171bb95eSnicm if (c == '\n' || c == EOI) {
135e3b7954bSetheisen /*
136e3b7954bSetheisen * End of the line.
137e3b7954bSetheisen */
138168565f4Sshadchin backchars = pflushmbc();
139e3b7954bSetheisen new_pos = ch_tell();
140171bb95eSnicm if (backchars > 0 && !chopline && hshift == 0) {
141168565f4Sshadchin new_pos -= backchars + 1;
142168565f4Sshadchin endline = FALSE;
143168565f4Sshadchin } else
14445076018Smillert endline = TRUE;
145e3b7954bSetheisen break;
146e3b7954bSetheisen }
147168565f4Sshadchin if (c != '\r')
148168565f4Sshadchin blankline = 0;
149e3b7954bSetheisen
150e3b7954bSetheisen /*
151e3b7954bSetheisen * Append the char to the line and get the next char.
152e3b7954bSetheisen */
153168565f4Sshadchin backchars = pappend(c, ch_tell()-1);
154171bb95eSnicm if (backchars > 0) {
155e3b7954bSetheisen /*
156e3b7954bSetheisen * The char won't fit in the line; the line
157e3b7954bSetheisen * is too long to print in the screen width.
158e3b7954bSetheisen * End the line here.
159e3b7954bSetheisen */
160171bb95eSnicm if (chopline || hshift > 0) {
161171bb95eSnicm do {
162*d65139b4Sderaadt if (abort_sigs()) {
163168565f4Sshadchin null_line();
164171bb95eSnicm return (-1);
165168565f4Sshadchin }
166e3b7954bSetheisen c = ch_forw_get();
167e3b7954bSetheisen } while (c != '\n' && c != EOI);
168e3b7954bSetheisen new_pos = ch_tell();
16945076018Smillert endline = TRUE;
17045076018Smillert quit_if_one_screen = FALSE;
171171bb95eSnicm } else {
172168565f4Sshadchin new_pos = ch_tell() - backchars;
17345076018Smillert endline = FALSE;
174e3b7954bSetheisen }
175e3b7954bSetheisen break;
176e3b7954bSetheisen }
177e3b7954bSetheisen c = ch_forw_get();
178e3b7954bSetheisen }
179168565f4Sshadchin
180168565f4Sshadchin pdone(endline, 1);
181168565f4Sshadchin
182171bb95eSnicm if (is_filtered(base_pos)) {
183168565f4Sshadchin /*
184168565f4Sshadchin * We don't want to display this line.
185168565f4Sshadchin * Get the next line.
186168565f4Sshadchin */
187168565f4Sshadchin curr_pos = new_pos;
188168565f4Sshadchin goto get_forw_line;
189168565f4Sshadchin }
190168565f4Sshadchin
191168565f4Sshadchin if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
192168565f4Sshadchin set_status_col('*');
193e3b7954bSetheisen
194171bb95eSnicm if (squeeze && blankline) {
195e3b7954bSetheisen /*
196e3b7954bSetheisen * This line is blank.
197e3b7954bSetheisen * Skip down to the last contiguous blank line
198e3b7954bSetheisen * and pretend it is the one which we are returning.
199e3b7954bSetheisen */
200e3b7954bSetheisen while ((c = ch_forw_get()) == '\n' || c == '\r')
201*d65139b4Sderaadt if (abort_sigs()) {
202e3b7954bSetheisen null_line();
203171bb95eSnicm return (-1);
204e3b7954bSetheisen }
205e3b7954bSetheisen if (c != EOI)
206e3b7954bSetheisen (void) ch_back_get();
207e3b7954bSetheisen new_pos = ch_tell();
208e3b7954bSetheisen }
209e3b7954bSetheisen
210e3b7954bSetheisen return (new_pos);
211e3b7954bSetheisen }
212e3b7954bSetheisen
213e3b7954bSetheisen /*
214e3b7954bSetheisen * Get the previous line.
215e3b7954bSetheisen * A "current" position is passed and a "new" position is returned.
216e3b7954bSetheisen * The current position is the position of the first character of
217e3b7954bSetheisen * a line. The new position is the position of the first character
218e3b7954bSetheisen * of the PREVIOUS line. The line obtained is the one starting at new_pos.
219e3b7954bSetheisen */
220171bb95eSnicm off_t
back_line(off_t curr_pos)221171bb95eSnicm back_line(off_t curr_pos)
222e3b7954bSetheisen {
223171bb95eSnicm off_t new_pos, begin_new_pos, base_pos;
224e3b7954bSetheisen int c;
225e3b7954bSetheisen int endline;
226168565f4Sshadchin int backchars;
227e3b7954bSetheisen
228168565f4Sshadchin get_back_line:
229171bb95eSnicm if (curr_pos == -1 || curr_pos <= ch_zero()) {
230e3b7954bSetheisen null_line();
231171bb95eSnicm return (-1);
232e3b7954bSetheisen }
233168565f4Sshadchin if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
234e3b7954bSetheisen prep_hilite((curr_pos < 3*size_linebuf) ?
23545076018Smillert 0 : curr_pos - 3*size_linebuf, curr_pos, -1);
236171bb95eSnicm if (ch_seek(curr_pos-1)) {
237e3b7954bSetheisen null_line();
238171bb95eSnicm return (-1);
239e3b7954bSetheisen }
240e3b7954bSetheisen
241171bb95eSnicm if (squeeze) {
242e3b7954bSetheisen /*
243e3b7954bSetheisen * Find out if the "current" line was blank.
244e3b7954bSetheisen */
245e3b7954bSetheisen (void) ch_forw_get(); /* Skip the newline */
246e3b7954bSetheisen c = ch_forw_get(); /* First char of "current" line */
247e3b7954bSetheisen (void) ch_back_get(); /* Restore our position */
248e3b7954bSetheisen (void) ch_back_get();
249e3b7954bSetheisen
250171bb95eSnicm if (c == '\n' || c == '\r') {
251e3b7954bSetheisen /*
252e3b7954bSetheisen * The "current" line was blank.
253e3b7954bSetheisen * Skip over any preceding blank lines,
254e3b7954bSetheisen * since we skipped them in forw_line().
255e3b7954bSetheisen */
256e3b7954bSetheisen while ((c = ch_back_get()) == '\n' || c == '\r')
257*d65139b4Sderaadt if (abort_sigs()) {
258e3b7954bSetheisen null_line();
259171bb95eSnicm return (-1);
260e3b7954bSetheisen }
261171bb95eSnicm if (c == EOI) {
262e3b7954bSetheisen null_line();
263171bb95eSnicm return (-1);
264e3b7954bSetheisen }
265e3b7954bSetheisen (void) ch_forw_get();
266e3b7954bSetheisen }
267e3b7954bSetheisen }
268e3b7954bSetheisen
269e3b7954bSetheisen /*
270e3b7954bSetheisen * Scan backwards until we hit the beginning of the line.
271e3b7954bSetheisen */
272171bb95eSnicm for (;;) {
273*d65139b4Sderaadt if (abort_sigs()) {
274e3b7954bSetheisen null_line();
275171bb95eSnicm return (-1);
276e3b7954bSetheisen }
277e3b7954bSetheisen c = ch_back_get();
278171bb95eSnicm if (c == '\n') {
279e3b7954bSetheisen /*
280e3b7954bSetheisen * This is the newline ending the previous line.
281e3b7954bSetheisen * We have hit the beginning of the line.
282e3b7954bSetheisen */
283168565f4Sshadchin base_pos = ch_tell() + 1;
284e3b7954bSetheisen break;
285e3b7954bSetheisen }
286171bb95eSnicm if (c == EOI) {
287e3b7954bSetheisen /*
288e3b7954bSetheisen * We have hit the beginning of the file.
289e3b7954bSetheisen * This must be the first line in the file.
290e3b7954bSetheisen * This must, of course, be the beginning of the line.
291e3b7954bSetheisen */
292168565f4Sshadchin base_pos = ch_tell();
293e3b7954bSetheisen break;
294e3b7954bSetheisen }
295e3b7954bSetheisen }
296e3b7954bSetheisen
297e3b7954bSetheisen /*
298e3b7954bSetheisen * Now scan forwards from the beginning of this line.
299e3b7954bSetheisen * We keep discarding "printable lines" (based on screen width)
300e3b7954bSetheisen * until we reach the curr_pos.
301e3b7954bSetheisen *
302e3b7954bSetheisen * {{ This algorithm is pretty inefficient if the lines
303e3b7954bSetheisen * are much longer than the screen width,
304e3b7954bSetheisen * but I don't know of any better way. }}
305e3b7954bSetheisen */
306168565f4Sshadchin new_pos = base_pos;
307171bb95eSnicm if (ch_seek(new_pos)) {
308e3b7954bSetheisen null_line();
309171bb95eSnicm return (-1);
310e3b7954bSetheisen }
31145076018Smillert endline = FALSE;
312e3b7954bSetheisen prewind();
313e3b7954bSetheisen plinenum(new_pos);
314168565f4Sshadchin loop:
315168565f4Sshadchin begin_new_pos = new_pos;
316e3b7954bSetheisen (void) ch_seek(new_pos);
317e3b7954bSetheisen
318171bb95eSnicm do {
319e3b7954bSetheisen c = ch_forw_get();
320*d65139b4Sderaadt if (c == EOI || abort_sigs()) {
321e3b7954bSetheisen null_line();
322171bb95eSnicm return (-1);
323e3b7954bSetheisen }
324e3b7954bSetheisen new_pos++;
325171bb95eSnicm if (c == '\n') {
326168565f4Sshadchin backchars = pflushmbc();
327171bb95eSnicm if (backchars > 0 && !chopline && hshift == 0) {
328168565f4Sshadchin backchars++;
329168565f4Sshadchin goto shift;
330168565f4Sshadchin }
33145076018Smillert endline = TRUE;
332e3b7954bSetheisen break;
333e3b7954bSetheisen }
334168565f4Sshadchin backchars = pappend(c, ch_tell()-1);
335171bb95eSnicm if (backchars > 0) {
336e3b7954bSetheisen /*
337e3b7954bSetheisen * Got a full printable line, but we haven't
338e3b7954bSetheisen * reached our curr_pos yet. Discard the line
339e3b7954bSetheisen * and start a new one.
340e3b7954bSetheisen */
341171bb95eSnicm if (chopline || hshift > 0) {
34245076018Smillert endline = TRUE;
34345076018Smillert quit_if_one_screen = FALSE;
344e3b7954bSetheisen break;
345e3b7954bSetheisen }
346168565f4Sshadchin shift:
347168565f4Sshadchin pshift_all();
348171bb95eSnicm while (backchars-- > 0) {
349e3b7954bSetheisen (void) ch_back_get();
350e3b7954bSetheisen new_pos--;
351168565f4Sshadchin }
352e3b7954bSetheisen goto loop;
353e3b7954bSetheisen }
354e3b7954bSetheisen } while (new_pos < curr_pos);
355e3b7954bSetheisen
356168565f4Sshadchin pdone(endline, 0);
357168565f4Sshadchin
358171bb95eSnicm if (is_filtered(base_pos)) {
359168565f4Sshadchin /*
360168565f4Sshadchin * We don't want to display this line.
361168565f4Sshadchin * Get the previous line.
362168565f4Sshadchin */
363168565f4Sshadchin curr_pos = begin_new_pos;
364168565f4Sshadchin goto get_back_line;
365168565f4Sshadchin }
366168565f4Sshadchin
367171bb95eSnicm if (status_col && curr_pos > 0 &&
368171bb95eSnicm is_hilited(base_pos, curr_pos-1, 1, NULL))
369168565f4Sshadchin set_status_col('*');
370e3b7954bSetheisen
371e3b7954bSetheisen return (begin_new_pos);
372e3b7954bSetheisen }
37345076018Smillert
37445076018Smillert /*
37545076018Smillert * Set attnpos.
37645076018Smillert */
377171bb95eSnicm void
set_attnpos(off_t pos)378171bb95eSnicm set_attnpos(off_t pos)
37945076018Smillert {
38045076018Smillert int c;
38145076018Smillert
382171bb95eSnicm if (pos != -1) {
38345076018Smillert if (ch_seek(pos))
38445076018Smillert return;
385171bb95eSnicm for (;;) {
38645076018Smillert c = ch_forw_get();
38745076018Smillert if (c == EOI)
38845076018Smillert return;
38945076018Smillert if (c != '\n' && c != '\r')
39045076018Smillert break;
39145076018Smillert pos++;
39245076018Smillert }
39345076018Smillert }
39445076018Smillert start_attnpos = pos;
395171bb95eSnicm for (;;) {
39645076018Smillert c = ch_forw_get();
39745076018Smillert pos++;
39845076018Smillert if (c == EOI || c == '\n' || c == '\r')
39945076018Smillert break;
40045076018Smillert }
40145076018Smillert end_attnpos = pos;
40245076018Smillert }
403