[BACK]Return to input.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

File: [local] / src / usr.bin / less / input.c (download)

Revision 1.11, Tue Sep 3 23:08:42 2019 UTC (4 years, 8 months ago) by deraadt
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, HEAD
Changes since 1.10: +8 -9 lines

less uses a correct raceless signal method of indicating signal events in
a volatile sig_atomic_t variable, and then processing events in the mainloop.
But only one variable was used for 3 signals, with |= bit operations which
are signal interruptable!  Rewrite the code to use 3 independent variables
and cleanup how the mainloop observes indications.
ok schwarze

/*
 * Copyright (C) 1984-2012  Mark Nudelman
 * Modified for use with illumos by Garrett D'Amore.
 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
 *
 * You may distribute under the terms of either the GNU General Public
 * License or the Less License, as specified in the README file.
 *
 * For more information, see the README file.
 */

/*
 * High level routines dealing with getting lines of input
 * from the file being viewed.
 *
 * When we speak of "lines" here, we mean PRINTABLE lines;
 * lines processed with respect to the screen width.
 * We use the term "raw line" to refer to lines simply
 * delimited by newlines; not processed with respect to screen width.
 */

#include "less.h"

extern int squeeze;
extern int chopline;
extern int hshift;
extern int quit_if_one_screen;
extern int ignore_eoi;
extern int status_col;
extern off_t start_attnpos;
extern off_t end_attnpos;
extern int hilite_search;
extern int size_linebuf;

/*
 * Get the next line.
 * A "current" position is passed and a "new" position is returned.
 * The current position is the position of the first character of
 * a line.  The new position is the position of the first character
 * of the NEXT line.  The line obtained is the line starting at curr_pos.
 */
off_t
forw_line(off_t curr_pos)
{
	off_t base_pos;
	off_t new_pos;
	int c;
	int blankline;
	int endline;
	int backchars;

get_forw_line:
	if (curr_pos == -1) {
		null_line();
		return (-1);
	}
	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
		/*
		 * If we are ignoring EOI (command F), only prepare
		 * one line ahead, to avoid getting stuck waiting for
		 * slow data without displaying the data we already have.
		 * If we're not ignoring EOI, we *could* do the same, but
		 * for efficiency we prepare several lines ahead at once.
		 */
		prep_hilite(curr_pos, curr_pos + 3*size_linebuf,
		    ignore_eoi ? 1 : -1);
	if (ch_seek(curr_pos)) {
		null_line();
		return (-1);
	}

	/*
	 * Step back to the beginning of the line.
	 */
	base_pos = curr_pos;
	for (;;) {
		if (abort_sigs()) {
			null_line();
			return (-1);
		}
		c = ch_back_get();
		if (c == EOI)
			break;
		if (c == '\n') {
			(void) ch_forw_get();
			break;
		}
		--base_pos;
	}

	/*
	 * Read forward again to the position we should start at.
	 */
	prewind();
	plinenum(base_pos);
	(void) ch_seek(base_pos);
	new_pos = base_pos;
	while (new_pos < curr_pos) {
		if (abort_sigs()) {
			null_line();
			return (-1);
		}
		c = ch_forw_get();
		backchars = pappend(c, new_pos);
		new_pos++;
		if (backchars > 0) {
			pshift_all();
			new_pos -= backchars;
			while (--backchars >= 0)
				(void) ch_back_get();
		}
	}
	(void) pflushmbc();
	pshift_all();

	/*
	 * Read the first character to display.
	 */
	c = ch_forw_get();
	if (c == EOI) {
		null_line();
		return (-1);
	}
	blankline = (c == '\n' || c == '\r');

	/*
	 * Read each character in the line and append to the line buffer.
	 */
	for (;;) {
		if (abort_sigs()) {
			null_line();
			return (-1);
		}
		if (c == '\n' || c == EOI) {
			/*
			 * End of the line.
			 */
			backchars = pflushmbc();
			new_pos = ch_tell();
			if (backchars > 0 && !chopline && hshift == 0) {
				new_pos -= backchars + 1;
				endline = FALSE;
			} else
				endline = TRUE;
			break;
		}
		if (c != '\r')
			blankline = 0;

		/*
		 * Append the char to the line and get the next char.
		 */
		backchars = pappend(c, ch_tell()-1);
		if (backchars > 0) {
			/*
			 * The char won't fit in the line; the line
			 * is too long to print in the screen width.
			 * End the line here.
			 */
			if (chopline || hshift > 0) {
				do {
					if (abort_sigs()) {
						null_line();
						return (-1);
					}
					c = ch_forw_get();
				} while (c != '\n' && c != EOI);
				new_pos = ch_tell();
				endline = TRUE;
				quit_if_one_screen = FALSE;
			} else {
				new_pos = ch_tell() - backchars;
				endline = FALSE;
			}
			break;
		}
		c = ch_forw_get();
	}

	pdone(endline, 1);

	if (is_filtered(base_pos)) {
		/*
		 * We don't want to display this line.
		 * Get the next line.
		 */
		curr_pos = new_pos;
		goto get_forw_line;
	}

	if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
		set_status_col('*');

	if (squeeze && blankline) {
		/*
		 * This line is blank.
		 * Skip down to the last contiguous blank line
		 * and pretend it is the one which we are returning.
		 */
		while ((c = ch_forw_get()) == '\n' || c == '\r')
			if (abort_sigs()) {
				null_line();
				return (-1);
			}
		if (c != EOI)
			(void) ch_back_get();
		new_pos = ch_tell();
	}

	return (new_pos);
}

