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