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