File: [local] / src / usr.bin / less / output.c (download)
Revision 1.12, Fri Nov 6 15:50:33 2015 UTC (8 years, 6 months ago) by nicm
Branch: MAIN
Changes since 1.11: +2 -4 lines
Garrett D'Amore has agreed to moving his copyright line up above the
terms with the existing copyright, so it is clear it applies the same
terms.
|
/*
* 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 volatile sig_atomic_t sigs;
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(void)
{
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)
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();
*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[INT_STRLEN_BOUND(num)+2]; \
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(linenumtoa, LINENUM)
TYPE_TO_A_FUNC(inttoa, int)
/*
* Output an integer in a given radix.
*/
static int
iprint_int(int num)
{
char buf[INT_STRLEN_BOUND(num)];
inttoa(num, buf, sizeof (buf));
putstr(buf);
return (strlen(buf));
}
/*
* Output a line number in a given radix.
*/
static int
iprint_linenum(LINENUM num)
{
char buf[INT_STRLEN_BOUND(num)];
linenumtoa(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();
}
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();
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();
return (c);
}