input.c revision 195941
1/*
2 * Copyright (C) 1984-2009  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					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, 1);
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
245back_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
253get_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, 0);
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
425set_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