xref: /freebsd/contrib/less/input.c (revision 5944f899a2519c6321bac3c17cc076418643a088)
1 /*
2  * Copyright (C) 1984-2015  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  * High level routines dealing with getting lines of input
13  * from the file being viewed.
14  *
15  * When we speak of "lines" here, we mean PRINTABLE lines;
16  * lines processed with respect to the screen width.
17  * We use the term "raw line" to refer to lines simply
18  * delimited by newlines; not processed with respect to screen width.
19  */
20 
21 #include "less.h"
22 
23 extern int squeeze;
24 extern int chopline;
25 extern int hshift;
26 extern int quit_if_one_screen;
27 extern int sigs;
28 extern int ignore_eoi;
29 extern int status_col;
30 extern POSITION start_attnpos;
31 extern POSITION end_attnpos;
32 #if HILITE_SEARCH
33 extern int hilite_search;
34 extern int size_linebuf;
35 #endif
36 
37 /*
38  * Get the next line.
39  * A "current" position is passed and a "new" position is returned.
40  * The current position is the position of the first character of
41  * a line.  The new position is the position of the first character
42  * of the NEXT line.  The line obtained is the line starting at curr_pos.
43  */
44 	public POSITION
45 forw_line(POSITION curr_pos)
46 {
47 	POSITION base_pos;
48 	POSITION new_pos;
49 	int c;
50 	int blankline;
51 	int endline;
52 	int backchars;
53 
54 get_forw_line:
55 	if (curr_pos == NULL_POSITION)
56 	{
57 		null_line();
58 		return (NULL_POSITION);
59 	}
60 #if HILITE_SEARCH
61 	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
62 	{
63 		/*
64 		 * If we are ignoring EOI (command F), only prepare
65 		 * one line ahead, to avoid getting stuck waiting for
66 		 * slow data without displaying the data we already have.
67 		 * If we're not ignoring EOI, we *could* do the same, but
68 		 * for efficiency we prepare several lines ahead at once.
69 		 */
70 		prep_hilite(curr_pos, curr_pos + 3*size_linebuf,
71 				ignore_eoi ? 1 : -1);
72 		curr_pos = next_unfiltered(curr_pos);
73 	}
74 #endif
75 	if (ch_seek(curr_pos))
76 	{
77 		null_line();
78 		return (NULL_POSITION);
79 	}
80 
81 	/*
82 	 * Step back to the beginning of the line.
83 	 */
84 	base_pos = curr_pos;
85 	for (;;)
86 	{
87 		if (ABORT_SIGS())
88 		{
89 			null_line();
90 			return (NULL_POSITION);
91 		}
92 		c = ch_back_get();
93 		if (c == EOI)
94 			break;
95 		if (c == '\n')
96 		{
97 			(void) ch_forw_get();
98 			break;
99 		}
100 		--base_pos;
101 	}
102 
103 	/*
104 	 * Read forward again to the position we should start at.
105 	 */
106  	prewind();
107 	plinenum(base_pos);
108 	(void) ch_seek(base_pos);
109 	new_pos = base_pos;
110 	while (new_pos < curr_pos)
111 	{
112 		if (ABORT_SIGS())
113 		{
114 			null_line();
115 			return (NULL_POSITION);
116 		}
117 		c = ch_forw_get();
118 		backchars = pappend(c, new_pos);
119 		new_pos++;
120 		if (backchars > 0)
121 		{
122 			pshift_all();
123 			new_pos -= backchars;
124 			while (--backchars >= 0)
125 				(void) ch_back_get();
126 		}
127 	}
128 	(void) pflushmbc();
129 	pshift_all();
130 
131 	/*
132 	 * Read the first character to display.
133 	 */
134 	c = ch_forw_get();
135 	if (c == EOI)
136 	{
137 		null_line();
138 		return (NULL_POSITION);
139 	}
140 	blankline = (c == '\n' || c == '\r');
141 
142 	/*
143 	 * Read each character in the line and append to the line buffer.
144 	 */
145 	for (;;)
146 	{
147 		if (ABORT_SIGS())
148 		{
149 			null_line();
150 			return (NULL_POSITION);
151 		}
152 		if (c == '\n' || c == EOI)
153 		{
154 			/*
155 			 * End of the line.
156 			 */
157 			backchars = pflushmbc();
158 			new_pos = ch_tell();
159 			if (backchars > 0 && !chopline && hshift == 0)
160 			{
161 				new_pos -= backchars + 1;
162 				endline = FALSE;
163 			} else
164 				endline = TRUE;
165 			break;
166 		}
167 		if (c != '\r')
168 			blankline = 0;
169 
170 		/*
171 		 * Append the char to the line and get the next char.
172 		 */
173 		backchars = pappend(c, ch_tell()-1);
174 		if (backchars > 0)
175 		{
176 			/*
177 			 * The char won't fit in the line; the line
178 			 * is too long to print in the screen width.
179 			 * End the line here.
180 			 */
181 			if (chopline || hshift > 0)
182 			{
183 				do
184 				{
185 					if (ABORT_SIGS())
186 					{
187 						null_line();
188 						return (NULL_POSITION);
189 					}
190 					c = ch_forw_get();
191 				} while (c != '\n' && c != EOI);
192 				new_pos = ch_tell();
193 				endline = TRUE;
194 				quit_if_one_screen = FALSE;
195 			} else
196 			{
197 				new_pos = ch_tell() - backchars;
198 				endline = FALSE;
199 			}
200 			break;
201 		}
202 		c = ch_forw_get();
203 	}
204 
205 	pdone(endline, 1);
206 
207 #if HILITE_SEARCH
208 	if (is_filtered(base_pos))
209 	{
210 		/*
211 		 * We don't want to display this line.
212 		 * Get the next line.
213 		 */
214 		curr_pos = new_pos;
215 		goto get_forw_line;
216 	}
217 
218 	if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
219 		set_status_col('*');
220 #endif
221 
222 	if (squeeze && blankline)
223 	{
224 		/*
225 		 * This line is blank.
226 		 * Skip down to the last contiguous blank line
227 		 * and pretend it is the one which we are returning.
228 		 */
229 		while ((c = ch_forw_get()) == '\n' || c == '\r')
230 			if (ABORT_SIGS())
231 			{
232 				null_line();
233 				return (NULL_POSITION);
234 			}
235 		if (c != EOI)
236 			(void) ch_back_get();
237 		new_pos = ch_tell();
238 	}
239 
240 	return (new_pos);
241 }
242 
243 /*
244  * Get the previous line.
245  * A "current" position is passed and a "new" position is returned.
246  * The current position is the position of the first character of
247  * a line.  The new position is the position of the first character
248  * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
249  */
250 	public POSITION
251 back_line(POSITION curr_pos)
252 {
253 	POSITION new_pos, begin_new_pos, base_pos;
254 	int c;
255 	int endline;
256 	int backchars;
257 
258 get_back_line:
259 	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
260 	{
261 		null_line();
262 		return (NULL_POSITION);
263 	}
264 #if HILITE_SEARCH
265 	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
266 		prep_hilite((curr_pos < 3*size_linebuf) ?
267 				0 : curr_pos - 3*size_linebuf, curr_pos, -1);
268 #endif
269 	if (ch_seek(curr_pos-1))
270 	{
271 		null_line();
272 		return (NULL_POSITION);
273 	}
274 
275 	if (squeeze)
276 	{
277 		/*
278 		 * Find out if the "current" line was blank.
279 		 */
280 		(void) ch_forw_get();    /* Skip the newline */
281 		c = ch_forw_get();       /* First char of "current" line */
282 		(void) ch_back_get();    /* Restore our position */
283 		(void) ch_back_get();
284 
285 		if (c == '\n' || c == '\r')
286 		{
287 			/*
288 			 * The "current" line was blank.
289 			 * Skip over any preceding blank lines,
290 			 * since we skipped them in forw_line().
291 			 */
292 			while ((c = ch_back_get()) == '\n' || c == '\r')
293 				if (ABORT_SIGS())
294 				{
295 					null_line();
296 					return (NULL_POSITION);
297 				}
298 			if (c == EOI)
299 			{
300 				null_line();
301 				return (NULL_POSITION);
302 			}
303 			(void) ch_forw_get();
304 		}
305 	}
306 
307 	/*
308 	 * Scan backwards until we hit the beginning of the line.
309 	 */
310 	for (;;)
311 	{
312 		if (ABORT_SIGS())
313 		{
314 			null_line();
315 			return (NULL_POSITION);
316 		}
317 		c = ch_back_get();
318 		if (c == '\n')
319 		{
320 			/*
321 			 * This is the newline ending the previous line.
322 			 * We have hit the beginning of the line.
323 			 */
324 			base_pos = ch_tell() + 1;
325 			break;
326 		}
327 		if (c == EOI)
328 		{
329 			/*
330 			 * We have hit the beginning of the file.
331 			 * This must be the first line in the file.
332 			 * This must, of course, be the beginning of the line.
333 			 */
334 			base_pos = ch_tell();
335 			break;
336 		}
337 	}
338 
339 	/*
340 	 * Now scan forwards from the beginning of this line.
341 	 * We keep discarding "printable lines" (based on screen width)
342 	 * until we reach the curr_pos.
343 	 *
344 	 * {{ This algorithm is pretty inefficient if the lines
345 	 *    are much longer than the screen width,
346 	 *    but I don't know of any better way. }}
347 	 */
348 	new_pos = base_pos;
349 	if (ch_seek(new_pos))
350 	{
351 		null_line();
352 		return (NULL_POSITION);
353 	}
354 	endline = FALSE;
355 	prewind();
356 	plinenum(new_pos);
357     loop:
358 	begin_new_pos = new_pos;
359 	(void) ch_seek(new_pos);
360 
361 	do
362 	{
363 		c = ch_forw_get();
364 		if (c == EOI || ABORT_SIGS())
365 		{
366 			null_line();
367 			return (NULL_POSITION);
368 		}
369 		new_pos++;
370 		if (c == '\n')
371 		{
372 			backchars = pflushmbc();
373 			if (backchars > 0 && !chopline && hshift == 0)
374 			{
375 				backchars++;
376 				goto shift;
377 			}
378 			endline = TRUE;
379 			break;
380 		}
381 		backchars = pappend(c, ch_tell()-1);
382 		if (backchars > 0)
383 		{
384 			/*
385 			 * Got a full printable line, but we haven't
386 			 * reached our curr_pos yet.  Discard the line
387 			 * and start a new one.
388 			 */
389 			if (chopline || hshift > 0)
390 			{
391 				endline = TRUE;
392 				quit_if_one_screen = FALSE;
393 				break;
394 			}
395 		shift:
396 			pshift_all();
397 			while (backchars-- > 0)
398 			{
399 				(void) ch_back_get();
400 				new_pos--;
401 			}
402 			goto loop;
403 		}
404 	} while (new_pos < curr_pos);
405 
406 	pdone(endline, 0);
407 
408 #if HILITE_SEARCH
409 	if (is_filtered(base_pos))
410 	{
411 		/*
412 		 * We don't want to display this line.
413 		 * Get the previous line.
414 		 */
415 		curr_pos = begin_new_pos;
416 		goto get_back_line;
417 	}
418 
419 	if (status_col && curr_pos > 0 && is_hilited(base_pos, curr_pos-1, 1, NULL))
420 		set_status_col('*');
421 #endif
422 
423 	return (begin_new_pos);
424 }
425 
426 /*
427  * Set attnpos.
428  */
429 	public void
430 set_attnpos(POSITION pos)
431 {
432 	int c;
433 
434 	if (pos != NULL_POSITION)
435 	{
436 		if (ch_seek(pos))
437 			return;
438 		for (;;)
439 		{
440 			c = ch_forw_get();
441 			if (c == EOI)
442 				break;
443 			if (c == '\n' || c == '\r')
444 			{
445 				(void) ch_back_get();
446 				break;
447 			}
448 			pos++;
449 		}
450 		end_attnpos = pos;
451 		for (;;)
452 		{
453 			c = ch_back_get();
454 			if (c == EOI || c == '\n' || c == '\r')
455 				break;
456 			pos--;
457 		}
458 	}
459 	start_attnpos = pos;
460 }
461