input.c revision 170257
11558Srgrimes/*
21558Srgrimes * Copyright (C) 1984-2007  Mark Nudelman
31558Srgrimes *
41558Srgrimes * You may distribute under the terms of either the GNU General Public
51558Srgrimes * License or the Less License, as specified in the README file.
61558Srgrimes *
71558Srgrimes * For more information about less, or for information on how to
81558Srgrimes * contact the author, see the README file.
91558Srgrimes */
101558Srgrimes
111558Srgrimes
121558Srgrimes/*
131558Srgrimes * High level routines dealing with getting lines of input
141558Srgrimes * from the file being viewed.
151558Srgrimes *
161558Srgrimes * When we speak of "lines" here, we mean PRINTABLE lines;
171558Srgrimes * lines processed with respect to the screen width.
181558Srgrimes * We use the term "raw line" to refer to lines simply
191558Srgrimes * delimited by newlines; not processed with respect to screen width.
201558Srgrimes */
211558Srgrimes
221558Srgrimes#include "less.h"
231558Srgrimes
241558Srgrimesextern int squeeze;
251558Srgrimesextern int chopline;
261558Srgrimesextern int hshift;
271558Srgrimesextern int quit_if_one_screen;
281558Srgrimesextern int sigs;
291558Srgrimesextern int ignore_eoi;
301558Srgrimesextern int status_col;
311558Srgrimesextern POSITION start_attnpos;
321558Srgrimesextern POSITION end_attnpos;
331558Srgrimes#if HILITE_SEARCH
341558Srgrimesextern int hilite_search;
3541477Sjulianextern int size_linebuf;
3623675Speter#endif
3741477Sjulian
3841477Sjulian/*
3950476Speter * Get the next line.
401558Srgrimes * A "current" position is passed and a "new" position is returned.
411558Srgrimes * The current position is the position of the first character of
421558Srgrimes * a line.  The new position is the position of the first character
431558Srgrimes * of the NEXT line.  The line obtained is the line starting at curr_pos.
441558Srgrimes */
451558Srgrimes	public POSITION
461558Srgrimesforw_line(curr_pos)
4723675Speter	POSITION curr_pos;
4823675Speter{
4923675Speter	POSITION base_pos;
5023675Speter	POSITION new_pos;
5123675Speter	register int c;
5223675Speter	int blankline;
531558Srgrimes	int endline;
541558Srgrimes	int backchars;
5523675Speter
561558Srgrimes	if (curr_pos == NULL_POSITION)
571558Srgrimes	{
581558Srgrimes		null_line();
591558Srgrimes		return (NULL_POSITION);
601558Srgrimes	}
611558Srgrimes#if HILITE_SEARCH
6223675Speter	if (hilite_search == OPT_ONPLUS || status_col)
6323675Speter		/*
6423675Speter		 * If we are ignoring EOI (command F), only prepare
6523675Speter		 * one line ahead, to avoid getting stuck waiting for
661558Srgrimes		 * slow data without displaying the data we already have.
6723675Speter		 * If we're not ignoring EOI, we *could* do the same, but
6823675Speter		 * for efficiency we prepare several lines ahead at once.
6923675Speter		 */
7023675Speter		prep_hilite(curr_pos, curr_pos + 3*size_linebuf,
7123675Speter				ignore_eoi ? 1 : -1);
727585Sbde#endif
731558Srgrimes	if (ch_seek(curr_pos))
741558Srgrimes	{
751558Srgrimes		null_line();
761558Srgrimes		return (NULL_POSITION);
7723675Speter	}
781558Srgrimes
791558Srgrimes	base_pos = curr_pos;
801558Srgrimes	for (;;)
811558Srgrimes	{
821558Srgrimes		if (ABORT_SIGS())
831558Srgrimes		{
841558Srgrimes			null_line();
8541474Sjulian			return (NULL_POSITION);
861558Srgrimes		}
871558Srgrimes		c = ch_back_get();
881558Srgrimes		if (c == EOI)
891558Srgrimes			break;
9053781Sphk		if (c == '\n')
9153781Sphk		{
9253781Sphk			(void) ch_forw_get();
931558Srgrimes			break;
941558Srgrimes		}
951558Srgrimes		--base_pos;
961558Srgrimes	}
971558Srgrimes
981558Srgrimes 	prewind();
991558Srgrimes	plinenum(base_pos);
1001558Srgrimes	(void) ch_seek(base_pos);
1011558Srgrimes	while (base_pos < curr_pos)
1021558Srgrimes	{
1031558Srgrimes		if (ABORT_SIGS())
1041558Srgrimes		{
1051558Srgrimes			null_line();
1061558Srgrimes			return (NULL_POSITION);
1071558Srgrimes		}
1081558Srgrimes		c = ch_forw_get();
1091558Srgrimes		backchars = pappend(c, base_pos);
1101558Srgrimes		base_pos++;
1111558Srgrimes		if (backchars > 0)
1121558Srgrimes		{
1131558Srgrimes			pshift_all();
1141558Srgrimes			base_pos -= backchars;
1151558Srgrimes			while (--backchars >= 0)
1161558Srgrimes				(void) ch_back_get();
11723675Speter		}
11841474Sjulian	}
1191558Srgrimes	(void) pflushmbc();
1201558Srgrimes	pshift_all();
1211558Srgrimes
1221558Srgrimes	c = ch_forw_get();
1231558Srgrimes	if (c == EOI)
1241558Srgrimes	{
1251558Srgrimes		null_line();
12623675Speter		return (NULL_POSITION);
1271558Srgrimes	}
1281558Srgrimes	blankline = (c == '\n' || c == '\r');
1291558Srgrimes
1301558Srgrimes	for (;;)
1311558Srgrimes	{
1321558Srgrimes		if (ABORT_SIGS())
1331558Srgrimes		{
1341558Srgrimes			null_line();
1351558Srgrimes			return (NULL_POSITION);
1361558Srgrimes		}
1371558Srgrimes		if (c == '\n' || c == EOI)
1381558Srgrimes		{
1391558Srgrimes			/*
1401558Srgrimes			 * End of the line.
1411558Srgrimes			 */
1421558Srgrimes			backchars = pflushmbc();
1431558Srgrimes			new_pos = ch_tell();
1446280Sbde			if (backchars > 0 && !chopline && hshift == 0)
1451558Srgrimes			{
1461558Srgrimes				new_pos -= backchars + 1;
1471558Srgrimes				endline = FALSE;
1486280Sbde			} else
1491558Srgrimes				endline = TRUE;
15041474Sjulian			break;
15141474Sjulian		}
15241474Sjulian		if (c != '\r')
15341474Sjulian			blankline = 0;
1541558Srgrimes
1551558Srgrimes		/*
1561558Srgrimes		 * Append the char to the line and get the next char.
1571558Srgrimes		 */
1581558Srgrimes		backchars = pappend(c, ch_tell()-1);
1591558Srgrimes		if (backchars > 0)
1601558Srgrimes		{
1611558Srgrimes			/*
1621558Srgrimes			 * The char won't fit in the line; the line
1631558Srgrimes			 * is too long to print in the screen width.
1641558Srgrimes			 * End the line here.
1651558Srgrimes			 */
1661558Srgrimes			if (chopline || hshift > 0)
1671558Srgrimes			{
1681558Srgrimes				do
1691558Srgrimes				{
1701558Srgrimes					c = ch_forw_get();
1711558Srgrimes				} while (c != '\n' && c != EOI);
1721558Srgrimes				new_pos = ch_tell();
1731558Srgrimes				endline = TRUE;
1748871Srgrimes				quit_if_one_screen = FALSE;
1751558Srgrimes			} else
1761558Srgrimes			{
1771558Srgrimes				new_pos = ch_tell() - backchars;
1781558Srgrimes				endline = FALSE;
1791558Srgrimes			}
1801558Srgrimes			break;
1811558Srgrimes		}
1821558Srgrimes		c = ch_forw_get();
1831558Srgrimes	}
1841558Srgrimes	pdone(endline);
1851558Srgrimes
1868871Srgrimes	if (squeeze && blankline)
1871558Srgrimes	{
1881558Srgrimes		/*
1891558Srgrimes		 * This line is blank.
1901558Srgrimes		 * Skip down to the last contiguous blank line
1911558Srgrimes		 * and pretend it is the one which we are returning.
1921558Srgrimes		 */
1931558Srgrimes		while ((c = ch_forw_get()) == '\n' || c == '\r')
1941558Srgrimes			if (ABORT_SIGS())
1951558Srgrimes			{
1961558Srgrimes				null_line();
1971558Srgrimes				return (NULL_POSITION);
1981558Srgrimes			}
1991558Srgrimes		if (c != EOI)
2001558Srgrimes			(void) ch_back_get();
2011558Srgrimes		new_pos = ch_tell();
2021558Srgrimes	}
2031558Srgrimes
2041558Srgrimes	return (new_pos);
2051558Srgrimes}
2061558Srgrimes
2071558Srgrimes/*
2081558Srgrimes * Get the previous line.
2091558Srgrimes * A "current" position is passed and a "new" position is returned.
2101558Srgrimes * The current position is the position of the first character of
2111558Srgrimes * a line.  The new position is the position of the first character
2121558Srgrimes * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
2131558Srgrimes */
2141558Srgrimes	public POSITION
2151558Srgrimesback_line(curr_pos)
2161558Srgrimes	POSITION curr_pos;
2171558Srgrimes{
2181558Srgrimes	POSITION new_pos, begin_new_pos;
2191558Srgrimes	int c;
2201558Srgrimes	int endline;
2211558Srgrimes	int backchars;
2221558Srgrimes
2231558Srgrimes	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
2241558Srgrimes	{
2251558Srgrimes		null_line();
2261558Srgrimes		return (NULL_POSITION);
2271558Srgrimes	}
2281558Srgrimes#if HILITE_SEARCH
2291558Srgrimes	if (hilite_search == OPT_ONPLUS || status_col)
2301558Srgrimes		prep_hilite((curr_pos < 3*size_linebuf) ?
2311558Srgrimes				0 : curr_pos - 3*size_linebuf, curr_pos, -1);
2321558Srgrimes#endif
2331558Srgrimes	if (ch_seek(curr_pos-1))
2341558Srgrimes	{
2351558Srgrimes		null_line();
2361558Srgrimes		return (NULL_POSITION);
2371558Srgrimes	}
2381558Srgrimes
2391558Srgrimes	if (squeeze)
24023675Speter	{
2411558Srgrimes		/*
24223675Speter		 * Find out if the "current" line was blank.
2431558Srgrimes		 */
2441558Srgrimes		(void) ch_forw_get();	/* Skip the newline */
2451558Srgrimes		c = ch_forw_get();	/* First char of "current" line */
2461558Srgrimes		(void) ch_back_get();	/* Restore our position */
2471558Srgrimes		(void) ch_back_get();
24823675Speter
24923675Speter		if (c == '\n' || c == '\r')
2501558Srgrimes		{
2511558Srgrimes			/*
2521558Srgrimes			 * The "current" line was blank.
2531558Srgrimes			 * Skip over any preceding blank lines,
2541558Srgrimes			 * since we skipped them in forw_line().
2551558Srgrimes			 */
2561558Srgrimes			while ((c = ch_back_get()) == '\n' || c == '\r')
2571558Srgrimes				if (ABORT_SIGS())
2581558Srgrimes				{
2591558Srgrimes					null_line();
2601558Srgrimes					return (NULL_POSITION);
2611558Srgrimes				}
2621558Srgrimes			if (c == EOI)
2631558Srgrimes			{
26434266Sjulian				null_line();
26534266Sjulian				return (NULL_POSITION);
26623675Speter			}
26734266Sjulian			(void) ch_forw_get();
2681558Srgrimes		}
2691558Srgrimes	}
2701558Srgrimes
2711558Srgrimes	/*
2721558Srgrimes	 * Scan backwards until we hit the beginning of the line.
2731558Srgrimes	 */
2741558Srgrimes	for (;;)
2751558Srgrimes	{
2761558Srgrimes		if (ABORT_SIGS())
2771558Srgrimes		{
2781558Srgrimes			null_line();
2791558Srgrimes			return (NULL_POSITION);
2801558Srgrimes		}
28141474Sjulian		c = ch_back_get();
28241474Sjulian		if (c == '\n')
28341474Sjulian		{
28441474Sjulian			/*
28541474Sjulian			 * This is the newline ending the previous line.
2861558Srgrimes			 * We have hit the beginning of the line.
2871558Srgrimes			 */
2881558Srgrimes			new_pos = ch_tell() + 1;
28934033Sphk			break;
29034033Sphk		}
29134033Sphk		if (c == EOI)
29234033Sphk		{
2931558Srgrimes			/*
2941558Srgrimes			 * We have hit the beginning of the file.
2951558Srgrimes			 * This must be the first line in the file.
2961558Srgrimes			 * This must, of course, be the beginning of the line.
2971558Srgrimes			 */
2981558Srgrimes			new_pos = ch_tell();
2991558Srgrimes			break;
3008871Srgrimes		}
3011558Srgrimes	}
3021558Srgrimes
3031558Srgrimes	/*
3041558Srgrimes	 * Now scan forwards from the beginning of this line.
30534266Sjulian	 * We keep discarding "printable lines" (based on screen width)
30634266Sjulian	 * until we reach the curr_pos.
30734266Sjulian	 *
30834266Sjulian	 * {{ This algorithm is pretty inefficient if the lines
3091558Srgrimes	 *    are much longer than the screen width,
3101558Srgrimes	 *    but I don't know of any better way. }}
3111558Srgrimes	 */
31223675Speter	if (ch_seek(new_pos))
3131558Srgrimes	{
3141558Srgrimes		null_line();
3151558Srgrimes		return (NULL_POSITION);
3161558Srgrimes	}
3171558Srgrimes	endline = FALSE;
3181558Srgrimes	prewind();
3197585Sbde	plinenum(new_pos);
3201558Srgrimes    loop:
3211558Srgrimes	begin_new_pos = new_pos;
3221558Srgrimes	(void) ch_seek(new_pos);
32323675Speter
3241558Srgrimes	do
3251558Srgrimes	{
3261558Srgrimes		c = ch_forw_get();
3271558Srgrimes		if (c == EOI || ABORT_SIGS())
3281558Srgrimes		{
3291558Srgrimes			null_line();
3301558Srgrimes			return (NULL_POSITION);
3311558Srgrimes		}
3321558Srgrimes		new_pos++;
3331558Srgrimes		if (c == '\n')
3341558Srgrimes		{
3351558Srgrimes			backchars = pflushmbc();
3361558Srgrimes			if (backchars > 0 && !chopline && hshift == 0)
3371558Srgrimes			{
3381558Srgrimes				backchars++;
3391558Srgrimes				goto shift;
3401558Srgrimes			}
3411558Srgrimes			endline = TRUE;
3421558Srgrimes			break;
3431558Srgrimes		}
3441558Srgrimes		backchars = pappend(c, ch_tell()-1);
3451558Srgrimes		if (backchars > 0)
3461558Srgrimes		{
3471558Srgrimes			/*
3481558Srgrimes			 * Got a full printable line, but we haven't
3491558Srgrimes			 * reached our curr_pos yet.  Discard the line
3501558Srgrimes			 * and start a new one.
3511558Srgrimes			 */
3521558Srgrimes			if (chopline || hshift > 0)
3531558Srgrimes			{
3541558Srgrimes				endline = TRUE;
3551558Srgrimes				quit_if_one_screen = FALSE;
3561558Srgrimes				break;
3571558Srgrimes			}
3581558Srgrimes		shift:
3591558Srgrimes			pshift_all();
3601558Srgrimes			while (backchars-- > 0)
3611558Srgrimes			{
3621558Srgrimes				(void) ch_back_get();
36323675Speter				new_pos--;
36423675Speter			}
3651558Srgrimes			goto loop;
3661558Srgrimes		}
3671558Srgrimes	} while (new_pos < curr_pos);
3681558Srgrimes
3691558Srgrimes	pdone(endline);
3701558Srgrimes
3711558Srgrimes	return (begin_new_pos);
3721558Srgrimes}
3731558Srgrimes
3741558Srgrimes/*
3751558Srgrimes * Set attnpos.
3761558Srgrimes */
37723675Speter	public void
37823675Speterset_attnpos(pos)
37923675Speter	POSITION pos;
38023675Speter{
38123675Speter	int c;
3821558Srgrimes
3831558Srgrimes	if (pos != NULL_POSITION)
3841558Srgrimes	{
3851558Srgrimes		if (ch_seek(pos))
3861558Srgrimes			return;
3871558Srgrimes		for (;;)
3881558Srgrimes		{
38923675Speter			c = ch_forw_get();
3901558Srgrimes			if (c == EOI)
3911558Srgrimes				return;
3921558Srgrimes			if (c != '\n' && c != '\r')
3931558Srgrimes				break;
39423675Speter			pos++;
39523675Speter		}
39623675Speter	}
39723675Speter	start_attnpos = pos;
39823675Speter	for (;;)
39923675Speter	{
40023675Speter		c = ch_forw_get();
40123675Speter		pos++;
40223675Speter		if (c == EOI || c == '\n' || c == '\r')
40323675Speter			break;
40423675Speter	}
40537236Sbde	end_attnpos = pos;
40637236Sbde}
40723675Speter