xref: /freebsd/contrib/less/input.c (revision 7374caaaed222af059fc66cb7fade7074ab391f4)
1 /*
2  * Copyright (C) 1984-2008  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 					c = ch_forw_get();
185 				} while (c != '\n' && c != EOI);
186 				new_pos = ch_tell();
187 				endline = TRUE;
188 				quit_if_one_screen = FALSE;
189 			} else
190 			{
191 				new_pos = ch_tell() - backchars;
192 				endline = FALSE;
193 			}
194 			break;
195 		}
196 		c = ch_forw_get();
197 	}
198 
199 	pdone(endline, c);
200 
201 #if HILITE_SEARCH
202 	if (is_filtered(base_pos))
203 	{
204 		/*
205 		 * We don't want to display this line.
206 		 * Get the next line.
207 		 */
208 		curr_pos = new_pos;
209 		goto get_forw_line;
210 	}
211 
212 	if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
213 		set_status_col('*');
214 #endif
215 
216 	if (squeeze && blankline)
217 	{
218 		/*
219 		 * This line is blank.
220 		 * Skip down to the last contiguous blank line
221 		 * and pretend it is the one which we are returning.
222 		 */
223 		while ((c = ch_forw_get()) == '\n' || c == '\r')
224 			if (ABORT_SIGS())
225 			{
226 				null_line();
227 				return (NULL_POSITION);
228 			}
229 		if (c != EOI)
230 			(void) ch_back_get();
231 		new_pos = ch_tell();
232 	}
233 
234 	return (new_pos);
235 }
236 
237 /*
238  * Get the previous line.
239  * A "current" position is passed and a "new" position is returned.
240  * The current position is the position of the first character of
241  * a line.  The new position is the position of the first character
242  * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
243  */
244 	public POSITION
245 back_line(curr_pos)
246 	POSITION curr_pos;
247 {
248 	POSITION new_pos, begin_new_pos, base_pos;
249 	int c;
250 	int endline;
251 	int backchars;
252 
253 get_back_line:
254 	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
255 	{
256 		null_line();
257 		return (NULL_POSITION);
258 	}
259 #if HILITE_SEARCH
260 	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
261 		prep_hilite((curr_pos < 3*size_linebuf) ?
262 				0 : curr_pos - 3*size_linebuf, curr_pos, -1);
263 #endif
264 	if (ch_seek(curr_pos-1))
265 	{
266 		null_line();
267 		return (NULL_POSITION);
268 	}
269 
270 	if (squeeze)
271 	{
272 		/*
273 		 * Find out if the "current" line was blank.
274 		 */
275 		(void) ch_forw_get();    /* Skip the newline */
276 		c = ch_forw_get();       /* First char of "current" line */
277 		(void) ch_back_get();    /* Restore our position */
278 		(void) ch_back_get();
279 
280 		if (c == '\n' || c == '\r')
281 		{
282 			/*
283 			 * The "current" line was blank.
284 			 * Skip over any preceding blank lines,
285 			 * since we skipped them in forw_line().
286 			 */
287 			while ((c = ch_back_get()) == '\n' || c == '\r')
288 				if (ABORT_SIGS())
289 				{
290 					null_line();
291 					return (NULL_POSITION);
292 				}
293 			if (c == EOI)
294 			{
295 				null_line();
296 				return (NULL_POSITION);
297 			}
298 			(void) ch_forw_get();
299 		}
300 	}
301 
302 	/*
303 	 * Scan backwards until we hit the beginning of the line.
304 	 */
305 	for (;;)
306 	{
307 		if (ABORT_SIGS())
308 		{
309 			null_line();
310 			return (NULL_POSITION);
311 		}
312 		c = ch_back_get();
313 		if (c == '\n')
314 		{
315 			/*
316 			 * This is the newline ending the previous line.
317 			 * We have hit the beginning of the line.
318 			 */
319 			base_pos = ch_tell() + 1;
320 			break;
321 		}
322 		if (c == EOI)
323 		{
324 			/*
325 			 * We have hit the beginning of the file.
326 			 * This must be the first line in the file.
327 			 * This must, of course, be the beginning of the line.
328 			 */
329 			base_pos = ch_tell();
330 			break;
331 		}
332 	}
333 
334 	/*
335 	 * Now scan forwards from the beginning of this line.
336 	 * We keep discarding "printable lines" (based on screen width)
337 	 * until we reach the curr_pos.
338 	 *
339 	 * {{ This algorithm is pretty inefficient if the lines
340 	 *    are much longer than the screen width,
341 	 *    but I don't know of any better way. }}
342 	 */
343 	new_pos = base_pos;
344 	if (ch_seek(new_pos))
345 	{
346 		null_line();
347 		return (NULL_POSITION);
348 	}
349 	endline = FALSE;
350 	prewind();
351 	plinenum(new_pos);
352     loop:
353 	begin_new_pos = new_pos;
354 	(void) ch_seek(new_pos);
355 
356 	do
357 	{
358 		c = ch_forw_get();
359 		if (c == EOI || ABORT_SIGS())
360 		{
361 			null_line();
362 			return (NULL_POSITION);
363 		}
364 		new_pos++;
365 		if (c == '\n')
366 		{
367 			backchars = pflushmbc();
368 			if (backchars > 0 && !chopline && hshift == 0)
369 			{
370 				backchars++;
371 				goto shift;
372 			}
373 			endline = TRUE;
374 			break;
375 		}
376 		backchars = pappend(c, ch_tell()-1);
377 		if (backchars > 0)
378 		{
379 			/*
380 			 * Got a full printable line, but we haven't
381 			 * reached our curr_pos yet.  Discard the line
382 			 * and start a new one.
383 			 */
384 			if (chopline || hshift > 0)
385 			{
386 				endline = TRUE;
387 				quit_if_one_screen = FALSE;
388 				break;
389 			}
390 		shift:
391 			pshift_all();
392 			while (backchars-- > 0)
393 			{
394 				(void) ch_back_get();
395 				new_pos--;
396 			}
397 			goto loop;
398 		}
399 	} while (new_pos < curr_pos);
400 
401 	pdone(endline, ch_forw_get());
402 
403 #if HILITE_SEARCH
404 	if (is_filtered(base_pos))
405 	{
406 		/*
407 		 * We don't want to display this line.
408 		 * Get the previous line.
409 		 */
410 		curr_pos = begin_new_pos;
411 		goto get_back_line;
412 	}
413 
414 	if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
415 		set_status_col('*');
416 #endif
417 
418 	return (begin_new_pos);
419 }
420 
421 /*
422  * Set attnpos.
423  */
424 	public void
425 set_attnpos(pos)
426 	POSITION pos;
427 {
428 	int c;
429 
430 	if (pos != NULL_POSITION)
431 	{
432 		if (ch_seek(pos))
433 			return;
434 		for (;;)
435 		{
436 			c = ch_forw_get();
437 			if (c == EOI)
438 				return;
439 			if (c != '\n' && c != '\r')
440 				break;
441 			pos++;
442 		}
443 	}
444 	start_attnpos = pos;
445 	for (;;)
446 	{
447 		c = ch_forw_get();
448 		pos++;
449 		if (c == EOI || c == '\n' || c == '\r')
450 			break;
451 	}
452 	end_attnpos = pos;
453 }
454