/*
- * Copyright (C) 1984-2012 Mark Nudelman
+ * Copyright (C) 1984-2022 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.
*/
-
/*
* Routines to manipulate the "line buffer".
* The line buffer holds a line of output as it is being built
#include "less.h"
#include "charset.h"
+#include "position.h"
+
+#if MSDOS_COMPILER==WIN32C
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
-static char *linebuf = NULL; /* Buffer which holds the current output line */
-static char *attr = NULL; /* Extension of linebuf to hold attributes */
-public int size_linebuf = 0; /* Size of line buffer (and attr buffer) */
+#define MAX_PFX_WIDTH (MAX_LINENUM_WIDTH + MAX_STATUSCOL_WIDTH + 1)
+static struct {
+ char *buf; /* Buffer which holds the current output line */
+ int *attr; /* Parallel to buf, to hold attributes */
+ int print; /* Index in buf of first printable char */
+ int end; /* Number of chars in buf */
+ char pfx[MAX_PFX_WIDTH]; /* Holds status column and line number */
+ int pfx_attr[MAX_PFX_WIDTH];
+ int pfx_end; /* Number of chars in pfx */
+} linebuf;
-static int cshift; /* Current left-shift of output line buffer */
-public int hshift; /* Desired left-shift of output line buffer */
+/*
+ * Buffer of ansi sequences which have been shifted off the left edge
+ * of the screen.
+ */
+struct xbuffer shifted_ansi;
+
+/*
+ * Ring buffer of last ansi sequences sent.
+ * While sending a line, these will be resent at the end
+ * of any highlighted string, to restore text modes.
+ * {{ Not ideal, since we don't really know how many to resend. }}
+ */
+#define NUM_LAST_ANSIS 3
+static struct xbuffer last_ansi;
+static struct xbuffer last_ansis[NUM_LAST_ANSIS];
+static int curr_last_ansi;
+
+public int size_linebuf = 0; /* Size of line buffer (and attr buffer) */
+static struct ansi_state *line_ansi = NULL;
+static int ansi_in_line;
+static int hlink_in_line;
+static int line_mark_attr;
+static int cshift; /* Current left-shift of output line buffer */
+public int hshift; /* Desired left-shift of output line buffer */
public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */
-public int ntabstops = 1; /* Number of tabstops */
-public int tabdefault = 8; /* Default repeated tabstops */
-public POSITION highest_hilite; /* Pos of last hilite in file found so far */
-
-static int curr; /* Index into linebuf */
-static int column; /* Printable length, accounting for
- backspaces, etc. */
-static int overstrike; /* Next char should overstrike previous char */
+public int ntabstops = 1; /* Number of tabstops */
+public int tabdefault = 8; /* Default repeated tabstops */
+public POSITION highest_hilite; /* Pos of last hilite in file found so far */
+
+static int end_column; /* Printable length, accounting for backspaces, etc. */
+static int right_curr;
+static int right_column;
+static int overstrike; /* Next char should overstrike previous char */
static int last_overstrike = AT_NORMAL;
-static int is_null_line; /* There is no current line */
-static int lmargin; /* Left margin */
-static char pendc;
+static int is_null_line; /* There is no current line */
+static LWCHAR pendc;
static POSITION pendpos;
static char *end_ansi_chars;
static char *mid_ansi_chars;
+static int in_hilite;
-static int attr_swidth();
-static int attr_ewidth();
-static int do_append();
+static int attr_swidth LESSPARAMS ((int a));
+static int attr_ewidth LESSPARAMS ((int a));
+static int do_append LESSPARAMS ((LWCHAR ch, char *rep, POSITION pos));
extern int sigs;
extern int bs_mode;
extern int twiddle;
extern int binattr;
extern int status_col;
+extern int status_col_width;
+extern int linenum_width;
extern int auto_wrap, ignaw;
extern int bo_s_width, bo_e_width;
extern int ul_s_width, ul_e_width;
extern int utf_mode;
extern POSITION start_attnpos;
extern POSITION end_attnpos;
+extern char rscroll_char;
+extern int rscroll_attr;
+extern int use_color;
+extern int status_line;
static char mbc_buf[MAX_UTF_CHAR_LEN];
static int mbc_buf_len = 0;
static int mbc_buf_index = 0;
static POSITION mbc_pos;
+/* Configurable color map */
+static char color_map[AT_NUM_COLORS][12] = {
+ "Wm", /* AT_COLOR_ATTN */
+ "kR", /* AT_COLOR_BIN */
+ "kR", /* AT_COLOR_CTRL */
+ "kY", /* AT_COLOR_ERROR */
+ "c", /* AT_COLOR_LINENUM */
+ "Wb", /* AT_COLOR_MARK */
+ "kC", /* AT_COLOR_PROMPT */
+ "kc", /* AT_COLOR_RSCROLL */
+ "kG", /* AT_COLOR_SEARCH */
+ "", /* AT_COLOR_HEADER */
+ "", /* AT_UNDERLINE */
+ "", /* AT_BOLD */
+ "", /* AT_BLINK */
+ "", /* AT_STANDOUT */
+};
+
+/* State while processing an ANSI escape sequence */
+struct ansi_state {
+ int hindex; /* Index into hyperlink prefix */
+ int hlink; /* Processing hyperlink address? */
+ int prev_esc; /* Prev char was ESC (to detect ESC-\ seq) */
+};
+
/*
* Initialize from environment variables.
*/
public void
-init_line()
+init_line(VOID_PARAM)
{
+ int ax;
+
end_ansi_chars = lgetenv("LESSANSIENDCHARS");
- if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
+ if (isnullenv(end_ansi_chars))
end_ansi_chars = "m";
mid_ansi_chars = lgetenv("LESSANSIMIDCHARS");
- if (mid_ansi_chars == NULL || *mid_ansi_chars == '\0')
- mid_ansi_chars = "0123456789;[?!\"'#%()*+ ";
+ if (isnullenv(mid_ansi_chars))
+ mid_ansi_chars = "0123456789:;[?!\"'#%()*+ ";
- linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
- attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
+ linebuf.buf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
+ linebuf.attr = (int *) ecalloc(LINEBUF_SIZE, sizeof(int));
size_linebuf = LINEBUF_SIZE;
+ xbuf_init(&shifted_ansi);
+ xbuf_init(&last_ansi);
+ for (ax = 0; ax < NUM_LAST_ANSIS; ax++)
+ xbuf_init(&last_ansis[ax]);
+ curr_last_ansi = 0;
}
/*
* Expand the line buffer.
*/
static int
-expand_linebuf()
+expand_linebuf(VOID_PARAM)
{
/* Double the size of the line buffer. */
int new_size = size_linebuf * 2;
-
- /* Just realloc to expand the buffer, if we can. */
-#if HAVE_REALLOC
- char *new_buf = (char *) realloc(linebuf, new_size);
- char *new_attr = (char *) realloc(attr, new_size);
-#else
char *new_buf = (char *) calloc(new_size, sizeof(char));
- char *new_attr = (char *) calloc(new_size, sizeof(char));
-#endif
+ int *new_attr = (int *) calloc(new_size, sizeof(int));
if (new_buf == NULL || new_attr == NULL)
{
if (new_attr != NULL)
free(new_buf);
return 1;
}
-#if HAVE_REALLOC
- /*
- * We realloc'd the buffers; they already have the old contents.
- */
- #if 0
- memset(new_buf + size_linebuf, 0, new_size - size_linebuf);
- memset(new_attr + size_linebuf, 0, new_size - size_linebuf);
- #endif
-#else
/*
* We just calloc'd the buffers; copy the old contents.
*/
- memcpy(new_buf, linebuf, size_linebuf * sizeof(char));
- memcpy(new_attr, attr, size_linebuf * sizeof(char));
- free(attr);
- free(linebuf);
-#endif
- linebuf = new_buf;
- attr = new_attr;
+ memcpy(new_buf, linebuf.buf, size_linebuf * sizeof(char));
+ memcpy(new_attr, linebuf.attr, size_linebuf * sizeof(int));
+ free(linebuf.attr);
+ free(linebuf.buf);
+ linebuf.buf = new_buf;
+ linebuf.attr = new_attr;
size_linebuf = new_size;
return 0;
}
}
/*
+ */
+ static void
+inc_end_column(w)
+ int w;
+{
+ if (end_column > right_column && w > 0)
+ {
+ right_column = end_column;
+ right_curr = linebuf.end;
+ }
+ end_column += w;
+}
+
+/*
* Rewind the line buffer.
*/
public void
-prewind()
+prewind(VOID_PARAM)
{
- curr = 0;
- column = 0;
+ int ax;
+
+ linebuf.print = 6; /* big enough for longest UTF-8 sequence */
+ linebuf.pfx_end = 0;
+ for (linebuf.end = 0; linebuf.end < linebuf.print; linebuf.end++)
+ {
+ linebuf.buf[linebuf.end] = '\0';
+ linebuf.attr[linebuf.end] = 0;
+ }
+
+ end_column = 0;
+ right_curr = 0;
+ right_column = 0;
cshift = 0;
overstrike = 0;
last_overstrike = AT_NORMAL;
mbc_buf_len = 0;
is_null_line = 0;
pendc = '\0';
- lmargin = 0;
- if (status_col)
- lmargin += 1;
+ in_hilite = 0;
+ ansi_in_line = 0;
+ hlink_in_line = 0;
+ line_mark_attr = 0;
+ xbuf_reset(&shifted_ansi);
+ xbuf_reset(&last_ansi);
+ for (ax = 0; ax < NUM_LAST_ANSIS; ax++)
+ xbuf_reset(&last_ansis[ax]);
+ curr_last_ansi = 0;
+}
+
+/*
+ * Set a character in the line buffer.
+ */
+ static void
+set_linebuf(n, ch, attr)
+ int n;
+ char ch;
+ int attr;
+{
+ linebuf.buf[n] = ch;
+ linebuf.attr[n] = attr;
+}
+
+/*
+ * Append a character to the line buffer.
+ */
+ static void
+add_linebuf(ch, attr, w)
+ char ch;
+ int attr;
+ int w;
+{
+ set_linebuf(linebuf.end++, ch, attr);
+ inc_end_column(w);
}
/*
- * Insert the line number (of the given position) into the line buffer.
+ * Append a string to the line buffer.
+ */
+ static void
+addstr_linebuf(s, attr, cw)
+ char *s;
+ int attr;
+ int cw;
+{
+ for ( ; *s != '\0'; s++)
+ add_linebuf(*s, attr, cw);
+}
+
+/*
+ * Set a character in the line prefix buffer.
+ */
+ static void
+set_pfx(n, ch, attr)
+ int n;
+ char ch;
+ int attr;
+{
+ linebuf.pfx[n] = ch;
+ linebuf.pfx_attr[n] = attr;
+}
+
+/*
+ * Append a character to the line prefix buffer.
+ */
+ static void
+add_pfx(ch, attr)
+ char ch;
+ int attr;
+{
+ set_pfx(linebuf.pfx_end++, ch, attr);
+}
+
+/*
+ * Insert the status column and line number into the line buffer.
*/
public void
-plinenum(pos)
+plinestart(pos)
POSITION pos;
{
- register LINENUM linenum = 0;
- register int i;
+ LINENUM linenum = 0;
+ int i;
if (linenums == OPT_ONPLUS)
{
* Get the line number and put it in the current line.
* {{ Note: since find_linenum calls forw_raw_line,
* it may seek in the input file, requiring the caller
- * of plinenum to re-seek if necessary. }}
+ * of plinestart to re-seek if necessary. }}
* {{ Since forw_raw_line modifies linebuf, we must
* do this first, before storing anything in linebuf. }}
*/
/*
* Display a status column if the -J option is set.
*/
- if (status_col)
+ if (status_col || status_line)
{
- linebuf[curr] = ' ';
- if (start_attnpos != NULL_POSITION &&
- pos >= start_attnpos && pos < end_attnpos)
- attr[curr] = AT_NORMAL|AT_HILITE;
- else
- attr[curr] = AT_NORMAL;
- curr++;
- column++;
+ char c = posmark(pos);
+ if (c != 0)
+ line_mark_attr = AT_HILITE|AT_COLOR_MARK;
+ else if (start_attnpos != NULL_POSITION &&
+ pos >= start_attnpos && pos <= end_attnpos)
+ line_mark_attr = AT_HILITE|AT_COLOR_ATTN;
+ if (status_col)
+ {
+ add_pfx(c ? c : ' ', line_mark_attr); /* column 0: status */
+ while (linebuf.pfx_end < status_col_width)
+ add_pfx(' ', AT_NORMAL);
+ }
}
+
/*
* Display the line number at the start of each line
* if the -N option is set.
*/
if (linenums == OPT_ONPLUS)
{
- char buf[INT_STRLEN_BOUND(pos) + 2];
- int n;
-
- linenumtoa(linenum, buf);
- n = strlen(buf);
- if (n < MIN_LINENUM_WIDTH)
- n = MIN_LINENUM_WIDTH;
- sprintf(linebuf+curr, "%*s ", n, buf);
- n++; /* One space after the line number. */
- for (i = 0; i < n; i++)
- attr[curr+i] = AT_NORMAL;
- curr += n;
- column += n;
- lmargin += n;
- }
+ char buf[INT_STRLEN_BOUND(linenum) + 2];
+ int len;
- /*
- * Append enough spaces to bring us to the lmargin.
- */
- while (column < lmargin)
- {
- linebuf[curr] = ' ';
- attr[curr++] = AT_NORMAL;
- column++;
+ linenum = vlinenum(linenum);
+ if (linenum == 0)
+ len = 0;
+ else
+ {
+ linenumtoa(linenum, buf);
+ len = (int) strlen(buf);
+ }
+ for (i = 0; i < linenum_width - len; i++)
+ add_pfx(' ', AT_NORMAL);
+ for (i = 0; i < len; i++)
+ add_pfx(buf[i], AT_BOLD|AT_COLOR_LINENUM);
+ add_pfx(' ', AT_NORMAL);
}
+ end_column = linebuf.pfx_end;
}
/*
- * Shift the input line left.
- * This means discarding N printable chars at the start of the buffer.
+ * Return the width of the line prefix (status column and line number).
+ * {{ Actual line number can be wider than linenum_width. }}
*/
- static void
-pshift(shift)
- int shift;
+ public int
+line_pfx_width(VOID_PARAM)
{
- LWCHAR prev_ch = 0;
- unsigned char c;
- int shifted = 0;
- int to;
- int from;
- int len;
- int width;
- int prev_attr;
- int next_attr;
-
- if (shift > column - lmargin)
- shift = column - lmargin;
- if (shift > curr - lmargin)
- shift = curr - lmargin;
-
- to = from = lmargin;
- /*
- * We keep on going when shifted == shift
- * to get all combining chars.
- */
- while (shifted <= shift && from < curr)
- {
- c = linebuf[from];
- if (ctldisp == OPT_ONPLUS && IS_CSI_START(c))
- {
- /* Keep cumulative effect. */
- linebuf[to] = c;
- attr[to++] = attr[from++];
- while (from < curr && linebuf[from])
- {
- linebuf[to] = linebuf[from];
- attr[to++] = attr[from];
- if (!is_ansi_middle(linebuf[from++]))
- break;
- }
- continue;
- }
-
- width = 0;
-
- if (!IS_ASCII_OCTET(c) && utf_mode)
- {
- /* Assumes well-formedness validation already done. */
- LWCHAR ch;
-
- len = utf_len(c);
- if (from + len > curr)
- break;
- ch = get_wchar(linebuf + from);
- if (!is_composing_char(ch) && !is_combining_char(prev_ch, ch))
- width = is_wide_char(ch) ? 2 : 1;
- prev_ch = ch;
- } else
- {
- len = 1;
- if (c == '\b')
- /* XXX - Incorrect if several '\b' in a row. */
- width = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
- else if (!control_char(c))
- width = 1;
- prev_ch = 0;
- }
-
- if (width == 2 && shift - shifted == 1) {
- /* Should never happen when called by pshift_all(). */
- attr[to] = attr[from];
- /*
- * Assume a wide_char will never be the first half of a
- * combining_char pair, so reset prev_ch in case we're
- * followed by a '\b'.
- */
- prev_ch = linebuf[to++] = ' ';
- from += len;
- shifted++;
- continue;
- }
-
- /* Adjust width for magic cookies. */
- prev_attr = (to > 0) ? attr[to-1] : AT_NORMAL;
- next_attr = (from + len < curr) ? attr[from + len] : prev_attr;
- if (!is_at_equiv(attr[from], prev_attr) &&
- !is_at_equiv(attr[from], next_attr))
- {
- width += attr_swidth(attr[from]);
- if (from + len < curr)
- width += attr_ewidth(attr[from]);
- if (is_at_equiv(prev_attr, next_attr))
- {
- width += attr_ewidth(prev_attr);
- if (from + len < curr)
- width += attr_swidth(next_attr);
- }
- }
-
- if (shift - shifted < width)
- break;
- from += len;
- shifted += width;
- if (shifted < 0)
- shifted = 0;
- }
- while (from < curr)
- {
- linebuf[to] = linebuf[from];
- attr[to++] = attr[from++];
- }
- curr = to;
- column -= shifted;
- cshift += shifted;
+ int width = 0;
+ if (status_col)
+ width += status_col_width;
+ if (linenums == OPT_ONPLUS)
+ width += linenum_width + 1;
+ return width;
}
/*
- *
+ * Shift line left so that the last char is just to the left
+ * of the first visible column.
*/
public void
-pshift_all()
+pshift_all(VOID_PARAM)
{
- pshift(column);
+ int i;
+ for (i = linebuf.print; i < linebuf.end; i++)
+ if (linebuf.attr[i] == AT_ANSI)
+ xbuf_add(&shifted_ansi, linebuf.buf[i]);
+ linebuf.end = linebuf.print;
+ end_column = linebuf.pfx_end;
}
/*
/*
* Return the printing width of a given character and attribute,
- * if the character were added to the current position in the line buffer.
+ * if the character were added after prev_ch.
* Adding a character with a given attribute may cause an enter or exit
* attribute sequence to be inserted, so this must be taken into account.
*/
- static int
-pwidth(ch, a, prev_ch)
+ public int
+pwidth(ch, a, prev_ch, prev_a)
LWCHAR ch;
int a;
LWCHAR prev_ch;
+ int prev_a;
{
int w;
if (ch == '\b')
+ {
/*
* Backspace moves backwards one or two positions.
- * XXX - Incorrect if several '\b' in a row.
*/
+ if (prev_a & (AT_ANSI|AT_BINARY))
+ return strlen(prchar('\b'));
return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1;
+ }
if (!utf_mode || is_ascii_char(ch))
{
*
* Some terminals, upon failure to compose a
* composing character with the character(s) that
- * precede(s) it will actually take up one column
+ * precede(s) it will actually take up one end_column
* for the composing character; there isn't much
* we could do short of testing the (complex)
* composition process ourselves and printing
w = 1;
if (is_wide_char(ch))
w++;
- if (curr > 0 && !is_at_equiv(attr[curr-1], a))
- w += attr_ewidth(attr[curr-1]);
- if ((apply_at_specials(a) != AT_NORMAL) &&
- (curr == 0 || !is_at_equiv(attr[curr-1], a)))
+ if (linebuf.end > 0 && !is_at_equiv(linebuf.attr[linebuf.end-1], a))
+ w += attr_ewidth(linebuf.attr[linebuf.end-1]);
+ if (apply_at_specials(a) != AT_NORMAL &&
+ (linebuf.end == 0 || !is_at_equiv(linebuf.attr[linebuf.end-1], a)))
w += attr_swidth(a);
return (w);
}
/*
* Delete to the previous base character in the line buffer.
- * Return 1 if one is found.
- */
- static int
-backc()
-{
- LWCHAR prev_ch;
- char *p = linebuf + curr;
- LWCHAR ch = step_char(&p, -1, linebuf + lmargin);
- int width;
-
- /* This assumes that there is no '\b' in linebuf. */
- while ( curr > lmargin
- && column > lmargin
- && (!(attr[curr - 1] & (AT_ANSI|AT_BINARY))))
- {
- curr = p - linebuf;
- prev_ch = step_char(&p, -1, linebuf + lmargin);
- width = pwidth(ch, attr[curr], prev_ch);
- column -= width;
- if (width > 0)
- return 1;
- ch = prev_ch;
- }
-
- return 0;
-}
-
-/*
- * Are we currently within a recognized ANSI escape sequence?
*/
static int
-in_ansi_esc_seq()
+backc(VOID_PARAM)
{
+ LWCHAR ch;
char *p;
- /*
- * Search backwards for either an ESC (which means we ARE in a seq);
- * or an end char (which means we're NOT in a seq).
- */
- for (p = &linebuf[curr]; p > linebuf; )
+ if (linebuf.end == 0)
+ return (0);
+ p = &linebuf.buf[linebuf.end];
+ ch = step_char(&p, -1, linebuf.buf);
+ /* Skip back to the next nonzero-width char. */
+ while (p > linebuf.buf)
{
- LWCHAR ch = step_char(&p, -1, linebuf);
- if (IS_CSI_START(ch))
- return (1);
- if (!is_ansi_middle(ch))
- return (0);
+ LWCHAR prev_ch;
+ int width;
+ linebuf.end = (int) (p - linebuf.buf);
+ prev_ch = step_char(&p, -1, linebuf.buf);
+ width = pwidth(ch, linebuf.attr[linebuf.end], prev_ch, linebuf.attr[linebuf.end-1]);
+ end_column -= width;
+ /* {{ right_column? }} */
+ if (width > 0)
+ break;
+ ch = prev_ch;
}
- return (0);
+ return (1);
}
/*
}
/*
- *
+ * Can a char appear in an ANSI escape sequence, before the end char?
*/
public int
is_ansi_middle(ch)
}
/*
+ * Skip past an ANSI escape sequence.
+ * pp is initially positioned just after the CSI_START char.
+ */
+ public void
+skip_ansi(pansi, pp, limit)
+ struct ansi_state *pansi;
+ char **pp;
+ constant char *limit;
+{
+ LWCHAR c;
+ do {
+ c = step_char(pp, +1, limit);
+ } while (*pp < limit && ansi_step(pansi, c) == ANSI_MID);
+ /* Note that we discard final char, for which is_ansi_end is true. */
+}
+
+/*
+ * Determine if a character starts an ANSI escape sequence.
+ * If so, return an ansi_state struct; otherwise return NULL.
+ */
+ public struct ansi_state *
+ansi_start(ch)
+ LWCHAR ch;
+{
+ struct ansi_state *pansi;
+
+ if (!IS_CSI_START(ch))
+ return NULL;
+ pansi = ecalloc(1, sizeof(struct ansi_state));
+ pansi->hindex = 0;
+ pansi->hlink = 0;
+ pansi->prev_esc = 0;
+ return pansi;
+}
+
+/*
+ * Determine whether the next char in an ANSI escape sequence
+ * ends the sequence.
+ */
+ public int
+ansi_step(pansi, ch)
+ struct ansi_state *pansi;
+ LWCHAR ch;
+{
+ if (pansi->hlink)
+ {
+ /* Hyperlink ends with \7 or ESC-backslash. */
+ if (ch == '\7')
+ return ANSI_END;
+ if (pansi->prev_esc && ch == '\\')
+ return ANSI_END;
+ pansi->prev_esc = (ch == ESC);
+ return ANSI_MID;
+ }
+ if (pansi->hindex >= 0)
+ {
+ static char hlink_prefix[] = ESCS "]8;";
+ if (ch == hlink_prefix[pansi->hindex] ||
+ (pansi->hindex == 0 && IS_CSI_START(ch)))
+ {
+ pansi->hindex++;
+ if (hlink_prefix[pansi->hindex] == '\0')
+ pansi->hlink = 1; /* now processing hyperlink addr */
+ return ANSI_MID;
+ }
+ pansi->hindex = -1; /* not a hyperlink */
+ }
+ /* Check for SGR sequences */
+ if (is_ansi_middle(ch))
+ return ANSI_MID;
+ if (is_ansi_end(ch))
+ return ANSI_END;
+ return ANSI_ERR;
+}
+
+/*
+ * Free an ansi_state structure.
+ */
+ public void
+ansi_done(pansi)
+ struct ansi_state *pansi;
+{
+ free(pansi);
+}
+
+/*
+ * Will w characters in attribute a fit on the screen?
+ */
+ static int
+fits_on_screen(w, a)
+ int w;
+ int a;
+{
+ if (ctldisp == OPT_ON)
+ /* We're not counting, so say that everything fits. */
+ return 1;
+ return (end_column - cshift + w + attr_ewidth(a) <= sc_width);
+}
+
+/*
* Append a character and attribute to the line buffer.
*/
-#define STORE_CHAR(ch,a,rep,pos) \
+#define STORE_CHAR(ch,a,rep,pos) \
do { \
if (store_char((ch),(a),(rep),(pos))) return (1); \
} while (0)
POSITION pos;
{
int w;
+ int i;
int replen;
char cs;
- w = (a & (AT_UNDERLINE|AT_BOLD)); /* Pre-use w. */
- if (w != AT_NORMAL)
- last_overstrike = w;
+ i = (a & (AT_UNDERLINE|AT_BOLD));
+ if (i != AT_NORMAL)
+ last_overstrike = i;
#if HILITE_SEARCH
{
int matches;
- if (is_hilited(pos, pos+1, 0, &matches))
+ int resend_last = 0;
+ int hl_attr;
+
+ if (pos == NULL_POSITION)
+ {
+ /* Color the prompt unless it has ansi sequences in it. */
+ hl_attr = ansi_in_line ? 0 : AT_STANDOUT|AT_COLOR_PROMPT;
+ } else
+ {
+ hl_attr = is_hilited_attr(pos, pos+1, 0, &matches);
+ if (hl_attr == 0 && status_line)
+ hl_attr = line_mark_attr;
+ }
+ if (hl_attr)
{
/*
* This character should be highlighted.
*/
if (a != AT_ANSI)
{
- if (highest_hilite != NULL_POSITION &&
- pos > highest_hilite)
- highest_hilite = pos;
- a |= AT_HILITE;
+ if (highest_hilite != NULL_POSITION && pos != NULL_POSITION && pos > highest_hilite)
+ highest_hilite = pos;
+ a |= hl_attr;
+ }
+ in_hilite = 1;
+ } else
+ {
+ if (in_hilite)
+ {
+ /*
+ * This is the first non-hilited char after a hilite.
+ * Resend the last ANSI seq to restore color.
+ */
+ resend_last = 1;
+ }
+ in_hilite = 0;
+ }
+ if (resend_last)
+ {
+ int ai;
+ for (ai = 0; ai < NUM_LAST_ANSIS; ai++)
+ {
+ int ax = (curr_last_ansi + ai) % NUM_LAST_ANSIS;
+ for (i = 0; i < last_ansis[ax].end; i++)
+ STORE_CHAR(last_ansis[ax].data[i], AT_ANSI, NULL, pos);
}
}
}
#endif
- if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
- {
- if (!is_ansi_end(ch) && !is_ansi_middle(ch)) {
- /* Remove whole unrecognized sequence. */
- char *p = &linebuf[curr];
- LWCHAR bch;
- do {
- bch = step_char(&p, -1, linebuf);
- } while (p > linebuf && !IS_CSI_START(bch));
- curr = p - linebuf;
- return 0;
- }
- a = AT_ANSI; /* Will force re-AT_'ing around it. */
+ if (a == AT_ANSI) {
w = 0;
- }
- else if (ctldisp == OPT_ONPLUS && IS_CSI_START(ch))
- {
- a = AT_ANSI; /* Will force re-AT_'ing around it. */
- w = 0;
- }
- else
- {
- char *p = &linebuf[curr];
- LWCHAR prev_ch = step_char(&p, -1, linebuf);
- w = pwidth(ch, a, prev_ch);
+ } else {
+ char *p = &linebuf.buf[linebuf.end];
+ LWCHAR prev_ch = (linebuf.end > 0) ? step_char(&p, -1, linebuf.buf) : 0;
+ int prev_a = (linebuf.end > 0) ? linebuf.attr[linebuf.end-1] : 0;
+ w = pwidth(ch, a, prev_ch, prev_a);
}
- if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
- /*
- * Won't fit on screen.
- */
+ if (!fits_on_screen(w, a))
return (1);
if (rep == NULL)
{
replen = utf_len(rep[0]);
}
- if (curr + replen >= size_linebuf-6)
+ if (linebuf.end + replen >= size_linebuf-6)
{
/*
* Won't fit in line buffer.
return (1);
}
- while (replen-- > 0)
+ if (cshift == hshift && shifted_ansi.end > 0)
+ {
+ /* Copy shifted ANSI sequences to beginning of line. */
+ for (i = 0; i < shifted_ansi.end; i++)
+ add_linebuf(shifted_ansi.data[i], AT_ANSI, 0);
+ xbuf_reset(&shifted_ansi);
+ }
+ /* Add the char to the buf, even if we will left-shift it next. */
+ inc_end_column(w);
+ for (i = 0; i < replen; i++)
+ add_linebuf(*rep++, a, 0);
+
+ if (cshift < hshift)
{
- linebuf[curr] = *rep++;
- attr[curr] = a;
- curr++;
+ /* We haven't left-shifted enough yet. */
+ if (a == AT_ANSI)
+ xbuf_add(&shifted_ansi, ch); /* Save ANSI attributes */
+ if (linebuf.end > linebuf.print)
+ {
+ /* Shift left enough to put last byte of this char at print-1. */
+ int i;
+ for (i = 0; i < linebuf.print; i++)
+ {
+ linebuf.buf[i] = linebuf.buf[i+replen];
+ linebuf.attr[i] = linebuf.attr[i+replen];
+ }
+ linebuf.end -= replen;
+ cshift += w;
+ /*
+ * If the char we just left-shifted was double width,
+ * the 2 spaces we shifted may be too much.
+ * Represent the "half char" at start of line with a highlighted space.
+ */
+ while (cshift > hshift)
+ {
+ add_linebuf(' ', rscroll_attr, 0);
+ cshift--;
+ }
+ }
}
- column += w;
return (0);
}
+#define STORE_STRING(s,a,pos) \
+ do { if (store_string((s),(a),(pos))) return (1); } while (0)
+
+ static int
+store_string(s, a, pos)
+ char *s;
+ int a;
+ POSITION pos;
+{
+ if (!fits_on_screen(strlen(s), a))
+ return 1;
+ for ( ; *s != 0; s++)
+ STORE_CHAR(*s, a, NULL, pos);
+ return 0;
+}
+
/*
* Append a tab to the line buffer.
* Store spaces to represent the tab.
*/
-#define STORE_TAB(a,pos) \
+#define STORE_TAB(a,pos) \
do { if (store_tab((a),(pos))) return (1); } while (0)
static int
int attr;
POSITION pos;
{
- int to_tab = column + cshift - lmargin;
- int i;
+ int to_tab = end_column - linebuf.pfx_end;
if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1])
to_tab = tabdefault -
((to_tab - tabstops[ntabstops-1]) % tabdefault);
else
{
+ int i;
for (i = ntabstops - 2; i >= 0; i--)
if (to_tab >= tabstops[i])
break;
to_tab = tabstops[i+1] - to_tab;
}
- if (column + to_tab - 1 + pwidth(' ', attr, 0) + attr_ewidth(attr) > sc_width)
- return 1;
-
do {
STORE_CHAR(' ', attr, " ", pos);
} while (--to_tab > 0);
static int
store_prchar(c, pos)
- char c;
+ LWCHAR c;
POSITION pos;
{
- char *s;
-
/*
* Convert to printable representation.
*/
- s = prchar(c);
-
- /*
- * Make sure we can get the entire representation
- * of the character on this line.
- */
- if (column + (int) strlen(s) - 1 +
- pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
- return 1;
-
- for ( ; *s != 0; s++)
- STORE_CHAR(*s, AT_BINARY, NULL, pos);
-
+ STORE_STRING(prchar(c), AT_BINARY|AT_COLOR_CTRL, pos);
return 0;
}
for (i = 0; i < mbc_buf_index; i++)
if (store_prchar(mbc_buf[i], pos))
return mbc_buf_index - i;
-
return 0;
}
*/
public int
pappend(c, pos)
- char c;
+ int c;
POSITION pos;
{
int r;
if (pendc)
{
+ if (c == '\r' && pendc == '\r')
+ return (0);
if (do_append(pendc, NULL, pendpos))
/*
* Oops. We've probably lost the char which
if (!utf_mode)
{
- r = do_append((LWCHAR) c, NULL, pos);
+ r = do_append(c, NULL, pos);
} else
{
/* Perform strict validation in all possible cases. */
mbc_buf_index = 1;
*mbc_buf = c;
if (IS_ASCII_OCTET(c))
- r = do_append((LWCHAR) c, NULL, pos);
+ r = do_append(c, NULL, pos);
else if (IS_UTF8_LEAD(c))
{
mbc_buf_len = utf_len(c);
mbc_buf[mbc_buf_index++] = c;
if (mbc_buf_index < mbc_buf_len)
return (0);
- if (is_utf8_well_formed(mbc_buf))
+ if (is_utf8_well_formed(mbc_buf, mbc_buf_index))
r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos);
else
/* Complete, but not shortest form, sequence. */
/* Handle new char. */
if (!r)
goto retry;
- }
- }
-
- /*
- * If we need to shift the line, do it.
- * But wait until we get to at least the middle of the screen,
- * so shifting it doesn't affect the chars we're currently
- * pappending. (Bold & underline can get messed up otherwise.)
- */
- if (cshift < hshift && column > sc_width / 2)
- {
- linebuf[curr] = '\0';
- pshift(hshift - cshift);
+ }
}
if (r)
{
}
static int
-do_append(ch, rep, pos)
+store_control_char(ch, rep, pos)
LWCHAR ch;
char *rep;
POSITION pos;
{
- register int a;
- LWCHAR prev_ch;
-
- a = AT_NORMAL;
+ if (ctldisp == OPT_ON)
+ {
+ /* Output the character itself. */
+ STORE_CHAR(ch, AT_NORMAL, rep, pos);
+ } else
+ {
+ /* Output a printable representation of the character. */
+ STORE_PRCHAR((char) ch, pos);
+ }
+ return (0);
+}
- if (ch == '\b')
+ static int
+store_ansi(ch, rep, pos)
+ LWCHAR ch;
+ char *rep;
+ POSITION pos;
+{
+ switch (ansi_step(line_ansi, ch))
{
- if (bs_mode == BS_CONTROL)
- goto do_control_char;
+ case ANSI_MID:
+ STORE_CHAR(ch, AT_ANSI, rep, pos);
+ if (line_ansi->hlink)
+ hlink_in_line = 1;
+ xbuf_add(&last_ansi, ch);
+ break;
+ case ANSI_END:
+ STORE_CHAR(ch, AT_ANSI, rep, pos);
+ ansi_done(line_ansi);
+ line_ansi = NULL;
+ xbuf_add(&last_ansi, ch);
+ xbuf_set(&last_ansis[curr_last_ansi], &last_ansi);
+ xbuf_reset(&last_ansi);
+ curr_last_ansi = (curr_last_ansi + 1) % NUM_LAST_ANSIS;
+ break;
+ case ANSI_ERR:
+ {
+ /* Remove whole unrecognized sequence. */
+ char *start = (cshift < hshift) ? shifted_ansi.data : linebuf.buf;
+ int *end = (cshift < hshift) ? &shifted_ansi.end : &linebuf.end;
+ char *p = start + *end;
+ LWCHAR bch;
+ do {
+ bch = step_char(&p, -1, start);
+ } while (p > start && !IS_CSI_START(bch));
+ *end = (int) (p - start);
+ }
+ xbuf_reset(&last_ansi);
+ ansi_done(line_ansi);
+ line_ansi = NULL;
+ break;
+ }
+ return (0);
+}
- /*
- * A better test is needed here so we don't
- * backspace over part of the printed
- * representation of a binary character.
- */
- if ( curr <= lmargin
- || column <= lmargin
- || (attr[curr - 1] & (AT_ANSI|AT_BINARY)))
- STORE_PRCHAR('\b', pos);
- else if (bs_mode == BS_NORMAL)
- STORE_CHAR(ch, AT_NORMAL, NULL, pos);
- else if (bs_mode == BS_SPECIAL)
- overstrike = backc();
+ static int
+store_bs(ch, rep, pos)
+ LWCHAR ch;
+ char *rep;
+ POSITION pos;
+{
+ if (bs_mode == BS_CONTROL)
+ return store_control_char(ch, rep, pos);
+ if (linebuf.end > 0 &&
+ ((linebuf.end <= linebuf.print && linebuf.buf[linebuf.end-1] == '\0') ||
+ (linebuf.end > 0 && linebuf.attr[linebuf.end - 1] & (AT_ANSI|AT_BINARY))))
+ STORE_PRCHAR('\b', pos);
+ else if (bs_mode == BS_NORMAL)
+ STORE_CHAR(ch, AT_NORMAL, NULL, pos);
+ else if (bs_mode == BS_SPECIAL)
+ overstrike = backc();
+ return 0;
+}
- return 0;
+ static int
+do_append(ch, rep, pos)
+ LWCHAR ch;
+ char *rep;
+ POSITION pos;
+{
+ int a = AT_NORMAL;
+
+ if (ctldisp == OPT_ONPLUS && line_ansi == NULL)
+ {
+ line_ansi = ansi_start(ch);
+ if (line_ansi != NULL)
+ ansi_in_line = 1;
}
+ if (line_ansi != NULL)
+ return store_ansi(ch, rep, pos);
+
+ if (ch == '\b')
+ return store_bs(ch, rep, pos);
+
if (overstrike > 0)
{
/*
* in the line buffer. This will cause either
* underline (if a "_" is overstruck),
* bold (if an identical character is overstruck),
- * or just deletion of the character in the buffer.
+ * or just replacing the character in the buffer.
*/
+ LWCHAR prev_ch;
overstrike = utf_mode ? -1 : 0;
- /* To be correct, this must be a base character. */
- prev_ch = get_wchar(linebuf + curr);
- a = attr[curr];
+ if (utf_mode)
+ {
+ /* To be correct, this must be a base character. */
+ prev_ch = get_wchar(&linebuf.buf[linebuf.end]);
+ } else
+ {
+ prev_ch = (unsigned char) linebuf.buf[linebuf.end];
+ }
+ a = linebuf.attr[linebuf.end];
if (ch == prev_ch)
{
/*
{
a |= AT_UNDERLINE;
ch = prev_ch;
- rep = linebuf + curr;
+ rep = &linebuf.buf[linebuf.end];
} else if (prev_ch == '_')
{
a |= AT_UNDERLINE;
} else if (overstrike < 0)
{
if ( is_composing_char(ch)
- || is_combining_char(get_wchar(linebuf + curr), ch))
+ || is_combining_char(get_wchar(&linebuf.buf[linebuf.end]), ch))
/* Continuation of the same overstrike. */
a = last_overstrike;
else
overstrike = 0;
}
- if (ch == '\t')
+ if (ch == '\t')
{
/*
* Expand a tab into spaces.
switch (bs_mode)
{
case BS_CONTROL:
- goto do_control_char;
+ return store_control_char(ch, rep, pos);
case BS_NORMAL:
case BS_SPECIAL:
STORE_TAB(a, pos);
break;
}
- } else if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch))
+ return (0);
+ }
+ if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch))
{
- do_control_char:
- if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && IS_CSI_START(ch)))
- {
- /*
- * Output as a normal character.
- */
- STORE_CHAR(ch, AT_NORMAL, rep, pos);
- } else
- {
- STORE_PRCHAR((char) ch, pos);
- }
+ return store_control_char(ch, rep, pos);
} else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch))
{
- char *s;
-
- s = prutfchar(ch);
-
- if (column + (int) strlen(s) - 1 +
- pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width)
- return (1);
-
- for ( ; *s != 0; s++)
- STORE_CHAR(*s, AT_BINARY, NULL, pos);
- } else
+ STORE_STRING(prutfchar(ch), AT_BINARY, pos);
+ } else
{
STORE_CHAR(ch, a, rep, pos);
}
- return (0);
+ return (0);
}
/*
*
*/
public int
-pflushmbc()
+pflushmbc(VOID_PARAM)
{
int r = 0;
}
/*
+ * Switch to normal attribute at end of line.
+ */
+ static void
+add_attr_normal(VOID_PARAM)
+{
+ if (ctldisp != OPT_ONPLUS || !is_ansi_end('m'))
+ return;
+ addstr_linebuf("\033[m", AT_ANSI, 0);
+ if (hlink_in_line) /* Don't send hyperlink clear if we know we don't need to. */
+ addstr_linebuf("\033]8;;\033\\", AT_ANSI, 0);
+}
+
+/*
* Terminate the line in the line buffer.
*/
public void
-pdone(endline, forw)
+pdone(endline, chopped, forw)
int endline;
+ int chopped;
int forw;
{
(void) pflushmbc();
*/
(void) do_append(pendc, NULL, pendpos);
- /*
- * Make sure we've shifted the line, if we need to.
- */
- if (cshift < hshift)
- pshift(hshift - cshift);
-
- if (ctldisp == OPT_ONPLUS && is_ansi_end('m'))
+ if (chopped && rscroll_char)
{
- /* Switch to normal attribute at end of line. */
- char *p = "\033[m";
- for ( ; *p != '\0'; p++)
+ /*
+ * Display the right scrolling char.
+ * If we've already filled the rightmost screen char
+ * (in the buffer), overwrite it.
+ */
+ if (end_column >= sc_width + cshift)
{
- linebuf[curr] = *p;
- attr[curr++] = AT_ANSI;
+ /* We've already written in the rightmost char. */
+ end_column = right_column;
+ linebuf.end = right_curr;
}
+ add_attr_normal();
+ while (end_column < sc_width-1 + cshift)
+ {
+ /*
+ * Space to last (rightmost) char on screen.
+ * This may be necessary if the char we overwrote
+ * was double-width.
+ */
+ add_linebuf(' ', rscroll_attr, 1);
+ }
+ /* Print rscroll char. It must be single-width. */
+ add_linebuf(rscroll_char, rscroll_attr, 1);
+ } else
+ {
+ add_attr_normal();
+ }
+
+ /*
+ * If we're coloring a status line, fill out the line with spaces.
+ */
+ if (status_line && line_mark_attr != 0) {
+ while (end_column +1 < sc_width + cshift)
+ add_linebuf(' ', line_mark_attr, 1);
}
/*
* the next line is blank. In that case the single newline output for
* that blank line would be ignored!)
*/
- if (column < sc_width || !auto_wrap || (endline && ignaw) || ctldisp == OPT_ON)
+ if (end_column < sc_width + cshift || !auto_wrap || (endline && ignaw) || ctldisp == OPT_ON)
{
- linebuf[curr] = '\n';
- attr[curr] = AT_NORMAL;
- curr++;
+ add_linebuf('\n', AT_NORMAL, 0);
}
- else if (ignaw && column >= sc_width && forw)
+ else if (ignaw && end_column >= sc_width + cshift && forw)
{
/*
* Terminals with "ignaw" don't wrap until they *really* need
* char on the next line. We don't need to do this "nudge"
* at the top of the screen anyway.
*/
- linebuf[curr] = ' ';
- attr[curr++] = AT_NORMAL;
- linebuf[curr] = '\b';
- attr[curr++] = AT_NORMAL;
+ add_linebuf(' ', AT_NORMAL, 1);
+ add_linebuf('\b', AT_NORMAL, -1);
}
- linebuf[curr] = '\0';
- attr[curr] = AT_NORMAL;
+ set_linebuf(linebuf.end, '\0', AT_NORMAL);
}
/*
- *
+ * Set an attribute on each char of the line in the line buffer.
*/
public void
-set_status_col(c)
- char c;
+set_attr_line(a)
+ int a;
{
- linebuf[0] = c;
- attr[0] = AT_NORMAL|AT_HILITE;
+ int i;
+
+ for (i = linebuf.print; i < linebuf.end; i++)
+ linebuf.attr[i] |= a;
+}
+
+/*
+ * Set the char to be displayed in the status column.
+ */
+ public void
+set_status_col(c, attr)
+ int c;
+ int attr;
+{
+ set_pfx(0, c, attr);
}
/*
*/
public int
gline(i, ap)
- register int i;
- register int *ap;
+ int i;
+ int *ap;
{
if (is_null_line)
{
return i ? '\0' : '\n';
}
- *ap = attr[i];
- return (linebuf[i] & 0xFF);
+ if (i < linebuf.pfx_end)
+ {
+ *ap = linebuf.pfx_attr[i];
+ return linebuf.pfx[i];
+ }
+ i += linebuf.print - linebuf.pfx_end;
+ *ap = linebuf.attr[i];
+ return (linebuf.buf[i] & 0xFF);
}
/*
* Indicate that there is no current line.
*/
public void
-null_line()
+null_line(VOID_PARAM)
{
is_null_line = 1;
cshift = 0;
char **linep;
int *line_lenp;
{
- register int n;
- register int c;
+ int n;
+ int c;
POSITION new_pos;
if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
break;
}
}
- linebuf[n++] = c;
+ linebuf.buf[n++] = c;
c = ch_forw_get();
}
- linebuf[n] = '\0';
+ linebuf.buf[n] = '\0';
if (linep != NULL)
- *linep = linebuf;
+ *linep = linebuf.buf;
if (line_lenp != NULL)
*line_lenp = n;
return (new_pos);
char **linep;
int *line_lenp;
{
- register int n;
- register int c;
+ int n;
+ int c;
POSITION new_pos;
if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
return (NULL_POSITION);
n = size_linebuf;
- linebuf[--n] = '\0';
+ linebuf.buf[--n] = '\0';
for (;;)
{
c = ch_back_get();
/*
* Shift the data to the end of the new linebuf.
*/
- for (fm = linebuf + old_size_linebuf - 1,
- to = linebuf + size_linebuf - 1;
- fm >= linebuf; fm--, to--)
+ for (fm = linebuf.buf + old_size_linebuf - 1,
+ to = linebuf.buf + size_linebuf - 1;
+ fm >= linebuf.buf; fm--, to--)
*to = *fm;
n = size_linebuf - old_size_linebuf;
}
- linebuf[--n] = c;
+ linebuf.buf[--n] = c;
}
if (linep != NULL)
- *linep = &linebuf[n];
+ *linep = &linebuf.buf[n];
if (line_lenp != NULL)
*line_lenp = size_linebuf - 1 - n;
return (new_pos);
}
+
+/*
+ * Append a string to the line buffer.
+ */
+ static int
+pappstr(str)
+ constant char *str;
+{
+ while (*str != '\0')
+ {
+ if (pappend(*str++, NULL_POSITION))
+ /* Doesn't fit on screen. */
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Load a string into the line buffer.
+ * If the string is too long to fit on the screen,
+ * truncate the beginning of the string to fit.
+ */
+ public void
+load_line(str)
+ constant char *str;
+{
+ int save_hshift = hshift;
+
+ hshift = 0;
+ for (;;)
+ {
+ prewind();
+ if (pappstr(str) == 0)
+ break;
+ /*
+ * Didn't fit on screen; increase left shift by one.
+ * {{ This gets very inefficient if the string
+ * is much longer than the screen width. }}
+ */
+ hshift += 1;
+ }
+ set_linebuf(linebuf.end, '\0', AT_NORMAL);
+ hshift = save_hshift;
+}
+
+/*
+ * Find the shift necessary to show the end of the longest displayed line.
+ */
+ public int
+rrshift(VOID_PARAM)
+{
+ POSITION pos;
+ int save_width;
+ int line;
+ int longest = 0;
+
+ save_width = sc_width;
+ sc_width = INT_MAX;
+ pos = position(TOP);
+ for (line = 0; line < sc_height && pos != NULL_POSITION; line++)
+ {
+ pos = forw_line(pos);
+ if (end_column > longest)
+ longest = end_column;
+ }
+ sc_width = save_width;
+ if (longest < sc_width)
+ return 0;
+ return longest - sc_width;
+}
+
+/*
+ * Get the color_map index associated with a given attribute.
+ */
+ static int
+color_index(attr)
+ int attr;
+{
+ if (use_color)
+ {
+ switch (attr & AT_COLOR)
+ {
+ case AT_COLOR_ATTN: return 0;
+ case AT_COLOR_BIN: return 1;
+ case AT_COLOR_CTRL: return 2;
+ case AT_COLOR_ERROR: return 3;
+ case AT_COLOR_LINENUM: return 4;
+ case AT_COLOR_MARK: return 5;
+ case AT_COLOR_PROMPT: return 6;
+ case AT_COLOR_RSCROLL: return 7;
+ case AT_COLOR_SEARCH: return 8;
+ case AT_COLOR_HEADER: return 9;
+ }
+ }
+ if (attr & AT_UNDERLINE)
+ return 10;
+ if (attr & AT_BOLD)
+ return 11;
+ if (attr & AT_BLINK)
+ return 12;
+ if (attr & AT_STANDOUT)
+ return 13;
+ return -1;
+}
+
+/*
+ * Set the color string to use for a given attribute.
+ */
+ public int
+set_color_map(attr, colorstr)
+ int attr;
+ char *colorstr;
+{
+ int cx = color_index(attr);
+ if (cx < 0)
+ return -1;
+ if (strlen(colorstr)+1 > sizeof(color_map[cx]))
+ return -1;
+ if (*colorstr != '\0' && parse_color(colorstr, NULL, NULL) == CT_NULL)
+ return -1;
+ strcpy(color_map[cx], colorstr);
+ return 0;
+}
+
+/*
+ * Get the color string to use for a given attribute.
+ */
+ public char *
+get_color_map(attr)
+ int attr;
+{
+ int cx = color_index(attr);
+ if (cx < 0)
+ return NULL;
+ return color_map[cx];
+}