=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/less/output.c,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- src/usr.bin/less/output.c 2001/11/19 19:02:14 1.4 +++ src/usr.bin/less/output.c 2003/04/13 18:26:26 1.5 @@ -1,29 +1,11 @@ -/* $OpenBSD: output.c,v 1.4 2001/11/19 19:02:14 mpech Exp $ */ - /* - * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman - * All rights reserved. + * Copyright (C) 1984-2002 Mark Nudelman * - * 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. + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. * - * 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. + * For more information about less, or for information on how to + * contact the author, see the README file. */ @@ -32,24 +14,38 @@ */ #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; 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; +#if 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() { - int c; - int i; + register int c; + register int i; int a; int curr_attr; @@ -104,9 +100,10 @@ case AT_BLINK: bl_exit(); break; case AT_STANDOUT: so_exit(); break; } + final_attr = curr_attr; } -static char obuf[1024]; +static char obuf[OUTBUF_SIZE]; static char *ob = obuf; /* @@ -128,22 +125,233 @@ public void flush() { - int n; - int fd; + register int n; + register int fd; -#if MSOFTC - *ob = '\0'; - _outtext(obuf); - ob = obuf; -#else n = ob - obuf; if (n == 0) return; +#if MSDOS_COMPILER==WIN32C + if (is_tty && any_display) + { + char *op; + DWORD nwritten = 0; + CONSOLE_SCREEN_BUFFER_INFO scr; + int row; + int col; + int olen; + extern HANDLE con_out; + + olen = ob - obuf; + /* + * There is a bug in Win32 WriteConsole() if we're + * writing in the last cell with a different color. + * To avoid color problems in the bottom line, + * we scroll the screen manually, before writing. + */ + GetConsoleScreenBufferInfo(con_out, &scr); + col = scr.dwCursorPosition.X; + row = scr.dwCursorPosition.Y; + for (op = obuf; op < obuf + olen; op++) + { + if (*op == '\n') + { + col = 0; + row++; + } else if (*op == '\r') + { + col = 0; + } else + { + col++; + if (col >= sc_width) + { + col = 0; + row++; + } + } + } + if (row > scr.srWindow.Bottom) + win32_scroll_up(row - scr.srWindow.Bottom); + WriteConsole(con_out, obuf, olen, &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'; + if (ctldisp != OPT_ONPLUS) + cputs(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; + int buflen = ob - obuf; + unsigned char fg, bg, norm_attr; + /* + * Only dark colors mentioned here, so that + * bold has visible effect. + */ + static enum COLORS screen_color[] = { + BLACK, RED, GREEN, BROWN, + BLUE, MAGENTA, CYAN, LIGHTGRAY + }; + + /* Normal text colors are used as baseline. */ + bg = nm_bg_color & 0xf; + fg = nm_fg_color & 0xf; + norm_attr = (bg << 4) | fg; + for (anchor = p_next = obuf; + (p_next = memchr (p_next, ESC, + buflen - (p_next - obuf))) + != NULL; ) + { + p = p_next; + + /* + * Handle the null escape sequence + * (ESC-[m), which is used to restore + * the original color. + */ + if (p[1] == '[' && is_ansi_end(p[2])) + { + textattr(norm_attr); + p += 3; + anchor = p_next = p; + continue; + } + + if (p[1] == '[') /* "Esc-[" sequence */ + { + /* + * If some chars seen since + * the last escape sequence, + * write it out to the screen + * using current text attributes. + */ + if (p > anchor) + { + *p = '\0'; + cputs (anchor); + *p = ESC; + anchor = p; + } + p += 2; + p_next = p; + while (!is_ansi_end(*p)) + { + char *q; + long code = strtol(p, &q, 10); + + if (!*q) + { + /* + * Incomplete sequence. + * Leave it unprocessed + * in the buffer. + */ + int slop = q - anchor; + strlcpy(obuf, anchor, + sizeof(obuf)); + 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) + { + case 1: /* bold on */ + fg = bo_fg_color; + bg = bo_bg_color; + break; + case 3: /* italic on */ + fg = so_fg_color; + bg = so_bg_color; + break; + case 4: /* underline on */ + fg = ul_fg_color; + bg = ul_bg_color; + break; + case 8: /* concealed on */ + fg = (bg & 7) | 8; + break; + case 0: /* all attrs off */ + case 22:/* bold off */ + case 23:/* italic off */ + case 24:/* underline off */ + fg = nm_fg_color; + bg = nm_bg_color; + 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) + { + bg &= 15; + fg &= 15; + textattr ((bg << 4)| fg); + p_next = anchor = p + 1; + } else + break; + } else + p_next++; + } + + /* Output what's left in the buffer. */ + cputs (anchor); + } + ob = obuf; + return; + } +#endif +#endif +#endif fd = (any_display) ? 1 : 2; if (write(fd, obuf, n) != n) screen_trashed = 1; ob = obuf; -#endif } /* @@ -153,17 +361,29 @@ putchr(c) int c; { - if (ob >= &obuf[sizeof(obuf)]) - flush(); if (need_clr) { need_clr = 0; clear_bot(); } -#if MSOFTC - if (c == '\n') +#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; return (c); } @@ -173,7 +393,7 @@ */ public void putstr(s) - char *s; + register char *s; { while (*s != '\0') putchr(*s++); @@ -181,34 +401,56 @@ /* + * Convert an integral type to a string. + */ +#define TYPE_TO_A_FUNC(funcname, type) \ +void funcname(num, buf, len) \ + type num; \ + char *buf; \ + size_t len; \ +{ \ + int neg = (num < 0); \ + char tbuf[INT_STRLEN_BOUND(num)+2]; \ + register char *s = tbuf + sizeof(tbuf); \ + if (neg) num = -num; \ + *--s = '\0'; \ + do { \ + *--s = (num % 10) + '0'; \ + } while ((num /= 10) != 0); \ + if (neg) *--s = '-'; \ + strlcpy(buf, s, len); \ +} + +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 -iprintnum(num, radix) +iprint_int(num) int num; - int radix; { - char *s; - int r; - int neg; - char buf[10]; + char buf[INT_STRLEN_BOUND(num)]; - if (neg = (num < 0)) - num = -num; + inttoa(num, buf, sizeof(buf)); + putstr(buf); + return (strlen(buf)); +} - s = buf; - do - { - *s++ = (num % radix) + '0'; - } while ((num /= radix) != 0); +/* + * Output a line number in a given radix. + */ + static int +iprint_linenum(num) + LINENUM num; +{ + char buf[INT_STRLEN_BOUND(num)]; - if (neg) - *s++ = '-'; - r = s - buf; - - while (s > buf) - putchr(*--s); - return (r); + linenumtoa(num, buf, sizeof(buf)); + putstr(buf); + return (strlen(buf)); } /* @@ -216,13 +458,12 @@ * using a more portable argument list mechanism than printf's. */ static int -iprintf(fmt, parg) - char *fmt; +less_printf(fmt, parg) + register char *fmt; PARG *parg; { - char *s; - int n; - int col; + register char *s; + register int col; col = 0; while (*fmt != '\0') @@ -234,7 +475,8 @@ } else { ++fmt; - switch (*fmt++) { + switch (*fmt++) + { case 's': s = parg->p_string; parg++; @@ -245,10 +487,13 @@ } break; case 'd': - n = parg->p_int; + col += iprint_int(parg->p_int); parg++; - col += iprintnum(n, 10); break; + case 'n': + col += iprint_linenum(parg->p_linenum); + parg++; + break; } } } @@ -256,6 +501,28 @@ } /* + * 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 == 'q') + quit(QUIT_OK); + 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. */ @@ -264,22 +531,21 @@ char *fmt; PARG *parg; { - int c; int col = 0; static char return_to_continue[] = " (press RETURN)"; errmsgs++; - if (any_display) + if (any_display && is_tty) { clear_bot(); so_enter(); col += so_s_width; } - col += iprintf(fmt, parg); + col += less_printf(fmt, parg); - if (!any_display) + if (!(any_display && is_tty)) { putchr('\n'); return; @@ -289,16 +555,7 @@ so_exit(); col += sizeof(return_to_continue) + so_e_width; -#if ONLY_RETURN - while ((c = getchr()) != '\n' && c != '\r') - bell(); -#else - c = getchr(); - if (c == 'q') - quit(QUIT_OK); - if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) - ungetcc(c); -#endif + get_return(); lower_left(); if (col >= sc_width) @@ -327,7 +584,7 @@ { clear_bot(); so_enter(); - (void) iprintf(fmt, parg); + (void) less_printf(fmt, parg); putstr(intr_to_abort); so_exit(); flush(); @@ -343,16 +600,16 @@ char *fmt; PARG *parg; { - int c; + register int c; int col = 0; - if (any_display) + if (any_display && is_tty) clear_bot(); - (void) iprintf(fmt, parg); + (void) less_printf(fmt, parg); c = getchr(); - if (!any_display) + if (!(any_display && is_tty)) { putchr('\n'); return (c);