Edit

IABSD.fr/src/usr.bin/less/input.c

Branch :

  • Show log

    Commit

  • Author : deraadt
    Date : 2019-09-03 23:08:42
    Hash : d65139b4
    Message : 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

  • usr.bin/less/input.c
  • /*
     * 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;
    }