input.c revision 221715
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
24extern int squeeze;
25extern int chopline;
26extern int hshift;
27extern int quit_if_one_screen;
28extern int sigs;
29extern int ignore_eoi;
30extern int status_col;
31extern POSITION start_attnpos;
32extern POSITION end_attnpos;
33#if HILITE_SEARCH
34extern int hilite_search;
35extern 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
46forw_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
56get_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
250back_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
258get_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
430set_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