xref: /openbsd-src/usr.bin/less/input.c (revision d65139b4ae439ce0363945a0003c48c6286cc117)
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