mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-27 11:55:06 +00:00
1ea316270f
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
603 lines
11 KiB
C
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);
|
|
}
|