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