input.c revision 225736
1275970Scy/*
2275970Scy * Copyright (C) 1984-2011  Mark Nudelman
3275970Scy *
4275970Scy * You may distribute under the terms of either the GNU General Public
5275970Scy * License or the Less License, as specified in the README file.
6275970Scy *
7275970Scy * For more information about less, or for information on how to
8275970Scy * contact the author, see the README file.
9338531Sdelphij */
10275970Scy
11275970Scy
12275970Scy/*
13275970Scy * High level routines dealing with getting lines of input
14275970Scy * from the file being viewed.
15275970Scy *
16275970Scy * When we speak of "lines" here, we mean PRINTABLE lines;
17275970Scy * lines processed with respect to the screen width.
18275970Scy * We use the term "raw line" to refer to lines simply
19275970Scy * delimited by newlines; not processed with respect to screen width.
20275970Scy */
21275970Scy
22275970Scy#include "less.h"
23275970Scy
24275970Scyextern int squeeze;
25275970Scyextern int chopline;
26275970Scyextern int hshift;
27275970Scyextern int quit_if_one_screen;
28275970Scyextern int sigs;
29275970Scyextern int ignore_eoi;
30275970Scyextern int status_col;
31275970Scyextern POSITION start_attnpos;
32275970Scyextern POSITION end_attnpos;
33275970Scy#if HILITE_SEARCH
34275970Scyextern int hilite_search;
35275970Scyextern int size_linebuf;
36275970Scy#endif
37275970Scy
38275970Scy/*
39275970Scy * Get the next line.
40275970Scy * A "current" position is passed and a "new" position is returned.
41275970Scy * The current position is the position of the first character of
42275970Scy * a line.  The new position is the position of the first character
43275970Scy * of the NEXT line.  The line obtained is the line starting at curr_pos.
44275970Scy */
45275970Scy	public POSITION
46275970Scyforw_line(curr_pos)
47275970Scy	POSITION curr_pos;
48275970Scy{
49275970Scy	POSITION base_pos;
50275970Scy	POSITION new_pos;
51275970Scy	register int c;
52275970Scy	int blankline;
53275970Scy	int endline;
54275970Scy	int backchars;
55275970Scy
56275970Scyget_forw_line:
57275970Scy	if (curr_pos == NULL_POSITION)
58275970Scy	{
59275970Scy		null_line();
60275970Scy		return (NULL_POSITION);
61275970Scy	}
62275970Scy#if HILITE_SEARCH
63275970Scy	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
64338531Sdelphij		/*
65275970Scy		 * If we are ignoring EOI (command F), only prepare
66275970Scy		 * one line ahead, to avoid getting stuck waiting for
67275970Scy		 * slow data without displaying the data we already have.
68275970Scy		 * If we're not ignoring EOI, we *could* do the same, but
69275970Scy		 * for efficiency we prepare several lines ahead at once.
70275970Scy		 */
71275970Scy		prep_hilite(curr_pos, curr_pos + 3*size_linebuf,
72275970Scy				ignore_eoi ? 1 : -1);
73275970Scy#endif
74275970Scy	if (ch_seek(curr_pos))
75275970Scy	{
76275970Scy		null_line();
77275970Scy		return (NULL_POSITION);
78275970Scy	}
79275970Scy
80275970Scy	/*
81275970Scy	 * Step back to the beginning of the line.
82275970Scy	 */
83275970Scy	base_pos = curr_pos;
84275970Scy	for (;;)
85275970Scy	{
86275970Scy		if (ABORT_SIGS())
87275970Scy		{
88275970Scy			null_line();
89275970Scy			return (NULL_POSITION);
90275970Scy		}
91275970Scy		c = ch_back_get();
92275970Scy		if (c == EOI)
93275970Scy			break;
94275970Scy		if (c == '\n')
95275970Scy		{
96275970Scy			(void) ch_forw_get();
97275970Scy			break;
98275970Scy		}
99275970Scy		--base_pos;
100275970Scy	}
101275970Scy
102275970Scy	/*
103275970Scy	 * Read forward again to the position we should start at.
104275970Scy	 */
105275970Scy 	prewind();
106275970Scy	plinenum(base_pos);
107275970Scy	(void) ch_seek(base_pos);
108275970Scy	new_pos = base_pos;
109275970Scy	while (new_pos < curr_pos)
110275970Scy	{
111275970Scy		if (ABORT_SIGS())
112275970Scy		{
113275970Scy			null_line();
114275970Scy			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