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

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

Revision 1.18, 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.17: +1 -2 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 the output to the screen.
 */

#include "less.h"

int errmsgs;	/* Count of messages displayed by error() */

extern int sc_width;
extern int so_s_width, so_e_width;
extern int screen_trashed;
extern int any_display;
extern int is_tty;
extern int oldbot;

static int need_clr;

/*
 * Display the line which is in the line buffer.
 */
void
put_line(void)
{
	int c;
	int i;
	int a;

	if (abort_sigs()) {
		/*
		 * Don't output if a signal is pending.
		 */
		screen_trashed = 1;
		return;
	}

	for (i = 0; (c = gline(i, &a)) != '\0'; i++) {
		at_switch(a);
		if (c == '\b')
			putbs();
		else
			(void) putchr(c);
	}

	at_exit();
}

static char obuf[OUTBUF_SIZE];
static char *ob = obuf;

/*
 * Flush buffered output.
 *
 * If we haven't displayed any file data yet,
 * output messages on error output (file descriptor 2),
 * otherwise output on standard output (file descriptor 1).
 *
 * This has the desirable effect of producing all
 * error messages on error output if standard output
 * is directed to a file.  It also does the same if
 * we never produce any real output; for example, if
 * the input file(s) cannot be opened.  If we do
 * eventually produce output, code in edit() makes
 * sure these messages can be seen before they are
 * overwritten or scrolled away.
 */
void
flush(int ignore_errors)
{
	int n;
	int fd;
	ssize_t nwritten;

	n = (intptr_t)ob - (intptr_t)obuf;
	if (n == 0)
		return;

	fd = (any_display) ? STDOUT_FILENO : STDERR_FILENO;
	nwritten = write(fd, obuf, n);
	if (nwritten != n) {
		if (nwritten == -1 && !ignore_errors)
			quit(QUIT_ERROR);
		screen_trashed = 1;
	}
	ob = obuf;
}

/*
 * Output a character.
 */
int
putchr(int c)
{
	if (need_clr) {
		need_clr = 0;
		clear_bot();
	}
	/*
	 * Some versions of flush() write to *ob, so we must flush
	 * when we are still one char from the end of obuf.
	 */
	if (ob >= &obuf[sizeof (obuf)-1])
		flush(0);
	*ob++ = (char)c;
	return (c);
}

/*
 * Output a string.
 */
void
putstr(const char *s)
{
	while (*s != '\0')
		(void) putchr(*s++);
}


/*
 * Convert an integral type to a string.
 */
#define	TYPE_TO_A_FUNC(funcname, type)		\
void						\
funcname(type num, char *buf, size_t len)	\
{						\
	int neg = (num < 0);			\
	char tbuf[23];	\
	char *s = tbuf + sizeof (tbuf);		\
	if (neg)				\
		num = -num;			\
	*--s = '\0';				\
	do {					\
		*--s = (num % 10) + '0';	\
	} while ((num /= 10) != 0);		\
	if (neg)				\
		 *--s = '-';			\
	(void) strlcpy(buf, s, len);		\
}

TYPE_TO_A_FUNC(postoa, off_t)
TYPE_TO_A_FUNC(inttoa, int)

/*
 * Output an integer in a given radix.
 */
static int
iprint_int(int num)
{
	char buf[11];

	inttoa(num, buf, sizeof (buf));
	putstr(buf);
	return (strlen(buf));
}

/*
 * Output a line number in a given radix.
 */
static int
iprint_linenum(off_t num)
{
	char buf[21];

	postoa(num, buf, sizeof(buf));
	putstr(buf);
	return (strlen(buf));
}

/*
 * This function implements printf-like functionality
 * using a more portable argument list mechanism than printf's.
 */
static int
less_printf(const char *fmt, PARG *parg)
{
	char *s;
	int col;

	col = 0;
	while (*fmt != '\0') {
		if (*fmt != '%') {
			(void) putchr(*fmt++);
			col++;
		} else {
			++fmt;
			switch (*fmt++) {
			case 's':
				s = parg->p_string;
				parg++;
				while (*s != '\0') {
					(void) putchr(*s++);
					col++;
				}
				break;
			case 'd':
				col += iprint_int(parg->p_int);
				parg++;
				break;
			case 'n':
				col += iprint_linenum(parg->p_linenum);
				parg++;
				break;
			}
		}
	}
	return (col);
}

/*
 * Get a RETURN.
 * If some other non-trivial char is pressed, unget it, so it will
 * become the next command.
 */
void
get_return(void)
{
	int c;

	c = getchr();
	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
		ungetcc(c);
}

/*
 * Output a message in the lower left corner of the screen
 * and wait for carriage return.
 */
void
error(const char *fmt, PARG *parg)
{
	int col = 0;
	static char return_to_continue[] = "  (press RETURN)";

	errmsgs++;

	if (any_display && is_tty) {
		if (!oldbot)
			squish_check();
		at_exit();
		clear_bot();
		at_enter(AT_STANDOUT);
		col += so_s_width;
	}

	col += less_printf(fmt, parg);

	if (!(any_display && is_tty)) {
		(void) putchr('\n');
		return;
	}

	putstr(return_to_continue);
	at_exit();
	col += sizeof (return_to_continue) + so_e_width;

	get_return();
	lower_left();
	clear_eol();

	if (col >= sc_width)
		/*
		 * Printing the message has probably scrolled the screen.
		 * {{ Unless the terminal doesn't have auto margins,
		 *    in which case we just hammered on the right margin. }}
		 */
		screen_trashed = 1;

	flush(0);
}

static char intr_to_abort[] = "... (interrupt to abort)";

/*
 * Output a message in the lower left corner of the screen
 * and don't wait for carriage return.
 * Usually used to warn that we are beginning a potentially
 * time-consuming operation.
 */
void
ierror(const char *fmt, PARG *parg)
{
	at_exit();
	clear_bot();
	at_enter(AT_STANDOUT);
	(void) less_printf(fmt, parg);
	putstr(intr_to_abort);
	at_exit();
	flush(0);
	need_clr = 1;
}

/*
 * Output a message in the lower left corner of the screen
 * and return a single-character response.
 */
int
query(const char *fmt, PARG *parg)
{
	int c;
	int col = 0;

	if (any_display && is_tty)
		clear_bot();

	(void) less_printf(fmt, parg);
	c = getchr();

	if (!(any_display && is_tty)) {
		(void) putchr('\n');
		return (c);
	}

	lower_left();
	if (col >= sc_width)
		screen_trashed = 1;
	flush(0);

	return (c);
}