input.c revision 330571
1/*
2 * Copyright (C) 1984-2017  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, see the README file.
8 */
9
10/*
11 * High level routines dealing with getting lines of input
12 * from the file being viewed.
13 *
14 * When we speak of "lines" here, we mean PRINTABLE lines;
15 * lines processed with respect to the screen width.
16 * We use the term "raw line" to refer to lines simply
17 * delimited by newlines; not processed with respect to screen width.
18 */
19
20#include "less.h"
21
22extern int squeeze;
23extern int chopline;
24extern int hshift;
25extern int quit_if_one_screen;
26extern int sigs;
27extern int ignore_eoi;
28extern int status_col;
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 base_pos;
48	POSITION new_pos;
49	int c;
50	int blankline;
51	int endline;
52	int chopped;
53	int backchars;
54
55get_forw_line:
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 || is_filtering() || status_col)
63	{
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		curr_pos = next_unfiltered(curr_pos);
74	}
75#endif
76	if (ch_seek(curr_pos))
77	{
78		null_line();
79		return (NULL_POSITION);
80	}
81
82	/*
83	 * Step back to the beginning of the line.
84	 */
85	base_pos = curr_pos;
86	for (;;)
87	{
88		if (ABORT_SIGS())
89		{
90			null_line();
91			return (NULL_POSITION);
92		}
93		c = ch_back_get();
94		if (c == EOI)
95			break;
96		if (c == '\n')
97		{
98			(void) ch_forw_get();
99			break;
100		}
101		--base_pos;
102	}
103
104	/*
105	 * Read forward again to the position we should start at.
106	 */
107 	prewind();
108	plinenum(base_pos);
109	(void) ch_seek(base_pos);
110	new_pos = base_pos;
111	while (new_pos < curr_pos)
112	{
113		if (ABORT_SIGS())
114		{
115			null_line();
116			return (NULL_POSITION);
117		}
118		c = ch_forw_get();
119		backchars = pappend(c, new_pos);
120		new_pos++;
121		if (backchars > 0)
122		{
123			pshift_all();
124			new_pos -= backchars;
125			while (--backchars >= 0)
126				(void) ch_back_get();
127		}
128	}
129	(void) pflushmbc();
130	pshift_all();
131
132	/*
133	 * Read the first character to display.
134	 */
135	c = ch_forw_get();
136	if (c == EOI)
137	{
138		null_line();
139		return (NULL_POSITION);
140	}
141	blankline = (c == '\n' || c == '\r');
142
143	/*
144	 * Read each character in the line and append to the line buffer.
145	 */
146	chopped = FALSE;
147	for (;;)
148	{
149		if (ABORT_SIGS())
150		{
151			null_line();
152			return (NULL_POSITION);
153		}
154		if (c == '\n' || c == EOI)
155		{
156			/*
157			 * End of the line.
158			 */
159			backchars = pflushmbc();
160			new_pos = ch_tell();
161			if (backchars > 0 && !chopline && hshift == 0)
162			{
163				new_pos -= backchars + 1;
164				endline = FALSE;
165			} else
166				endline = TRUE;
167			break;
168		}
169		if (c != '\r')
170			blankline = 0;
171
172		/*
173		 * Append the char to the line and get the next char.
174		 */
175		backchars = pappend(c, ch_tell()-1);
176		if (backchars > 0)
177		{
178			/*
179			 * The char won't fit in the line; the line
180			 * is too long to print in the screen width.
181			 * End the line here.
182			 */
183			if (chopline || hshift > 0)
184			{
185				do
186				{
187					if (ABORT_SIGS())
188					{
189						null_line();
190						return (NULL_POSITION);
191					}
192					c = ch_forw_get();
193				} while (c != '\n' && c != EOI);
194				new_pos = ch_tell();
195				endline = TRUE;
196				quit_if_one_screen = FALSE;
197				chopped = TRUE;
198			} else
199			{
200				new_pos = ch_tell() - backchars;
201				endline = FALSE;
202			}
203			break;
204		}
205		c = ch_forw_get();
206	}
207
208	pdone(endline, chopped, 1);
209
210#if HILITE_SEARCH
211	if (is_filtered(base_pos))
212	{
213		/*
214		 * We don't want to display this line.
215		 * Get the next line.
216		 */
217		curr_pos = new_pos;
218		goto get_forw_line;
219	}
220
221	if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
222		set_status_col('*');
223#endif
224
225	if (squeeze && blankline)
226	{
227		/*
228		 * This line is blank.
229		 * Skip down to the last contiguous blank line
230		 * and pretend it is the one which we are returning.
231		 */
232		while ((c = ch_forw_get()) == '\n' || c == '\r')
233			if (ABORT_SIGS())
234			{
235				null_line();
236				return (NULL_POSITION);
237			}
238		if (c != EOI)
239			(void) ch_back_get();
240		new_pos = ch_tell();
241	}
242
243	return (new_pos);
244}
245
246/*
247 * Get the previous line.
248 * A "current" position is passed and a "new" position is returned.
249 * The current position is the position of the first character of
250 * a line.  The new position is the position of the first character
251 * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
252 */
253	public POSITION
254back_line(curr_pos)
255	POSITION curr_pos;
256{
257	POSITION new_pos, begin_new_pos, base_pos;
258	int c;
259	int endline;
260	int chopped;
261	int backchars;
262
263get_back_line:
264	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
265	{
266		null_line();
267		return (NULL_POSITION);
268	}
269#if HILITE_SEARCH
270	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
271		prep_hilite((curr_pos < 3*size_linebuf) ?
272				0 : curr_pos - 3*size_linebuf, curr_pos, -1);
273#endif
274	if (ch_seek(curr_pos-1))
275	{
276		null_line();
277		return (NULL_POSITION);
278	}
279
280	if (squeeze)
281	{
282		/*
283		 * Find out if the "current" line was blank.
284		 */
285		(void) ch_forw_get();    /* Skip the newline */
286		c = ch_forw_get();       /* First char of "current" line */
287		(void) ch_back_get();    /* Restore our position */
288		(void) ch_back_get();
289
290		if (c == '\n' || c == '\r')
291		{
292			/*
293			 * The "current" line was blank.
294			 * Skip over any preceding blank lines,
295			 * since we skipped them in forw_line().
296			 */
297			while ((c = ch_back_get()) == '\n' || c == '\r')
298				if (ABORT_SIGS())
299				{
300					null_line();
301					return (NULL_POSITION);
302				}
303			if (c == EOI)
304			{
305				null_line();
306				return (NULL_POSITION);
307			}
308			(void) ch_forw_get();
309		}
310	}
311
312	/*
313	 * Scan backwards until we hit the beginning of the line.
314	 */
315	for (;;)
316	{
317		if (ABORT_SIGS())
318		{
319			null_line();
320			return (NULL_POSITION);
321		}
322		c = ch_back_get();
323		if (c == '\n')
324		{
325			/*
326			 * This is the newline ending the previous line.
327			 * We have hit the beginning of the line.
328			 */
329			base_pos = ch_tell() + 1;
330			break;
331		}
332		if (c == EOI)
333		{
334			/*
335			 * We have hit the beginning of the file.
336			 * This must be the first line in the file.
337			 * This must, of course, be the beginning of the line.
338			 */
339			base_pos = ch_tell();
340			break;
341		}
342	}
343
344	/*
345	 * Now scan forwards from the beginning of this line.
346	 * We keep discarding "printable lines" (based on screen width)
347	 * until we reach the curr_pos.
348	 *
349	 * {{ This algorithm is pretty inefficient if the lines
350	 *    are much longer than the screen width,
351	 *    but I don't know of any better way. }}
352	 */
353	new_pos = base_pos;
354	if (ch_seek(new_pos))
355	{
356		null_line();
357		return (NULL_POSITION);
358	}
359	endline = FALSE;
360	prewind();
361	plinenum(new_pos);
362    loop:
363	begin_new_pos = new_pos;
364	(void) ch_seek(new_pos);
365	chopped = FALSE;
366
367	do
368	{
369		c = ch_forw_get();
370		if (c == EOI || ABORT_SIGS())
371		{
372			null_line();
373			return (NULL_POSITION);
374		}
375		new_pos++;
376		if (c == '\n')
377		{
378			backchars = pflushmbc();
379			if (backchars > 0 && !chopline && hshift == 0)
380			{
381				backchars++;
382				goto shift;
383			}
384			endline = TRUE;
385			break;
386		}
387		backchars = pappend(c, ch_tell()-1);
388		if (backchars > 0)
389		{
390			/*
391			 * Got a full printable line, but we haven't
392			 * reached our curr_pos yet.  Discard the line
393			 * and start a new one.
394			 */
395			if (chopline || hshift > 0)
396			{
397				endline = TRUE;
398				chopped = TRUE;
399				quit_if_one_screen = FALSE;
400				break;
401			}
402		shift:
403			pshift_all();
404			while (backchars-- > 0)
405			{
406				(void) ch_back_get();
407				new_pos--;
408			}
409			goto loop;
410		}
411	} while (new_pos < curr_pos);
412
413	pdone(endline, chopped, 0);
414
415#if HILITE_SEARCH
416	if (is_filtered(base_pos))
417	{
418		/*
419		 * We don't want to display this line.
420		 * Get the previous line.
421		 */
422		curr_pos = begin_new_pos;
423		goto get_back_line;
424	}
425
426	if (status_col && curr_pos > 0 && is_hilited(base_pos, curr_pos-1, 1, NULL))
427		set_status_col('*');
428#endif
429
430	return (begin_new_pos);
431}
432
433/*
434 * Set attnpos.
435 */
436	public void
437set_attnpos(pos)
438	POSITION pos;
439{
440	int c;
441
442	if (pos != NULL_POSITION)
443	{
444		if (ch_seek(pos))
445			return;
446		for (;;)
447		{
448			c = ch_forw_get();
449			if (c == EOI)
450				break;
451			if (c == '\n' || c == '\r')
452			{
453				(void) ch_back_get();
454				break;
455			}
456			pos++;
457		}
458		end_attnpos = pos;
459		for (;;)
460		{
461			c = ch_back_get();
462			if (c == EOI || c == '\n' || c == '\r')
463				break;
464			pos--;
465		}
466	}
467	start_attnpos = pos;
468}
469