450 lines
8.6 KiB
C
450 lines
8.6 KiB
C
/* $NetBSD: output.c,v 1.2 1998/02/22 14:57:31 christos Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1984,1985,1989,1994,1995,1996 Mark Nudelman
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice in the documentation and/or other materials provided with
|
|
* the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
/*
|
|
* 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;
|
|
|
|
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;
|
|
|
|
static int iprintnum __P((int, int));
|
|
static int iprintf __P((char *, PARG *));
|
|
|
|
/*
|
|
* Display the line which is in the line buffer.
|
|
*/
|
|
public void
|
|
put_line()
|
|
{
|
|
register int c;
|
|
register int i;
|
|
int a;
|
|
int curr_attr;
|
|
|
|
if (ABORT_SIGS())
|
|
{
|
|
/*
|
|
* Don't output if a signal is pending.
|
|
*/
|
|
screen_trashed = 1;
|
|
return;
|
|
}
|
|
|
|
curr_attr = AT_NORMAL;
|
|
|
|
for (i = 0; (c = gline(i, &a)) != '\0'; i++)
|
|
{
|
|
if (a != curr_attr)
|
|
{
|
|
/*
|
|
* Changing attributes.
|
|
* Display the exit sequence for the old attribute
|
|
* and the enter sequence for the new one.
|
|
*/
|
|
switch (curr_attr)
|
|
{
|
|
case AT_UNDERLINE: ul_exit(); break;
|
|
case AT_BOLD: bo_exit(); break;
|
|
case AT_BLINK: bl_exit(); break;
|
|
case AT_STANDOUT: so_exit(); break;
|
|
}
|
|
switch (a)
|
|
{
|
|
case AT_UNDERLINE: ul_enter(); break;
|
|
case AT_BOLD: bo_enter(); break;
|
|
case AT_BLINK: bl_enter(); break;
|
|
case AT_STANDOUT: so_enter(); break;
|
|
}
|
|
curr_attr = a;
|
|
}
|
|
if (curr_attr == AT_INVIS)
|
|
continue;
|
|
if (c == '\b')
|
|
putbs();
|
|
else
|
|
putchr(c);
|
|
}
|
|
|
|
switch (curr_attr)
|
|
{
|
|
case AT_UNDERLINE: ul_exit(); break;
|
|
case AT_BOLD: bo_exit(); break;
|
|
case AT_BLINK: bl_exit(); break;
|
|
case AT_STANDOUT: so_exit(); break;
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
register int n;
|
|
register int fd;
|
|
|
|
n = ob - obuf;
|
|
if (n == 0)
|
|
return;
|
|
#if MSDOS_COMPILER==WIN32C
|
|
if (is_tty && any_display)
|
|
{
|
|
char *p;
|
|
DWORD nwritten = 0;
|
|
CONSOLE_SCREEN_BUFFER_INFO scr;
|
|
DWORD nchars;
|
|
COORD cpos;
|
|
extern HANDLE con_out;
|
|
|
|
*ob = '\0';
|
|
GetConsoleScreenBufferInfo(con_out, &scr);
|
|
if (scr.dwCursorPosition.Y != scr.srWindow.Bottom ||
|
|
(p = strchr(obuf, '\n')) == NULL)
|
|
{
|
|
WriteConsole(con_out, obuf, strlen(obuf),
|
|
&nwritten, NULL);
|
|
} else
|
|
{
|
|
/*
|
|
* To avoid color problems, if we're writing a
|
|
* newline at the bottom of the screen, we write
|
|
* only up to the newline, then fill the bottom
|
|
* line with the correct attribute, then write
|
|
* the rest. When Windows-95 scrolls, it takes the
|
|
* attributes for the new line from the first char
|
|
* of the (previously) bottom line.
|
|
*/
|
|
WriteConsole(con_out, obuf, p - obuf + 1,
|
|
&nwritten, NULL);
|
|
cpos.X = 0;
|
|
cpos.Y = scr.dwCursorPosition.Y;
|
|
FillConsoleOutputAttribute(con_out, scr.wAttributes,
|
|
sc_width, cpos, &nchars);
|
|
WriteConsole(con_out, p + 1, strlen(p + 1),
|
|
&nwritten, NULL);
|
|
}
|
|
ob = obuf;
|
|
return;
|
|
}
|
|
|
|
#else
|
|
#if MSDOS_COMPILER==MSOFTC
|
|
if (is_tty && any_display)
|
|
{
|
|
*ob = '\0';
|
|
_outtext(obuf);
|
|
ob = obuf;
|
|
return;
|
|
}
|
|
#else
|
|
#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
|
|
if (is_tty && any_display)
|
|
{
|
|
*ob = '\0';
|
|
cputs(obuf);
|
|
ob = obuf;
|
|
return;
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
fd = (any_display) ? 1 : 2;
|
|
if (write(fd, obuf, n) != n)
|
|
screen_trashed = 1;
|
|
ob = obuf;
|
|
}
|
|
|
|
/*
|
|
* Output a character.
|
|
*/
|
|
public void
|
|
putchr(c)
|
|
int c;
|
|
{
|
|
if (need_clr)
|
|
{
|
|
need_clr = 0;
|
|
clear_bot();
|
|
}
|
|
#if MSDOS_COMPILER
|
|
if (c == '\n' && is_tty)
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Output a string.
|
|
*/
|
|
public void
|
|
putstr(s)
|
|
register char *s;
|
|
{
|
|
while (*s != '\0')
|
|
putchr(*s++);
|
|
}
|
|
|
|
|
|
/*
|
|
* Output an integer in a given radix.
|
|
*/
|
|
static int
|
|
iprintnum(num, radix)
|
|
int num;
|
|
int radix;
|
|
{
|
|
register char *s;
|
|
int r;
|
|
int neg;
|
|
char buf[10];
|
|
|
|
neg = (num < 0);
|
|
if (neg)
|
|
num = -num;
|
|
|
|
s = buf;
|
|
do
|
|
{
|
|
*s++ = (num % radix) + '0';
|
|
} while ((num /= radix) != 0);
|
|
|
|
if (neg)
|
|
*s++ = '-';
|
|
r = s - buf;
|
|
|
|
while (s > buf)
|
|
putchr(*--s);
|
|
return (r);
|
|
}
|
|
|
|
/*
|
|
* This function implements printf-like functionality
|
|
* using a more portable argument list mechanism than printf's.
|
|
*/
|
|
static int
|
|
iprintf(fmt, parg)
|
|
register char *fmt;
|
|
PARG *parg;
|
|
{
|
|
register char *s;
|
|
register int n;
|
|
register 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':
|
|
n = parg->p_int;
|
|
parg++;
|
|
col += iprintnum(n, 10);
|
|
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()
|
|
{
|
|
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(fmt, parg)
|
|
char *fmt;
|
|
PARG *parg;
|
|
{
|
|
int col = 0;
|
|
static char return_to_continue[] = " (press RETURN)";
|
|
|
|
errmsgs++;
|
|
|
|
if (any_display && is_tty)
|
|
{
|
|
clear_bot();
|
|
so_enter();
|
|
col += so_s_width;
|
|
}
|
|
|
|
col += iprintf(fmt, parg);
|
|
|
|
if (!(any_display && is_tty))
|
|
{
|
|
putchr('\n');
|
|
return;
|
|
}
|
|
|
|
putstr(return_to_continue);
|
|
so_exit();
|
|
col += sizeof(return_to_continue) + so_e_width;
|
|
|
|
get_return();
|
|
lower_left();
|
|
|
|
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(fmt, parg)
|
|
char *fmt;
|
|
PARG *parg;
|
|
{
|
|
clear_bot();
|
|
so_enter();
|
|
(void) iprintf(fmt, parg);
|
|
putstr(intr_to_abort);
|
|
so_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(fmt, parg)
|
|
char *fmt;
|
|
PARG *parg;
|
|
{
|
|
register int c;
|
|
int col = 0;
|
|
|
|
if (any_display && is_tty)
|
|
clear_bot();
|
|
|
|
(void) iprintf(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);
|
|
}
|