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