/*
 * Get the previous line.
 * A "current" position is passed and a "new" position is returned.
 * The current position is the position of the first character of
 * a line.  The new position is the position of the first character
 * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
 */
off_t
back_line(off_t curr_pos)
{
	off_t new_pos, begin_new_pos, base_pos;
	int c;
	int endline;
	int backchars;

get_back_line:
	if (curr_pos == -1 || curr_pos <= ch_zero()) {
		null_line();
		return (-1);
	}
	if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
		prep_hilite((curr_pos < 3*size_linebuf) ?
		    0 : curr_pos - 3*size_linebuf, curr_pos, -1);
	if (ch_seek(curr_pos-1)) {
		null_line();
		return (-1);
	}

	if (squeeze) {
		/*
		 * Find out if the "current" line was blank.
		 */
		(void) ch_forw_get();	/* Skip the newline */
		c = ch_forw_get();	/* First char of "current" line */
		(void) ch_back_get();	/* Restore our position */
		(void) ch_back_get();

		if (c == '\n' || c == '\r') {
			/*
			 * The "current" line was blank.
			 * Skip over any preceding blank lines,
			 * since we skipped them in forw_line().
			 */
			while ((c = ch_back_get()) == '\n' || c == '\r')
				if (abort_sigs()) {
					null_line();
					return (-1);
				}
			if (c == EOI) {
				null_line();
				return (-1);
			}
			(void) ch_forw_get();
		}
	}

	/*
	 * Scan backwards until we hit the beginning of the line.
	 */
	for (;;) {
		if (abort_sigs()) {
			null_line();
			return (-1);
		}
		c = ch_back_get();
		if (c == '\n') {
			/*
			 * This is the newline ending the previous line.
			 * We have hit the beginning of the line.
			 */
			base_pos = ch_tell() + 1;
			break;
		}
		if (c == EOI) {
			/*
			 * We have hit the beginning of the file.
			 * This must be the first line in the file.
			 * This must, of course, be the beginning of the line.
			 */
			base_pos = ch_tell();
			break;
		}
	}

	/*
	 * Now scan forwards from the beginning of this line.
	 * We keep discarding "printable lines" (based on screen width)
	 * until we reach the curr_pos.
	 *
	 * {{ This algorithm is pretty inefficient if the lines
	 *    are much longer than the screen width,
	 *    but I don't know of any better way. }}
	 */
	new_pos = base_pos;
	if (ch_seek(new_pos)) {
		null_line();
		return (-1);
	}
	endline = FALSE;
	prewind();
	plinenum(new_pos);
loop:
	begin_new_pos = new_pos;
	(void) ch_seek(new_pos);

	do {
		c = ch_forw_get();
		if (c == EOI || abort_sigs()) {
			null_line();
			return (-1);
		}
		new_pos++;
		if (c == '\n') {
			backchars = pflushmbc();
			if (backchars > 0 && !chopline && hshift == 0) {
				backchars++;
				goto shift;
			}
			endline = TRUE;
			break;
		}
		backchars = pappend(c, ch_tell()-1);
		if (backchars > 0) {
			/*
			 * Got a full printable line, but we haven't
			 * reached our curr_pos yet.  Discard the line
			 * and start a new one.
			 */
			if (chopline || hshift > 0) {
				endline = TRUE;
				quit_if_one_screen = FALSE;
				break;
			}
		shift:
			pshift_all();
			while (backchars-- > 0) {
				(void) ch_back_get();
				new_pos--;
			}
			goto loop;
		}
	} while (new_pos < curr_pos);

	pdone(endline, 0);

	if (is_filtered(base_pos)) {
		/*
		 * We don't want to display this line.
		 * Get the previous line.
		 */
		curr_pos = begin_new_pos;
		goto get_back_line;
	}

	if (status_col && curr_pos > 0 &&
	    is_hilited(base_pos, curr_pos-1, 1, NULL))
		set_status_col('*');

	return (begin_new_pos);
}

/*
 * Set attnpos.
 */
void
set_attnpos(off_t pos)
{
	int c;

	if (pos != -1) {
		if (ch_seek(pos))
			return;
		for (;;) {
			c = ch_forw_get();
			if (c == EOI)
				return;
			if (c != '\n' && c != '\r')
				break;
			pos++;
		}
	}
	start_attnpos = pos;
	for (;;) {
		c = ch_forw_get();
		pos++;
		if (c == EOI || c == '\n' || c == '\r')
			break;
	}
	end_attnpos = pos;
}