freebsd-skq/contrib/less/output.c
rwatson 29d7b190c2 Currently, less(1) uses K&R prototypes, which both fails to provide useful
compiler-time type checking, and also causes problems for targets where
multiple incompatible calling conventions may be selected based on argument
types.  This change switches less(1) to ANSI prototypes.

While there, we also remove use of "register", and attempt to use "const" a
bit better now that the compiler can check argument types.

Reviewed by:	cem, emaste
MFC after:	3 weeks
Sponsored by:	DARPA, AFRL
Differential Revision:	https://reviews.freebsd.org/D10152
2017-03-31 21:29:43 +00:00

603 lines
11 KiB
C

/*
* Copyright (C) 1984-2015 Mark Nudelman
*
* 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"
#if MSDOS_COMPILER==WIN32C
#include "windows.h"
#endif
public int errmsgs; /* Count of messages displayed by error() */
public int need_clr;
public int final_attr;
public int at_prompt;
extern int 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;
#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
extern int ctldisp;
extern int nm_fg_color, nm_bg_color;
extern int bo_fg_color, bo_bg_color;
extern int ul_fg_color, ul_bg_color;
extern int so_fg_color, so_bg_color;
extern int bl_fg_color, bl_bg_color;
#endif
/*
* Display the line which is in the line buffer.
*/
public 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;
}
final_attr = AT_NORMAL;
for (i = 0; (c = gline(i, &a)) != '\0'; i++)
{
at_switch(a);
final_attr = a;
if (c == '\b')
putbs();
else
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.
*/
public void
flush(void)
{
int n;
int fd;
n = (int) (ob - obuf);
if (n == 0)
return;
#if MSDOS_COMPILER==MSOFTC
if (is_tty && any_display)
{
*ob = '\0';
_outtext(obuf);
ob = obuf;
return;
}
#else
#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
if (is_tty && any_display)
{
*ob = '\0';
if (ctldisp != OPT_ONPLUS)
WIN32textout(obuf, ob - obuf);
else
{
/*
* Look for SGR escape sequences, and convert them
* to color commands. Replace bold, underline,
* and italic escapes into colors specified via
* the -D command-line option.
*/
char *anchor, *p, *p_next;
unsigned char fg, bg;
static unsigned char at;
#if MSDOS_COMPILER==WIN32C
/* Screen colors used by 3x and 4x SGR commands. */
static unsigned char screen_color[] = {
0, /* BLACK */
FOREGROUND_RED,
FOREGROUND_GREEN,
FOREGROUND_RED|FOREGROUND_GREEN,
FOREGROUND_BLUE,
FOREGROUND_BLUE|FOREGROUND_RED,
FOREGROUND_BLUE|FOREGROUND_GREEN,
FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
};
#else
static enum COLORS screen_color[] = {
BLACK, RED, GREEN, BROWN,
BLUE, MAGENTA, CYAN, LIGHTGRAY
};
#endif
for (anchor = p_next = obuf;
(p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
{
p = p_next;
if (p[1] == '[') /* "ESC-[" sequence */
{
if (p > anchor)
{
/*
* If some chars seen since
* the last escape sequence,
* write them out to the screen.
*/
WIN32textout(anchor, p-anchor);
anchor = p;
}
p += 2; /* Skip the "ESC-[" */
if (is_ansi_end(*p))
{
/*
* Handle null escape sequence
* "ESC[m", which restores
* the normal color.
*/
p++;
anchor = p_next = p;
at = 0;
WIN32setcolors(nm_fg_color, nm_bg_color);
continue;
}
p_next = p;
/*
* Select foreground/background colors
* based on the escape sequence.
*/
fg = nm_fg_color;
bg = nm_bg_color;
while (!is_ansi_end(*p))
{
char *q;
long code = strtol(p, &q, 10);
if (*q == '\0')
{
/*
* Incomplete sequence.
* Leave it unprocessed
* in the buffer.
*/
int slop = (int) (q - anchor);
/* {{ strcpy args overlap! }} */
strcpy(obuf, anchor);
ob = &obuf[slop];
return;
}
if (q == p ||
code > 49 || code < 0 ||
(!is_ansi_end(*q) && *q != ';'))
{
p_next = q;
break;
}
if (*q == ';')
q++;
switch (code)
{
default:
/* case 0: all attrs off */
fg = nm_fg_color;
bg = nm_bg_color;
at = 0;
break;
case 1: /* bold on */
at |= 1;
break;
case 3: /* italic on */
case 7: /* inverse on */
at |= 2;
break;
case 4: /* underline on */
at |= 4;
break;
case 5: /* slow blink on */
case 6: /* fast blink on */
at |= 8;
break;
case 8: /* concealed on */
fg = (bg & 7) | 8;
break;
case 22: /* bold off */
at &= ~1;
break;
case 23: /* italic off */
case 27: /* inverse off */
at &= ~2;
break;
case 24: /* underline off */
at &= ~4;
break;
case 30: case 31: case 32:
case 33: case 34: case 35:
case 36: case 37:
fg = (fg & 8) | (screen_color[code - 30]);
break;
case 39: /* default fg */
fg = nm_fg_color;
break;
case 40: case 41: case 42:
case 43: case 44: case 45:
case 46: case 47:
bg = (bg & 8) | (screen_color[code - 40]);
break;
case 49: /* default fg */
bg = nm_bg_color;
break;
}
p = q;
}
if (!is_ansi_end(*p) || p == p_next)
break;
if (at & 1)
{
/*
* If \e[1m use defined bold
* color, else set intensity.
*/
if (p[-2] == '[')
{
#if MSDOS_COMPILER==WIN32C
fg |= FOREGROUND_INTENSITY;
bg |= BACKGROUND_INTENSITY;
#else
fg = bo_fg_color;
bg = bo_bg_color;
#endif
} else
fg |= 8;
} else if (at & 2)
{
fg = so_fg_color;
bg = so_bg_color;
} else if (at & 4)
{
fg = ul_fg_color;
bg = ul_bg_color;
} else if (at & 8)
{
fg = bl_fg_color;
bg = bl_bg_color;
}
fg &= 0xf;
bg &= 0xf;
WIN32setcolors(fg, bg);
p_next = anchor = p + 1;
} else
p_next++;
}
/* Output what's left in the buffer. */
WIN32textout(anchor, ob - anchor);
}
ob = obuf;
return;
}
#endif
#endif
fd = (any_display) ? 1 : 2;
if (write(fd, obuf, n) != n)
screen_trashed = 1;
ob = obuf;
}
/*
* Output a character.
*/
public int
putchr(int c)
{
#if 0 /* fake UTF-8 output for testing */
extern int utf_mode;
if (utf_mode)
{
static char ubuf[MAX_UTF_CHAR_LEN];
static int ubuf_len = 0;
static int ubuf_index = 0;
if (ubuf_len == 0)
{
ubuf_len = utf_len(c);
ubuf_index = 0;
}
ubuf[ubuf_index++] = c;
if (ubuf_index < ubuf_len)
return c;
c = get_wchar(ubuf) & 0xFF;
ubuf_len = 0;
}
#endif
if (need_clr)
{
need_clr = 0;
clear_bot();
}
#if MSDOS_COMPILER
if (c == '\n' && is_tty)
{
/* remove_top(1); */
putchr('\r');
}
#else
#ifdef _OSK
if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */
putchr(0x0A);
#endif
#endif
/*
* 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++ = c;
at_prompt = 0;
return (c);
}
/*
* Output a string.
*/
public void
putstr(constant char *s)
{
while (*s != '\0')
putchr(*s++);
}
/*
* Convert an integral type to a string.
*/
#define TYPE_TO_A_FUNC(funcname, type) \
void funcname(num, buf) \
type num; \
char *buf; \
{ \
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 = '-'; \
strcpy(buf, s); \
}
TYPE_TO_A_FUNC(postoa, POSITION)
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);
putstr(buf);
return ((int) 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);
putstr(buf);
return ((int) strlen(buf));
}
/*
* This function implements printf-like functionality
* using a more portable argument list mechanism than printf's.
*/
static int
less_printf(char *fmt, PARG *parg)
{
char *s;
int col;
col = 0;
while (*fmt != '\0')
{
if (*fmt != '%')
{
putchr(*fmt++);
col++;
} else
{
++fmt;
switch (*fmt++)
{
case 's':
s = parg->p_string;
parg++;
while (*s != '\0')
{
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.
*/
public void
get_return(void)
{
int c;
#if ONLY_RETURN
while ((c = getchr()) != '\n' && c != '\r')
bell();
#else
c = getchr();
if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
ungetcc(c);
#endif
}
/*
* Output a message in the lower left corner of the screen
* and wait for carriage return.
*/
public void
error(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))
{
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.
*/
public void
ierror(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.
*/
public int
query(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))
{
putchr('\n');
return (c);
}
lower_left();
if (col >= sc_width)
screen_trashed = 1;
flush();
return (c);
}