input.c revision 60786
1/*
2 * Copyright (C) 1984-2000  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 quit_if_one_screen;
27extern int sigs;
28extern int ignore_eoi;
29extern POSITION start_attnpos;
30extern POSITION end_attnpos;
31#if HILITE_SEARCH
32extern int hilite_search;
33extern int size_linebuf;
34#endif
35
36/*
37 * Get the next line.
38 * A "current" position is passed and a "new" position is returned.
39 * The current position is the position of the first character of
40 * a line.  The new position is the position of the first character
41 * of the NEXT line.  The line obtained is the line starting at curr_pos.
42 */
43	public POSITION
44forw_line(curr_pos)
45	POSITION curr_pos;
46{
47	POSITION new_pos;
48	register int c;
49	int blankline;
50	int endline;
51
52	if (curr_pos == NULL_POSITION)
53	{
54		null_line();
55		return (NULL_POSITION);
56	}
57#if HILITE_SEARCH
58	if (hilite_search == OPT_ONPLUS)
59		/*
60		 * If we are ignoring EOI (command F), only prepare
61		 * one line ahead, to avoid getting stuck waiting for
62		 * slow data without displaying the data we already have.
63		 * If we're not ignoring EOI, we *could* do the same, but
64		 * for efficiency we prepare several lines ahead at once.
65		 */
66		prep_hilite(curr_pos, curr_pos + 3*size_linebuf,
67				ignore_eoi ? 1 : -1);
68#endif
69	if (ch_seek(curr_pos))
70	{
71		null_line();
72		return (NULL_POSITION);
73	}
74
75	prewind();
76	plinenum(curr_pos);
77	(void) ch_seek(curr_pos);
78
79	c = ch_forw_get();
80	if (c == EOI)
81	{
82		null_line();
83		return (NULL_POSITION);
84	}
85	blankline = (c == '\n' || c == '\r');
86
87	for (;;)
88	{
89		if (ABORT_SIGS())
90		{
91			null_line();
92			return (NULL_POSITION);
93		}
94		if (c == '\n' || c == EOI)
95		{
96			/*
97			 * End of the line.
98			 */
99			new_pos = ch_tell();
100			endline = TRUE;
101			break;
102		}
103
104		/*
105		 * Append the char to the line and get the next char.
106		 */
107		if (pappend(c, ch_tell()-1))
108		{
109			/*
110			 * The char won't fit in the line; the line
111			 * is too long to print in the screen width.
112			 * End the line here.
113			 */
114			if (chopline)
115			{
116				do
117				{
118					c = ch_forw_get();
119				} while (c != '\n' && c != EOI);
120				new_pos = ch_tell();
121				endline = TRUE;
122				quit_if_one_screen = FALSE;
123			} else
124			{
125				new_pos = ch_tell() - 1;
126				endline = FALSE;
127			}
128			break;
129		}
130		c = ch_forw_get();
131	}
132	pdone(endline);
133
134	if (squeeze && blankline)
135	{
136		/*
137		 * This line is blank.
138		 * Skip down to the last contiguous blank line
139		 * and pretend it is the one which we are returning.
140		 */
141		while ((c = ch_forw_get()) == '\n' || c == '\r')
142			if (ABORT_SIGS())
143			{
144				null_line();
145				return (NULL_POSITION);
146			}
147		if (c != EOI)
148			(void) ch_back_get();
149		new_pos = ch_tell();
150	}
151
152	return (new_pos);
153}
154
155/*
156 * Get the previous line.
157 * A "current" position is passed and a "new" position is returned.
158 * The current position is the position of the first character of
159 * a line.  The new position is the position of the first character
160 * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
161 */
162	public POSITION
163back_line(curr_pos)
164	POSITION curr_pos;
165{
166	POSITION new_pos, begin_new_pos;
167	int c;
168	int endline;
169
170	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
171	{
172		null_line();
173		return (NULL_POSITION);
174	}
175#if HILITE_SEARCH
176	if (hilite_search == OPT_ONPLUS)
177		prep_hilite((curr_pos < 3*size_linebuf) ?
178				0 : curr_pos - 3*size_linebuf, curr_pos, -1);
179#endif
180	if (ch_seek(curr_pos-1))
181	{
182		null_line();
183		return (NULL_POSITION);
184	}
185
186	if (squeeze)
187	{
188		/*
189		 * Find out if the "current" line was blank.
190		 */
191		(void) ch_forw_get();	/* Skip the newline */
192		c = ch_forw_get();	/* First char of "current" line */
193		(void) ch_back_get();	/* Restore our position */
194		(void) ch_back_get();
195
196		if (c == '\n' || c == '\r')
197		{
198			/*
199			 * The "current" line was blank.
200			 * Skip over any preceding blank lines,
201			 * since we skipped them in forw_line().
202			 */
203			while ((c = ch_back_get()) == '\n' || c == '\r')
204				if (ABORT_SIGS())
205				{
206					null_line();
207					return (NULL_POSITION);
208				}
209			if (c == EOI)
210			{
211				null_line();
212				return (NULL_POSITION);
213			}
214			(void) ch_forw_get();
215		}
216	}
217
218	/*
219	 * Scan backwards until we hit the beginning of the line.
220	 */
221	for (;;)
222	{
223		if (ABORT_SIGS())
224		{
225			null_line();
226			return (NULL_POSITION);
227		}
228		c = ch_back_get();
229		if (c == '\n')
230		{
231			/*
232			 * This is the newline ending the previous line.
233			 * We have hit the beginning of the line.
234			 */
235			new_pos = ch_tell() + 1;
236			break;
237		}
238		if (c == EOI)
239		{
240			/*
241			 * We have hit the beginning of the file.
242			 * This must be the first line in the file.
243			 * This must, of course, be the beginning of the line.
244			 */
245			new_pos = ch_tell();
246			break;
247		}
248	}
249
250	/*
251	 * Now scan forwards from the beginning of this line.
252	 * We keep discarding "printable lines" (based on screen width)
253	 * until we reach the curr_pos.
254	 *
255	 * {{ This algorithm is pretty inefficient if the lines
256	 *    are much longer than the screen width,
257	 *    but I don't know of any better way. }}
258	 */
259	if (ch_seek(new_pos))
260	{
261		null_line();
262		return (NULL_POSITION);
263	}
264	endline = FALSE;
265    loop:
266	begin_new_pos = new_pos;
267	prewind();
268	plinenum(new_pos);
269	(void) ch_seek(new_pos);
270
271	do
272	{
273		c = ch_forw_get();
274		if (c == EOI || ABORT_SIGS())
275		{
276			null_line();
277			return (NULL_POSITION);
278		}
279		new_pos++;
280		if (c == '\n')
281		{
282			endline = TRUE;
283			break;
284		}
285		if (pappend(c, ch_tell()-1))
286		{
287			/*
288			 * Got a full printable line, but we haven't
289			 * reached our curr_pos yet.  Discard the line
290			 * and start a new one.
291			 */
292			if (chopline)
293			{
294				endline = TRUE;
295				quit_if_one_screen = FALSE;
296				break;
297			}
298			pdone(0);
299			(void) ch_back_get();
300			new_pos--;
301			goto loop;
302		}
303	} while (new_pos < curr_pos);
304
305	pdone(endline);
306
307	return (begin_new_pos);
308}
309
310/*
311 * Set attnpos.
312 */
313	public void
314set_attnpos(pos)
315	POSITION pos;
316{
317	int c;
318
319	if (pos != NULL_POSITION)
320	{
321		if (ch_seek(pos))
322			return;
323		for (;;)
324		{
325			c = ch_forw_get();
326			if (c == EOI)
327				return;
328			if (c != '\n' && c != '\r')
329				break;
330			pos++;
331		}
332	}
333	start_attnpos = pos;
334	for (;;)
335	{
336		c = ch_forw_get();
337		pos++;
338		if (c == EOI || c == '\n' || c == '\r')
339			break;
340	}
341	end_attnpos = pos;
342}
343