summaryrefslogtreecommitdiff
path: root/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'output.c')
-rwxr-xr-xoutput.c609
1 files changed, 609 insertions, 0 deletions
diff --git a/output.c b/output.c
new file mode 100755
index 0000000..bcc8471
--- /dev/null
+++ b/output.c
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 1984-2012 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()
+{
+ register int c;
+ register 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()
+{
+ register int n;
+ register int fd;
+
+ n = 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 = 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] == '[')
+ {
+ fg = bo_fg_color;
+ bg = bo_bg_color;
+ } 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(c)
+ 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(s)
+ register 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]; \
+ register 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(num)
+ int num;
+{
+ char buf[INT_STRLEN_BOUND(num)];
+
+ inttoa(num, buf);
+ putstr(buf);
+ return (strlen(buf));
+}
+
+/*
+ * Output a line number in a given radix.
+ */
+ static int
+iprint_linenum(num)
+ LINENUM num;
+{
+ char buf[INT_STRLEN_BOUND(num)];
+
+ linenumtoa(num, 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(fmt, parg)
+ register char *fmt;
+ PARG *parg;
+{
+ register char *s;
+ 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':
+ 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()
+{
+ 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)
+ {
+ 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(fmt, parg)
+ 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(fmt, parg)
+ char *fmt;
+ PARG *parg;
+{
+ register 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);
+}