xref: /freebsd/contrib/less/input.c (revision 7750ad47a9a7dbc83f87158464170c8640723293)
1 /*
2  * Copyright (C) 1984-2011  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 about less, or for information on how to
8  * contact the author, see the README file.
9  */
10 
11 
12 /*
13  * High level routines dealing with getting lines of input
14  * from the file being viewed.
15  *
16  * When we speak of "lines" here, we mean PRINTABLE lines;
17  * lines processed with respect to the screen width.
18  * We use the term "raw line" to refer to lines simply
19  * delimited by newlines; not processed with respect to screen width.
20  */
21 
22 #include "less.h"
23 
24 extern int squeeze;
25 extern int chopline;
26 extern int hshift;
27 extern int quit_if_one_screen;
28 extern int sigs;
29 extern int ignore_eoi;
30 extern int status_col;
31 extern POSITION start_attnpos;
32 extern POSITION end_attnpos;
33 #if HILITE_SEARCH
34 extern int hilite_search;
35 extern int size_linebuf;
36 #endif
37 
38 /*
39  * Get the next line.
40  * A "current" position is passed and a "new" position is returned.
41  * The current position is the position of the first character of
42  * a line.  The new position is the position of the first character
43  * of the NEXT line.  The line obtained is the line starting at curr_pos.
44  */
45 	public POSITION
46 forw_line(curr_pos)
47 	POSITION curr_pos;
48 {
49 	POSITION base_pos;
50 	POSITION new_pos;
51 	register int c;
52 	int blankline;
53 	int endline;
54 	int backchars;
55 
56 get_forw_line:
57 	if (curr_pos == NULL_POSITION)
58 	{
59 		null_line();
60 		return (NULL_POSITION);
61 	}
62 #if HILITE_SEARCH
63 	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
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 #endif
74 	if (ch_seek(curr_pos))
75 	{
76 		null_line();
77 		return (NULL_POSITION);
78 	}
79 
80 	/*
81 	 * Step back to the beginning of the line.
82 	 */
83 	base_pos = curr_pos;
84 	for (;;)
85 	{
86 		if (ABORT_SIGS())
87 		{
88 			null_line();
89 			return (NULL_POSITION);
90 		}
91 		c = ch_back_get();
92 		if (c == EOI)
93 			break;
94 		if (c == '\n')
95 		{
96 			(void) ch_forw_get();
97 			break;
98 		}
99 		--base_pos;
100 	}
101 
102 	/*
103 	 * Read forward again to the position we should start at.
104 	 */
105  	prewind();
106 	plinenum(base_pos);
107 	(void) ch_seek(base_pos);
108 	new_pos = base_pos;
109 	while (new_pos < curr_pos)
110 	{
111 		if (ABORT_SIGS())
112 		{
113 			null_line();
114 			return (NULL_POSITION);
115 		}
116 		c = ch_forw_get();
117 		backchars = pappend(c, new_pos);
118 		new_pos++;
119 		if (backchars > 0)
120 		{
121 			pshift_all();
122 			new_pos -= backchars;
123 			while (--backchars >= 0)
124 				(void) ch_back_get();
125 		}
126 	}
127 	(void) pflushmbc();
128 	pshift_all();
129 
130 	/*
131 	 * Read the first character to display.
132 	 */
133 	c = ch_forw_get();
134 	if (c == EOI)
135 	{
136 		null_line();
137 		return (NULL_POSITION);
138 	}
139 	blankline = (c == '\n' || c == '\r');
140 
141 	/*
142 	 * Read each character in the line and append to the line buffer.
143 	 */
144 	for (;;)
145 	{
146 		if (ABORT_SIGS())
147 		{
148 			null_line();
149 			return (NULL_POSITION);
150 		}
151 		if (c == '\n' || c == EOI)
152 		{
153 			/*
154 			 * End of the line.
155 			 */
156 			backchars = pflushmbc();
157 			new_pos = ch_tell();
158 			if (backchars > 0 && !chopline && hshift == 0)
159 			{
160 				new_pos -= backchars + 1;
161 				endline = FALSE;
162 			} else
163 				endline = TRUE;
164 			break;
165 		}
166 		if (c != '\r')
167 			blankline = 0;
168 
169 		/*
170 		 * Append the char to the line and get the next char.
171 		 */
172 		backchars = pappend(c, ch_tell()-1);
173 		if (backchars > 0)
174 		{
175 			/*
176 			 * The char won't fit in the line; the line
177 			 * is too long to print in the screen width.
178 			 * End the line here.
179 			 */
180 			if (chopline || hshift > 0)
181 			{
182 				do
183 				{
184 					if (ABORT_SIGS())
185 					{
186 						null_line();
187 						return (NULL_POSITION);
188 					}
189 					c = ch_forw_get();
190 				} while (c != '\n' && c != EOI);
191 				new_pos = ch_tell();
192 				endline = TRUE;
193 				quit_if_one_screen = FALSE;
194 			} else
195 			{
196 				new_pos = ch_tell() - backchars;
197 				endline = FALSE;
198 			}
199 			break;
200 		}
201 		c = ch_forw_get();
202 	}
203 
204 	pdone(endline, 1);
205 
206 #if HILITE_SEARCH
207 	if (is_filtered(base_pos))
208 	{
209 		/*
210 		 * We don't want to display this line.
211 		 * Get the next line.
212 		 */
213 		curr_pos = new_pos;
214 		goto get_forw_line;
215 	}
216 
217 	if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
218 		set_status_col('*');
219 #endif
220 
221 	if (squeeze && blankline)
222 	{
223 		/*
224 		 * This line is blank.
225 		 * Skip down to the last contiguous blank line
226 		 * and pretend it is the one which we are returning.
227 		 */
228 		while ((c = ch_forw_get()) == '\n' || c == '\r')
229 			if (ABORT_SIGS())
230 			{
231 				null_line();
232 				return (NULL_POSITION);
233 			}
234 		if (c != EOI)
235 			(void) ch_back_get();
236 		new_pos = ch_tell();
237 	}
238 
239 	return (new_pos);
240 }
241 
242 /*
243  * Get the previous line.
244  * A "current" position is passed and a "new" position is returned.
245  * The current position is the position of the first character of
246  * a line.  The new position is the position of the first character
247  * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
248  */
249 	public POSITION
250 back_line(curr_pos)
251 	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 && is_hilited(base_pos, ch_tell()-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(pos)
431 	POSITION pos;
432 {
433 	int c;
434 
435 	if (pos != NULL_POSITION)
436 	{
437 		if (ch_seek(pos))
438 			return;
439 		for (;;)
440 		{
441 			c = ch_forw_get();
442 			if (c == EOI)
443 				return;
444 			if (c != '\n' && c != '\r')
445 				break;
446 			pos++;
447 		}
448 	}
449 	start_attnpos = pos;
450 	for (;;)
451 	{
452 		c = ch_forw_get();
453 		pos++;
454 		if (c == EOI || c == '\n' || c == '\r')
455 			break;
456 	}
457 	end_attnpos = pos;
458 }
459