2989 lines
72 KiB
C
2989 lines
72 KiB
C
/*
|
||
* screen.c
|
||
*
|
||
* Written By Matthew Green, based on window.c by Michael Sandrof
|
||
* Copyright(c) 1993 Matthew Green
|
||
* Significant modifications by Jeremy Nelson
|
||
* Copyright 1997 EPIC Software Labs,
|
||
* Significant additions by J. Kean Johnston
|
||
* Copyright 1998 J. Kean Johnston, used with permission
|
||
* Modifications Copyright Colten Edwards 1997-1998
|
||
* See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
|
||
*/
|
||
|
||
|
||
#include "irc.h"
|
||
static char cvsrevision[] = "$Id$";
|
||
CVS_REVISION(screen_c)
|
||
#include "struct.h"
|
||
|
||
#include "commands.h"
|
||
#include "screen.h"
|
||
#include "status.h"
|
||
#include "window.h"
|
||
#include "output.h"
|
||
#include "vars.h"
|
||
#include "server.h"
|
||
#include "list.h"
|
||
#include "ircterm.h"
|
||
#include "names.h"
|
||
#include "ircaux.h"
|
||
#include "input.h"
|
||
#include "log.h"
|
||
#include "hook.h"
|
||
#include "exec.h"
|
||
#include "newio.h"
|
||
#include "misc.h"
|
||
#include "cset.h"
|
||
#include "tcl_bx.h"
|
||
#include "gui.h"
|
||
#define MAIN_SOURCE
|
||
#include "modval.h"
|
||
|
||
#ifdef TRANSLATE
|
||
#include "translat.h"
|
||
#endif
|
||
|
||
#ifdef WANT_HEBREW
|
||
#include "hebrew.h"
|
||
#endif
|
||
|
||
|
||
Screen *main_screen = NULL;
|
||
Screen *last_input_screen = NULL;
|
||
Screen *output_screen = NULL;
|
||
Window *debugging_window = NULL;
|
||
|
||
int extended_handled = 0;
|
||
extern int in_add_to_tcl;
|
||
extern int foreground;
|
||
|
||
/* Our list of screens */
|
||
Screen *screen_list = NULL;
|
||
|
||
int strip_ansi_never_xlate = 0;
|
||
|
||
/* * * * * * * * * OUTPUT CHAIN * * * * * * * * * * * * * * * * * * * * * *
|
||
* Entry is to say(), output(), yell(), put_it(), etc.
|
||
* ------- They call add_to_screen()
|
||
* ------- Which calls add_to_window()
|
||
* ------- Which calls split_up_line()
|
||
* ------- And then rite()
|
||
* ------- Which calls scroll_window()
|
||
* ------- And output_line()
|
||
* ------- Which calls term_putchar().
|
||
*
|
||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||
static int add_to_display_list(Window *, const char *);
|
||
Display *new_display_line (Display *);
|
||
static int rite (Window *, const char *);
|
||
|
||
static char display_standout (int flag);
|
||
static char display_bold (int flag);
|
||
static char display_blink (int flag);
|
||
static char display_underline (int flag);
|
||
static int display_color (long color1, long color2);
|
||
static void display_normal (void);
|
||
static char display_altcharset (int flag);
|
||
|
||
static void put_color (int, int);
|
||
|
||
static void delchar(char **text, int cnum)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < strlen(*text) - 1; i++)
|
||
if (i >= cnum) text[0][i] = text[0][i+1];
|
||
|
||
text[0][i] = 0;
|
||
}
|
||
|
||
char *BX_skip_incoming_mirc(char *text)
|
||
{
|
||
int i, x;
|
||
|
||
for (i = 0; i < strlen(text); i++) {
|
||
if (text[i] == 3)
|
||
{
|
||
x = 0;
|
||
while (isdigit((unsigned char)text[i]) || text[i] == 3)
|
||
{
|
||
while (text[i] == 3)
|
||
delchar(&text, i);
|
||
while (isdigit((unsigned char)text[i]) && x < 2)
|
||
{
|
||
delchar(&text, i);
|
||
if (isdigit((unsigned char)text[i]))
|
||
{
|
||
delchar(&text, i);
|
||
x += 2;
|
||
} else x += 1;
|
||
}
|
||
if (text[i] != ',') break;
|
||
delchar(&text, i); x = 0;
|
||
}
|
||
i -= 7;
|
||
}
|
||
}
|
||
return text;
|
||
}
|
||
|
||
/*
|
||
* add_to_screen: This adds the given null terminated buffer to the screen.
|
||
* That is, it determines which window the information should go to, which
|
||
* lastlog the information should be added to, which log the information
|
||
* should be sent to, etc
|
||
*/
|
||
void BX_add_to_screen(char *buffer)
|
||
{
|
||
char *out;
|
||
|
||
if (!get_int_var(MIRCS_VAR))
|
||
out = skip_incoming_mirc(buffer);
|
||
else
|
||
out = buffer;
|
||
|
||
if (in_window_command)
|
||
{
|
||
in_window_command = 0;
|
||
update_all_windows();
|
||
in_window_command = 1;
|
||
}
|
||
|
||
if (!current_window)
|
||
{
|
||
#ifdef GUI
|
||
gui_puts(out);
|
||
#else
|
||
puts(out);
|
||
#endif
|
||
return;
|
||
}
|
||
#ifdef WANT_TCL
|
||
if (in_add_to_tcl)
|
||
{
|
||
add_to_tcl(current_window, out);
|
||
return;
|
||
}
|
||
#endif
|
||
if (dumb_mode)
|
||
{
|
||
add_to_lastlog(current_window, out);
|
||
if (do_hook(WINDOW_LIST, "%u %s", current_window->refnum, out))
|
||
puts(out);
|
||
fflush(stdout);
|
||
#ifdef GUI
|
||
gui_flush();
|
||
#endif
|
||
return;
|
||
}
|
||
|
||
if (target_window)
|
||
{
|
||
target_window->output_func(target_window, out);
|
||
#ifdef GUI
|
||
gui_activity(COLOR_ACTIVE);
|
||
#endif
|
||
}
|
||
else
|
||
current_window->output_func(current_window, out);
|
||
|
||
}
|
||
|
||
|
||
/*
|
||
* add_to_window: adds the given string to the display. No kidding. This
|
||
* routine handles the whole ball of wax. It keeps track of what's on the
|
||
* screen, does the scrolling, everything... well, not quite everything...
|
||
* The CONTINUED_LINE idea thanks to Jan L. Peterson (jlp@hamblin.byu.edu)
|
||
*
|
||
* At least it used to. Now most of this is done by split_up_line, and this
|
||
* function just dumps it onto the screen. This is because the scrollback
|
||
* functions need to be able to figure out how to split things up too.
|
||
*/
|
||
void BX_add_to_window(Window *window, const char *str)
|
||
{
|
||
|
||
if (window->server >= 0 && get_server_redirect(window->server))
|
||
redirect_text(window->server,
|
||
get_server_redirect(window->server), str, STXT_QUIET);
|
||
|
||
if (do_hook(WINDOW_LIST, "%u %s", window->refnum, str))
|
||
{
|
||
char **lines;
|
||
int cols;
|
||
|
||
add_to_log(window->log_fp, 0, str, window->mangler);
|
||
add_to_lastlog(window, str);
|
||
display_standout(OFF);
|
||
display_bold(OFF);
|
||
|
||
if (window->screen)
|
||
cols = window->screen->co;
|
||
else
|
||
cols = window->columns;
|
||
|
||
for (lines = split_up_line(str, cols/* + 1*/); *lines; lines++)
|
||
{
|
||
if (add_to_display_list(window, *lines))
|
||
rite(window, *lines);
|
||
}
|
||
term_flush();
|
||
|
||
/*
|
||
* This used to be in rite(), but since rite() is a general
|
||
* purpose function, and this stuff really is only intended
|
||
* to hook on "new" output, it really makes more sense to do
|
||
* this here. This also avoids the terrible problem of
|
||
* recursive calls to split_up_line, which are bad.
|
||
*/
|
||
if (!window->visible)
|
||
{
|
||
/*
|
||
* This is for archon -- he wanted a way to have
|
||
* a hidden window always beep, even if BEEP is off.
|
||
*/
|
||
if (window->beep_always && strchr(str, BELL_CHAR))
|
||
{
|
||
Window *old_target_window = target_window;
|
||
target_window = current_window;
|
||
term_beep();
|
||
say("Beep in window %d", window->refnum);
|
||
target_window = old_target_window;
|
||
}
|
||
|
||
/*
|
||
* Tell some visible window about our problems
|
||
* if the user wants us to.
|
||
*/
|
||
if (!(window->miscflags & WINDOW_NOTIFIED) &&
|
||
who_level & window->notify_level)
|
||
{
|
||
window->miscflags |= WINDOW_NOTIFIED;
|
||
if (window->miscflags & WINDOW_NOTIFY)
|
||
{
|
||
Window *old_target_window = target_window;
|
||
int lastlog_level;
|
||
|
||
lastlog_level = set_lastlog_msg_level(LOG_CRAP);
|
||
target_window = current_window;
|
||
say("Activity in window %d",
|
||
window->refnum);
|
||
target_window = old_target_window;
|
||
set_lastlog_msg_level(lastlog_level);
|
||
}
|
||
update_all_status(current_window, NULL, 0);
|
||
}
|
||
}
|
||
|
||
cursor_in_display(window);
|
||
}
|
||
}
|
||
|
||
|
||
char **BX_split_up_line(const char *str, int max_cols)
|
||
{
|
||
int nl = 0;
|
||
return prepare_display(str, max_cols, &nl, 0);
|
||
}
|
||
|
||
|
||
/*
|
||
* Given an input string, prepare it for display. This involves several
|
||
* phases. We parse through the original string and prepare data in
|
||
* a list of strings, which are returned as this functions return value.
|
||
* During the parsing of the string, we adhere to the maximum width of
|
||
* the string, and to the user variables CONTINUED_LINE and INDENT.
|
||
* When calculating the width of the string, we do not take into account
|
||
* "invisible" characters, such as color and attribute setting characters.
|
||
*
|
||
* If we encounter an ANSI escape sequence starting, we decide how to
|
||
* handle it. If COLOR_MODE is 0, we silently drop all ANSI characters.
|
||
* if it is 1, we convert ANSI escape sequences which set color to the
|
||
* sequences required by the current terminal. If mode is 2, we leave these
|
||
* escape sequences alone and pass them through.
|
||
*
|
||
* Next, if we encounter an upper ASCII character, or a lower ASCII character
|
||
* that is not a format-specific character, we check the value of the user
|
||
* variable DISPLAY_PC_CHARACTERS. This variable is used as follows:
|
||
* 0 - ignore the character completely.
|
||
* 1 - if the terminal has a specific escape sequence for displaying PC
|
||
* ROM characters, use that sequence for displaying the character.
|
||
* Otherwise, display the character in inverse. If the character is
|
||
* >= 128, display it in bold inverse.
|
||
* 2 - if the terminal has a specific escape sequence for displaying
|
||
* PC ROM characters, display the character with that sequence, otherwise
|
||
* strip the character from the output string completely.
|
||
* 3 - insert the character exactly as is, and reply on the users terminal
|
||
* displaying it correctly as a graphics character (pass-through mode).
|
||
*
|
||
* If we detect the beginning of a Ctrl-C color sequence, we pick up the
|
||
* color values, and then examine the user variable COLOR_CONTROL_C.
|
||
* If this value is 0, we ignore the sequences completely, stripping them
|
||
* from the output string. If it is 1, and our terminal supports color,
|
||
* replace the color sequence with the terminal's color escape sequence.
|
||
* If the terminal does not support color, strip them from the output
|
||
* string. If the value is 2, convert these escape sequences explicitly
|
||
* into ANSI escape sequences.
|
||
*
|
||
* While we are preparing the output strings, we take special care not to
|
||
* extend the maximum column width of the output display. We will return
|
||
* as many strings as we need to in order to display the string, and we
|
||
* will wrap as intelligently as we know how.
|
||
*
|
||
* This function can also be run in several ways, and the mode parameter
|
||
* controls this. In the default mode (mode 0) we preserve ^B, ^C, ^F,
|
||
* ^I, ^V and ^_ characters in the string. This assumes that some other
|
||
* function will be doing the output interpretation of the strings.
|
||
* In this mode, depending on the conversion behaviour defined above,
|
||
* we will always convert ANSI color escape sequences to ^C sequences,
|
||
* and we will introduce a new sequence, ^R, for displaying PC ROM
|
||
* characters. ^R is always followed by 3 decimal digits, ALWAYS.
|
||
* Of course, if pass-through is in effect for graphics characters,
|
||
* the raw characters are displayed. To support all of this lot, we
|
||
* provide another function, display_final, which will actually insert
|
||
* all of the right codes for the colors, highlights etc.
|
||
*
|
||
* In mode 1, we prepare a string which is capable of being sent directly
|
||
* to the display, with all formatting in place. Thus the string can be
|
||
* sent with a single fputs(), which is much quicker than the looping
|
||
* one which itteratively does a term_putchar().
|
||
*
|
||
* It may be a good idea to allow users to control this behaviour, possibly
|
||
* by introducing a new variable, DISPLAY_MODE. 0 is the traditional way
|
||
* and 1 is the quick way. Anyway, we don't care at this level, as we are
|
||
* simply preparing the strings here.
|
||
*
|
||
* Errm ... oh yes, and since this caught me by surprise and caused many a
|
||
* minutes worth of hair pulling, don't forget we need to keep count of
|
||
* PRINTED Characters not buffer positions :-)
|
||
*/
|
||
#define SPLIT_EXTENT 40
|
||
char **BX_prepare_display(const char *orig_str,
|
||
int max_cols,
|
||
int *lused,
|
||
int flags)
|
||
{
|
||
static int recursion = 0,
|
||
output_size = 0;
|
||
int pos = 0, /* Current position in "buffer" */
|
||
col = 0, /* Current column in display */
|
||
word_break = 0, /* Last end of word */
|
||
indent = 0, /* Start of second word */
|
||
firstwb = 0,
|
||
beep_cnt = 0, /* Number of beeps */
|
||
beep_max, /* Maximum number of beeps */
|
||
tab_cnt = 0, /* TAB counter */
|
||
tab_max, /* Maximum number of tabs */
|
||
nds_count = 0,
|
||
nds_max,
|
||
line = 0, /* Current pos in "output" */
|
||
len, i, /* Used for counting tabs */
|
||
do_indent, /* Use indent or continued line? */
|
||
in_rev = 0, /* Are we in reverse mode? */
|
||
newline = 0; /* Number of newlines */
|
||
static char **output = NULL;
|
||
const char *ptr = NULL;
|
||
char buffer[BIG_BUFFER_SIZE + 1],
|
||
*cont_ptr = NULL,
|
||
*cont = empty_string,
|
||
c,
|
||
*words = NULL,
|
||
*str = NULL;
|
||
|
||
if (recursion)
|
||
ircpanic("prepare_display() called recursively");
|
||
recursion++;
|
||
|
||
beep_max = get_int_var(BEEP_VAR)? get_int_var(BEEP_MAX_VAR) : -1;
|
||
tab_max = get_int_var(TAB_VAR) ? get_int_var(TAB_MAX_VAR) : -1;
|
||
nds_max = get_int_var(ND_SPACE_MAX_VAR);
|
||
do_indent = get_int_var(INDENT_VAR);
|
||
words = get_string_var(WORD_BREAK_VAR);
|
||
|
||
if (!words)
|
||
words = ", ";
|
||
if (!(cont_ptr = get_string_var(CONTINUED_LINE_VAR)))
|
||
cont_ptr = empty_string;
|
||
|
||
buffer[0] = 0;
|
||
|
||
/* Handle blank or non-existent lines */
|
||
if (!orig_str || !orig_str[0])
|
||
orig_str = space;
|
||
|
||
if (!output_size)
|
||
{
|
||
int new_i = SPLIT_EXTENT;
|
||
RESIZE(output, char *, new_i);
|
||
while (output_size < new_i)
|
||
output[output_size++] = 0;
|
||
}
|
||
|
||
/*
|
||
* Before we do anything else, we convert the string to a normalized
|
||
* string. Until such time as I can do this all properly, this is
|
||
* the best effort we can make. The ultimate goal is to have the
|
||
* ANSI stripper / converter split everything into multiple lines,
|
||
* which will decrease the amounT of time spent calculating split
|
||
* lines. For now though, we simply "normalize" the line before we
|
||
* start working with it. This will convert ANSI escape sequences
|
||
* into easy-to-work-with Ctrl-C sequences, or IRCI-II formatting
|
||
* characters.
|
||
* The stripper is a separate function for right now to make
|
||
* debugging easier. This code will be improved, this is proof-of-concept
|
||
* code only, right now.
|
||
*/
|
||
str = strip_ansi (orig_str);
|
||
#ifdef WANT_HEBREW
|
||
/*
|
||
* crisk: Hebrew processing the line.
|
||
*
|
||
*/
|
||
if (get_int_var(HEBREW_TOGGLE_VAR))
|
||
hebrew_process(str);
|
||
#endif
|
||
|
||
/*
|
||
* Start walking through the entire string.
|
||
*/
|
||
for (ptr = str; *ptr && (pos < BIG_BUFFER_SIZE - 8); ptr++)
|
||
{
|
||
switch (*ptr)
|
||
{
|
||
case BELL_CHAR: /* bell */
|
||
{
|
||
beep_cnt++;
|
||
if ((beep_max == -1) || (beep_cnt > beep_max))
|
||
{
|
||
if (!in_rev)
|
||
buffer[pos++] = REV_TOG;
|
||
buffer[pos++] = (*ptr & 127) | 64;
|
||
if (!in_rev)
|
||
buffer[pos++] = REV_TOG;
|
||
col++;
|
||
}
|
||
else
|
||
buffer[pos++] = *ptr;
|
||
|
||
break; /* case '\a' */
|
||
}
|
||
case '\t': /* TAB */
|
||
{
|
||
tab_cnt++;
|
||
if ((tab_max > 0) && (tab_cnt > tab_max))
|
||
{
|
||
if (!in_rev)
|
||
buffer[pos++] = REV_TOG;
|
||
buffer[pos++] = (*ptr & 0x7f) | 64;
|
||
if (!in_rev)
|
||
buffer[pos++] = REV_TOG;
|
||
col++;
|
||
}
|
||
else
|
||
{
|
||
if (indent == 0)
|
||
indent = -1;
|
||
word_break = pos;
|
||
|
||
/* Only go as far as the edge of the screen */
|
||
len = 8 - (col % 8);
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
buffer[pos++] = ' ';
|
||
if (col++ >= max_cols)
|
||
break;
|
||
}
|
||
}
|
||
break; /* case '\t' */
|
||
}
|
||
case ND_SPACE:
|
||
{
|
||
nds_count++;
|
||
/*
|
||
* Just swallop up any ND's over the max
|
||
*/
|
||
if ((nds_max > 0) && (nds_count > nds_max))
|
||
;
|
||
else
|
||
buffer[pos++] = ND_SPACE;
|
||
break;
|
||
}
|
||
case '\n': /* Forced newline */
|
||
{
|
||
newline = 1;
|
||
if (indent == 0)
|
||
indent = -1;
|
||
word_break = pos;
|
||
break; /* case '\n' */
|
||
}
|
||
case COLOR_CHAR:
|
||
{
|
||
int lhs = 0, rhs = 0;
|
||
const char *end = skip_ctl_c_seq(ptr, &lhs, &rhs, 0);
|
||
while (ptr < end)
|
||
buffer[pos++] = *ptr++;
|
||
ptr = end - 1;
|
||
break; /* case COLOR_CHAR */
|
||
}
|
||
case ROM_CHAR:
|
||
{
|
||
/*
|
||
* Copy the \r and the first three digits that
|
||
* are after it. Careful! There may not be
|
||
* three digits after it! If there arent 3
|
||
* chars, we fake it with zeros. This is the
|
||
* wrong thing, but its better than crashing.
|
||
*/
|
||
buffer[pos++] = *ptr++; /* Copy the \R ... */
|
||
if (*ptr)
|
||
buffer[pos++] = *ptr++;
|
||
else
|
||
buffer[pos++] = '0';
|
||
if (*ptr)
|
||
buffer[pos++] = *ptr++;
|
||
else
|
||
buffer[pos++] = '0';
|
||
if (*ptr)
|
||
buffer[pos++] = *ptr;
|
||
else
|
||
buffer[pos++] = '0';
|
||
|
||
col++; /* This is a printable */
|
||
break; /* case ROM_CHAR */
|
||
}
|
||
|
||
case UND_TOG:
|
||
case ALL_OFF:
|
||
case REV_TOG:
|
||
case BOLD_TOG:
|
||
case BLINK_TOG:
|
||
case ALT_TOG:
|
||
{
|
||
buffer[pos++] = *ptr;
|
||
if (*ptr == ALL_OFF)
|
||
in_rev = 0;
|
||
else if (*ptr == REV_TOG)
|
||
in_rev = !in_rev;
|
||
break;
|
||
}
|
||
|
||
default:
|
||
{
|
||
if (*ptr == ' ' || strchr(words, *ptr))
|
||
{
|
||
if (indent == 0)
|
||
{
|
||
indent = -1;
|
||
firstwb = pos;
|
||
}
|
||
if (*ptr == ' ')
|
||
word_break = pos;
|
||
if (*ptr != ' ' && ptr[1] &&
|
||
(col + 1 < max_cols))
|
||
word_break = pos + 1;
|
||
buffer[pos++] = *ptr;
|
||
}
|
||
else
|
||
{
|
||
if (indent == -1)
|
||
indent = col;
|
||
buffer[pos++] = *ptr;
|
||
}
|
||
col++;
|
||
break;
|
||
}
|
||
} /* End of switch (*ptr) */
|
||
|
||
/*
|
||
* Must check for cols >= maxcols+1 because we can have a
|
||
* character on the extreme screen edge, and we would still
|
||
* want to treat this exactly as 1 line, and cols has already
|
||
* been incremented.
|
||
*/
|
||
if ((col >= max_cols) || newline)
|
||
{
|
||
char *pos_copy;
|
||
|
||
if (!word_break || (flags & PREPARE_NOWRAP))
|
||
word_break = max_cols /*pos - 1*/;
|
||
else if (col > max_cols)
|
||
word_break = pos - 1;
|
||
|
||
/*
|
||
* XXXX Massive hackwork here.
|
||
*
|
||
* Due to some ... interesting design considerations,
|
||
* if you have /set indent on and your first line has
|
||
* exactly one word separation in it, then obviously
|
||
* there is a really long "word" to the right of the
|
||
* first word. Normally, we would just break the
|
||
* line after the first word and then plop the really
|
||
* big word down to the second line. Only problem is
|
||
* that the (now) second line needs to be broken right
|
||
* there, and we chew up (and lose) a character going
|
||
* through the parsing loop before we notice this.
|
||
* Not good. It seems that in this very rare case,
|
||
* people would rather not have the really long word
|
||
* be sent to the second line, but rather included on
|
||
* the first line (it does look better this way),
|
||
* and so we can detect this condition here, without
|
||
* losing a char but this really is just a hack when
|
||
* it all comes down to it. Good thing its cheap. ;-)
|
||
*/
|
||
if (!*cont && (firstwb == word_break) && do_indent)
|
||
word_break = pos;
|
||
|
||
/*
|
||
* If we are approaching the number of lines that
|
||
* we have space for, then resize the master line
|
||
* buffer so we don't run out.
|
||
*/
|
||
|
||
if (line >= output_size - 3)
|
||
{
|
||
int new_i = output_size + SPLIT_EXTENT;
|
||
RESIZE(output, char *, new_i);
|
||
while (output_size < new_i)
|
||
output[output_size++] = 0;
|
||
}
|
||
|
||
c = buffer[word_break];
|
||
buffer[word_break] = 0;
|
||
malloc_strcpy(&(output[line++]), buffer);
|
||
|
||
buffer[word_break] = c;
|
||
|
||
if (!*cont && do_indent && (indent < (max_cols / 3)) &&
|
||
(strlen(cont_ptr) < indent))
|
||
{
|
||
cont = alloca(indent+10);
|
||
sprintf(cont, "%-*s", indent, cont_ptr);
|
||
}
|
||
else if (!*cont && *cont_ptr)
|
||
cont = cont_ptr;
|
||
while (word_break < pos && buffer[word_break] == ' ')
|
||
word_break++;
|
||
buffer[pos] = 0;
|
||
|
||
pos_copy = alloca(strlen(buffer) + strlen(cont) + 20);
|
||
strcpy(pos_copy, buffer+word_break);
|
||
|
||
strcpy (buffer, cont);
|
||
strcat (buffer, pos_copy);
|
||
col = pos = strlen(buffer);
|
||
|
||
word_break = 0;
|
||
newline = 0;
|
||
|
||
/* Only allow N lines... */
|
||
if (*lused && line >= *lused)
|
||
{
|
||
*buffer = 0;
|
||
break;
|
||
}
|
||
}
|
||
} /* End of (ptr = str; *ptr && (pos < BIG_BUFFER_SIZE - 8); ptr++) */
|
||
|
||
buffer[pos++] = ALL_OFF;
|
||
buffer[pos] = 0;
|
||
if (*buffer)
|
||
malloc_strcpy(&(output[line++]),buffer);
|
||
|
||
recursion--;
|
||
new_free(&output[line]);
|
||
*lused = line-1;
|
||
new_free(&str);
|
||
return output;
|
||
}
|
||
|
||
|
||
/*
|
||
* This puts the given string into a scratch window. It ALWAYS suppresses
|
||
* any further action (by returning a FAIL, so rite() is not called).
|
||
*/
|
||
static int add_to_scratch_window_display_list(Window *window, const char *str)
|
||
{
|
||
Display *my_line, *my_line_prev;
|
||
int cnt;
|
||
|
||
if (window->scratch_line == -1)
|
||
ircpanic("This is not a scratch window.");
|
||
|
||
if (window->scratch_line > window->display_size)
|
||
ircpanic("scratch_line is too big.");
|
||
|
||
my_line = window->top_of_display;
|
||
my_line_prev = NULL;
|
||
|
||
for (cnt = 0; cnt < window->scratch_line; cnt++)
|
||
{
|
||
if (my_line == window->display_ip)
|
||
{
|
||
if (my_line_prev)
|
||
{
|
||
my_line_prev->next = new_display_line(my_line_prev);
|
||
my_line = my_line_prev->next;
|
||
my_line->next = window->display_ip;
|
||
window->display_ip->prev = my_line;
|
||
window->display_buffer_size++;
|
||
}
|
||
else
|
||
{
|
||
my_line = new_display_line(NULL);
|
||
window->top_of_display = my_line;
|
||
window->top_of_scrollback = my_line;
|
||
my_line->next = window->display_ip;
|
||
window->display_ip->prev = my_line;
|
||
}
|
||
}
|
||
|
||
my_line_prev = my_line;
|
||
my_line = my_line->next;
|
||
}
|
||
/* XXXX DO this right!!!! XXXX */
|
||
if (my_line == window->display_ip)
|
||
{
|
||
my_line = new_display_line(window->display_ip->prev);
|
||
if (window->display_ip->prev)
|
||
window->display_ip->prev->next = my_line;
|
||
my_line->next = window->display_ip;
|
||
window->display_ip->prev = my_line;
|
||
window->display_buffer_size++;
|
||
}
|
||
|
||
malloc_strcpy(&my_line->line, str);
|
||
window->cursor = window->scratch_line;
|
||
|
||
window->scratch_line++;
|
||
if (window->scratch_line >= window->display_size)
|
||
window->scratch_line = 0; /* Just go back to top */
|
||
if (window->noscroll)
|
||
{
|
||
if (window->scratch_line == 0)
|
||
my_line = window->top_of_display;
|
||
else if (my_line->next != window->display_ip)
|
||
my_line = my_line->next;
|
||
else
|
||
my_line = NULL;
|
||
|
||
if (my_line)
|
||
malloc_strcpy(&my_line->line, empty_string);
|
||
}
|
||
|
||
|
||
/*
|
||
* Make sure the cursor ends up in the right spot
|
||
*/
|
||
if (window->visible)
|
||
{
|
||
window->screen->cursor_window = window;
|
||
term_move_cursor(0, window->top + window->cursor);
|
||
term_clear_to_eol();
|
||
output_line(str);
|
||
display_standout(OFF);
|
||
if (window->noscroll)
|
||
{
|
||
term_move_cursor(0, window->top + window->scratch_line);
|
||
term_clear_to_eol();
|
||
}
|
||
}
|
||
|
||
window->cursor = window->scratch_line;
|
||
return 0; /* Express a failure */
|
||
}
|
||
|
||
|
||
/*
|
||
* This function adds an item to the window's display list. If the item
|
||
* should be displayed on the screen, then 1 is returned. If the item is
|
||
* not to be displayed, then 0 is returned. This function handles all
|
||
* the hold_mode stuff.
|
||
*/
|
||
static int add_to_display_list(Window *window, const char *str)
|
||
{
|
||
if (window->scratch_line != -1)
|
||
return add_to_scratch_window_display_list(window, str);
|
||
|
||
/* Add to the display list */
|
||
window->display_ip->next = new_display_line(window->display_ip);
|
||
malloc_strcpy(&window->display_ip->line, str);
|
||
window->display_ip = window->display_ip->next;
|
||
window->display_buffer_size++;
|
||
window->distance_from_display++;
|
||
|
||
/*
|
||
* Only output the line if hold mode isn't activated
|
||
*/
|
||
if (((window->distance_from_display > window->display_size) &&
|
||
window->scrollback_point) ||
|
||
(window->hold_mode &&
|
||
(++window->held_displayed > window->display_size)) ||
|
||
(window->holding_something && window->lines_held))
|
||
{
|
||
if (!window->holding_something)
|
||
window->holding_something = 1;
|
||
|
||
window->lines_held++;
|
||
if (window->lines_held >= window->last_lines_held + 10)
|
||
{
|
||
update_window_status(window, 0);
|
||
window->last_lines_held = window->lines_held;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
if (window->in_more && window->hold_mode && window->last_lines_held &&
|
||
!window->lines_held && !window->holding_something &&
|
||
window->held_displayed)
|
||
{
|
||
/* if we get here, it should mean that no more lastlog more
|
||
* is possible, reset the values.
|
||
*/
|
||
reset_hold_mode(window);
|
||
}
|
||
|
||
/*
|
||
* Before outputting the line, we snip back the top of the
|
||
* display buffer. (The fact that we do this only when we get
|
||
* to here is what keeps whats currently on the window always
|
||
* active -- once we get out of hold mode or scrollback mode, then
|
||
* we truncate the display buffer at that point.)
|
||
*/
|
||
while (window->display_buffer_size > window->display_buffer_max)
|
||
{
|
||
Display *next = window->top_of_scrollback->next;
|
||
delete_display_line(window->top_of_scrollback);
|
||
window->top_of_scrollback = next;
|
||
window->display_buffer_size--;
|
||
}
|
||
|
||
/*
|
||
* And tell them its ok to output this line.
|
||
*/
|
||
return 1;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
* rite: this routine displays a line to the screen adding bold facing when
|
||
* specified by ^Bs, etc. It also does handle scrolling and paging, if
|
||
* SCROLL is on, and HOLD_MODE is on, etc. This routine assumes that str
|
||
* already fits on one screen line. If show is true, str is displayed
|
||
* regardless of the hold mode state. If redraw is true, it is assumed we a
|
||
* redrawing the screen from the display_ip list, and we should not add what
|
||
* we are displaying back to the display_ip list again.
|
||
*
|
||
* Note that rite sets display_highlight() to what it was at then end of the
|
||
* last rite(). Also, before returning, it sets display_highlight() to OFF.
|
||
* This way, between susequent rites(), you can be assured that the state of
|
||
* bold face will remain the same and the it won't interfere with anything
|
||
* else (i.e. status line, input line).
|
||
*/
|
||
static int rite(Window *window, const char *str)
|
||
{
|
||
static int high = OFF;
|
||
static int bold = OFF;
|
||
static int undl = OFF;
|
||
static int blink = OFF;
|
||
static int altc = OFF;
|
||
|
||
#ifdef GUI
|
||
gui_flush();
|
||
#endif
|
||
output_screen = window->screen;
|
||
scroll_window(window);
|
||
|
||
if (window->visible && foreground && window->display_size)
|
||
{
|
||
#if 1
|
||
display_standout(high);
|
||
display_bold(bold);
|
||
display_blink(blink);
|
||
display_underline(undl);
|
||
display_altcharset(altc);
|
||
put_color(-2, -2); /* Reinstate color */
|
||
|
||
output_line(str);
|
||
|
||
high = display_standout(OFF);
|
||
bold = display_bold(OFF);
|
||
undl = display_underline(OFF);
|
||
blink = display_blink(OFF);
|
||
altc = display_altcharset(OFF);
|
||
#else
|
||
put_color(-2,-2);
|
||
output_line(str);
|
||
#endif
|
||
term_newline();
|
||
term_cr();
|
||
}
|
||
window->cursor++;
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
* A temporary wrapper function for backwards compatibility.
|
||
*/
|
||
int BX_output_line(const char *str)
|
||
{
|
||
output_with_count(str, 1, 1);
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* NOTE: When we output colors, we explicitly turn off bold and reverse,
|
||
* as they can affect the display of the colors. We turn them back on
|
||
* afterwards, though. We don't need to worry about blinking or underline
|
||
* as they don't affect the colors. But reverse and bold do, so we need to
|
||
* make sure that the color sequence has preference, rather than the previous
|
||
* IRC-II formatting colors.
|
||
*
|
||
* OKAY ... just how pedantic do you wanna get? We have a problem with
|
||
* colorization, and its mostly to do with how ANSI is interpreted by
|
||
* different systems. Both Linux and SCO consoles do the right thing,
|
||
* but termcap lacks the ability to turn off blinking and underline
|
||
* as individual sequences, so the "normal" sequence is sent. This causes
|
||
* things such as reverse video, bold, underline, blinking etc to be turned
|
||
* off. So, we have to keep track of our current set of attributes always.
|
||
* Yes, this adds a little bit of processing to the loop, but this has already
|
||
* been optimized to be faster than the previous mechanism, and the cycles
|
||
* are so few, it hardly matters. I think it is more important to get the
|
||
* correct rendering of colors than to get just one more nanosecond of
|
||
* display speed.
|
||
*/
|
||
int BX_output_with_count(const char *str, int clreol, int output)
|
||
{
|
||
const char *ptr = str;
|
||
int beep = 0,
|
||
out = 0;
|
||
int val1,
|
||
val2;
|
||
char old_bold = 0,
|
||
old_rev = 0,
|
||
old_blink = 0,
|
||
old_undl = 0,
|
||
old_altc = 0,
|
||
in_color = 0;
|
||
|
||
if (output)
|
||
{
|
||
old_bold = display_bold(999);
|
||
old_rev = display_standout(999);
|
||
old_blink = display_blink(999);
|
||
old_undl = display_underline(999);
|
||
old_altc = display_altcharset(999);
|
||
in_color = display_color(999,999);
|
||
}
|
||
|
||
while (ptr && *ptr)
|
||
{
|
||
switch (*ptr)
|
||
{
|
||
case REV_TOG:
|
||
{
|
||
if (output)
|
||
{
|
||
display_standout(TOGGLE);
|
||
if (!in_color)
|
||
{
|
||
if (!(old_rev = !old_rev))
|
||
{
|
||
if (old_bold)
|
||
term_bold_on();
|
||
if (old_blink)
|
||
term_blink_on();
|
||
if (old_undl)
|
||
term_underline_on();
|
||
if (old_altc)
|
||
term_altcharset_on();
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case UND_TOG:
|
||
{
|
||
if (output)
|
||
{
|
||
display_underline(TOGGLE);
|
||
if (!in_color)
|
||
{
|
||
if (!(old_undl = !old_undl))
|
||
{
|
||
if (old_bold)
|
||
term_bold_on();
|
||
if (old_blink)
|
||
term_blink_on();
|
||
if (old_rev)
|
||
term_standout_on();
|
||
if (old_altc)
|
||
term_altcharset_on();
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case BOLD_TOG:
|
||
{
|
||
if (output)
|
||
{
|
||
display_bold(TOGGLE);
|
||
if (!in_color)
|
||
{
|
||
if (!(old_bold = !old_bold))
|
||
{
|
||
if (old_undl)
|
||
term_underline_on();
|
||
if (old_blink)
|
||
term_blink_on();
|
||
if (old_rev)
|
||
term_standout_on();
|
||
if (old_altc)
|
||
term_altcharset_on();
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case BLINK_TOG:
|
||
{
|
||
if (output)
|
||
{
|
||
display_blink(TOGGLE);
|
||
if (!in_color)
|
||
{
|
||
if (!(old_blink = !old_blink))
|
||
{
|
||
if (old_undl)
|
||
term_underline_on();
|
||
if (old_bold)
|
||
term_bold_on();
|
||
if (old_rev)
|
||
term_standout_on();
|
||
if (old_altc)
|
||
term_altcharset_on();
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case ALT_TOG:
|
||
{
|
||
if (output)
|
||
{
|
||
display_altcharset(TOGGLE);
|
||
if (!in_color)
|
||
{
|
||
if (!(old_altc = !old_altc))
|
||
{
|
||
if (old_undl)
|
||
term_underline_on();
|
||
if (old_bold)
|
||
term_bold_on();
|
||
if (old_rev)
|
||
term_standout_on();
|
||
if (old_blink)
|
||
term_blink_on();
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case ALL_OFF:
|
||
{
|
||
if (output)
|
||
{
|
||
display_underline(OFF);
|
||
display_bold(OFF);
|
||
display_standout(OFF);
|
||
display_blink(OFF);
|
||
display_altcharset(OFF);
|
||
display_color(-1,-1);
|
||
in_color = 0;
|
||
old_bold = old_rev = old_blink = old_undl = 0;
|
||
old_altc = 0;
|
||
if (!ptr[1])
|
||
display_normal();
|
||
}
|
||
break;
|
||
}
|
||
case BELL_CHAR:
|
||
{
|
||
beep++;
|
||
break;
|
||
}
|
||
/* Don't ask */
|
||
case '\f':
|
||
{
|
||
if (output)
|
||
{
|
||
display_standout(TOGGLE);
|
||
putchar_x('f');
|
||
display_standout(TOGGLE);
|
||
}
|
||
out++;
|
||
break;
|
||
}
|
||
case ROM_CHAR:
|
||
{
|
||
/* Sanity... */
|
||
if (!ptr[1] || !ptr[2] || !ptr[3])
|
||
break;
|
||
|
||
val1 = ((ptr[1] -'0') * 100) +
|
||
((ptr[2] -'0') * 10) +
|
||
(ptr[3] - '0');
|
||
|
||
if (output)
|
||
term_putgchar(val1);
|
||
|
||
ptr += 3;
|
||
out++;
|
||
break;
|
||
}
|
||
case COLOR_CHAR:
|
||
{
|
||
/*
|
||
* By the time we get here, we know we need to
|
||
* display these as colors, and they have been
|
||
* conveniently "rationalized" for us to provide
|
||
* for easy color insertion.
|
||
*/
|
||
val1 = val2 = -1;
|
||
ptr = skip_ctl_c_seq(ptr, &val1, &val2, 0);
|
||
if (output)
|
||
{
|
||
if ((val1 > -1) || (val2 > -1))
|
||
{
|
||
in_color = 1;
|
||
/* BAH. this screws up ansi term_standout_off();*/
|
||
}
|
||
if (display_bold(999) &&
|
||
(val1 >= 30 && val1 <= 37))
|
||
val1 += 20;
|
||
if (display_blink(999) &&
|
||
(val2 >= 40 && val2 <= 47))
|
||
val2 += 10;
|
||
display_color (val1, val2);
|
||
|
||
if ((val1 == -1) && (val2 == -1))
|
||
{
|
||
display_normal();
|
||
if (old_bold == ON)
|
||
term_bold_on();
|
||
if (old_rev == ON)
|
||
term_standout_on();
|
||
if (old_blink == ON)
|
||
term_blink_on();
|
||
if (old_undl == ON)
|
||
term_underline_on();
|
||
if (old_altc == ON)
|
||
term_altcharset_on();
|
||
in_color = 0;
|
||
}
|
||
}
|
||
ptr--;
|
||
break;
|
||
}
|
||
case ND_SPACE:
|
||
{
|
||
term_right(1);
|
||
out++;
|
||
break;
|
||
}
|
||
|
||
default:
|
||
{
|
||
/*
|
||
* JKJ TESTME: here I believe it is safe to use putchar_x,
|
||
* which is MUCH faster than all the weird processing in
|
||
* term_putchar. The reason for this is we can safely
|
||
* assume that the stripper has handled control sequences
|
||
* correctly before we ever get to this point.
|
||
* term_putchar() can still be left there, for other places
|
||
* that may want to call it, but at this level, I do not
|
||
* see any benefit of using it. using putchar_x makes things
|
||
* a LOT faster, as we have already done the processing once
|
||
* to make output "terminal friendly".
|
||
*/
|
||
if (output)
|
||
putchar_x(*ptr);
|
||
out++;
|
||
}
|
||
}
|
||
ptr++;
|
||
}
|
||
|
||
if (output)
|
||
{
|
||
if (beep)
|
||
term_beep();
|
||
if (clreol)
|
||
term_clear_to_eol();
|
||
}
|
||
|
||
return out;
|
||
}
|
||
|
||
/*
|
||
* scroll_window: Given a pointer to a window, this determines if that window
|
||
* should be scrolled, or the cursor moved to the top of the screen again, or
|
||
* if it should be left alone.
|
||
*/
|
||
void BX_scroll_window(Window *window)
|
||
{
|
||
if (dumb_mode)
|
||
return;
|
||
|
||
|
||
if (window->cursor == window->display_size)
|
||
{
|
||
int scroll, i;
|
||
if (window->holding_something || window->scrollback_point)
|
||
ircpanic("Can't scroll this window!");
|
||
|
||
if ((scroll = get_int_var(SCROLL_LINES_VAR)) <= 0)
|
||
scroll = 1;
|
||
|
||
|
||
for (i = 0; i < scroll; i++)
|
||
{
|
||
window->top_of_display = window->top_of_display->next;
|
||
window->distance_from_display--;
|
||
}
|
||
|
||
|
||
if (window->visible && foreground && window->display_size)
|
||
{
|
||
if (window->status_split)
|
||
term_scroll(window->top+window->status_lines,
|
||
window->top + window->status_lines +
|
||
window->cursor-1, scroll);
|
||
else
|
||
term_scroll(window->top,
|
||
window->top - window->status_lines +
|
||
window->cursor, scroll);
|
||
}
|
||
window->cursor -= scroll;
|
||
}
|
||
if (window->visible && window->display_size)
|
||
{
|
||
window->screen->cursor_window = window;
|
||
if (window->status_split)
|
||
term_move_cursor(0, window->top + window->status_lines + window->cursor);
|
||
else
|
||
term_move_cursor(0, window->top + window->cursor);
|
||
term_clear_to_eol();
|
||
}
|
||
}
|
||
|
||
#define ATTRIBUTE_HANDLER(x, y) \
|
||
static char display_ ## x (int flag) \
|
||
{ \
|
||
static int x = OFF; \
|
||
\
|
||
if (flag == 999) \
|
||
return (x); \
|
||
if (flag == (x)) \
|
||
return flag; \
|
||
\
|
||
switch (flag) \
|
||
{ \
|
||
case ON: \
|
||
{ \
|
||
x = ON; \
|
||
term_ ## x ## _on(); \
|
||
return OFF; \
|
||
} \
|
||
case OFF: \
|
||
{ \
|
||
x = OFF; \
|
||
term_ ## x ## _off(); \
|
||
return ON; \
|
||
} \
|
||
case TOGGLE: \
|
||
{ \
|
||
if (x == ON) \
|
||
{ \
|
||
x = OFF; \
|
||
term_ ## x ## _off(); \
|
||
return ON; \
|
||
} \
|
||
else \
|
||
{ \
|
||
x = ON; \
|
||
term_ ## x ## _on(); \
|
||
return OFF; \
|
||
} \
|
||
} \
|
||
} \
|
||
return OFF; \
|
||
}
|
||
|
||
ATTRIBUTE_HANDLER(underline, UNDERLINE_VIDEO_VAR)
|
||
ATTRIBUTE_HANDLER(bold, BOLD_VIDEO_VAR)
|
||
ATTRIBUTE_HANDLER(blink, BLINK_VIDEO_VAR)
|
||
ATTRIBUTE_HANDLER(standout, INVERSE_VIDEO_VAR)
|
||
ATTRIBUTE_HANDLER(altcharset, ALT_CHARSET_VAR)
|
||
|
||
static void display_normal (void)
|
||
{
|
||
tputs_x(current_term->TI_sgrstrs[TERM_SGR_NORMAL-1]);
|
||
}
|
||
|
||
static int display_color (long color1, long color2)
|
||
{
|
||
static int doing_color = 0;
|
||
|
||
if ((color1 == 999) && (color2 == 999))
|
||
return doing_color;
|
||
|
||
if ((color1 == -1) && (color2 == -1))
|
||
doing_color = 0;
|
||
else
|
||
doing_color = 1;
|
||
|
||
put_color(color1, color2);
|
||
return doing_color;
|
||
}
|
||
|
||
/*
|
||
* cursor_not_in_display: This forces the cursor out of the display by
|
||
* setting the cursor window to null. This doesn't actually change the
|
||
* physical position of the cursor, but it will force rite() to reset the
|
||
* cursor upon its next call
|
||
*/
|
||
void BX_cursor_not_in_display (Screen *s)
|
||
{
|
||
if (!s)
|
||
s = output_screen;
|
||
if (s->cursor_window)
|
||
s->cursor_window = NULL;
|
||
}
|
||
|
||
/*
|
||
* cursor_in_display: this forces the cursor_window to be the
|
||
* output_screen->current_window.
|
||
* It is actually only used in hold.c to trick the system into thinking the
|
||
* cursor is in a window, thus letting the input updating routines move the
|
||
* cursor down to the input line. Dumb dumb dumb
|
||
*/
|
||
void BX_cursor_in_display (Window *w)
|
||
{
|
||
if (!w)
|
||
w = current_window;
|
||
if (w->screen)
|
||
w->screen->cursor_window = w;
|
||
}
|
||
|
||
/*
|
||
* is_cursor_in_display: returns true if the cursor is in one of the windows
|
||
* (cursor_window is not null), false otherwise
|
||
*/
|
||
int BX_is_cursor_in_display(Screen *screen)
|
||
{
|
||
if (!screen && current_window->screen)
|
||
screen = current_window->screen;
|
||
return (screen->cursor_window ? 1 : 0);
|
||
}
|
||
|
||
/* * * * * * * SCREEN UDPATING AND RESIZING * * * * * * * * */
|
||
/*
|
||
* repaint_window: redraw the "m"th through the "n"th part of the window.
|
||
* If end_line is -1, then that means clear the display if any of it appears
|
||
* after the end of the scrollback buffer.
|
||
*/
|
||
void BX_repaint_window (Window *window, int start_line, int end_line)
|
||
{
|
||
Display *curr_line;
|
||
int count;
|
||
int clean_display = 0;
|
||
|
||
if (dumb_mode || !window->visible)
|
||
return;
|
||
|
||
if (end_line == -1)
|
||
{
|
||
end_line = window->display_size;
|
||
clean_display = 1;
|
||
}
|
||
|
||
curr_line = window->top_of_display;
|
||
for (count = 0; count < start_line; count++)
|
||
{
|
||
curr_line = curr_line->next;
|
||
if(!curr_line)
|
||
return;
|
||
}
|
||
|
||
window->cursor = start_line;
|
||
for (count = start_line; count < end_line; count++)
|
||
{
|
||
if (!curr_line) break;
|
||
rite(window, curr_line->line);
|
||
|
||
if (curr_line == window->display_ip)
|
||
break;
|
||
|
||
curr_line = curr_line->next;
|
||
}
|
||
|
||
/*
|
||
* If "end_line" is -1, then we're supposed to clear off the rest
|
||
* of the display, if possible. Do that here.
|
||
*/
|
||
if (clean_display)
|
||
{
|
||
for (; count < end_line - 1; count++)
|
||
{
|
||
term_clear_to_eol();
|
||
term_newline();
|
||
}
|
||
/* update_window_status(window, 1);*/
|
||
}
|
||
|
||
recalculate_window_cursor(window); /* XXXX */
|
||
}
|
||
|
||
/*
|
||
* create_new_screen creates a new screen structure. with the help of
|
||
* this structure we maintain ircII windows that cross screen window
|
||
* boundaries.
|
||
*/
|
||
Screen * BX_create_new_screen(void)
|
||
{
|
||
Screen *new = NULL, *list;
|
||
static int refnumber = 0;
|
||
|
||
for (list = screen_list; list; list = list->next)
|
||
{
|
||
if (!list->alive)
|
||
{
|
||
new = list;
|
||
break;
|
||
}
|
||
|
||
if (!list->next)
|
||
break;
|
||
}
|
||
|
||
if (!new)
|
||
{
|
||
new = (Screen *) new_malloc(sizeof(Screen));
|
||
new->screennum = ++refnumber;
|
||
new->next = NULL;
|
||
if (list)
|
||
list->next = new;
|
||
else
|
||
screen_list = new;
|
||
}
|
||
new->last_window_refnum = 1;
|
||
|
||
#ifdef WINNT
|
||
new->hStdin = GetStdHandle(STD_INPUT_HANDLE);
|
||
new->hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||
#endif
|
||
|
||
/* new->fdout = 1;*/
|
||
|
||
new->fpout = stdout;
|
||
new->fdout = fileno(stdout);
|
||
if (use_input)
|
||
new_open(0);
|
||
/* new->fdin = 0;*/
|
||
|
||
new->fpin = stdin;
|
||
new->fdin = fileno(stdin);
|
||
|
||
new->alive = 1;
|
||
new->li = current_term->li;
|
||
new->co = current_term->co;
|
||
new->old_li = 0;
|
||
new->old_co = 0;
|
||
new->buffer_pos = new->buffer_min_pos = 0;
|
||
new->input_buffer[0] = '\0';
|
||
new->input_cursor = 0;
|
||
new->input_visible = 0;
|
||
new->input_start_zone = 0;
|
||
new->input_end_zone = 0;
|
||
new->input_prompt = NULL;
|
||
new->input_prompt_len = 0;
|
||
new->input_prompt_malloc = 0;
|
||
new->input_line = 23;
|
||
|
||
new->redirect_server = -1;
|
||
|
||
#ifdef GUI
|
||
/* I don't know why I need these but until I figure it out... */
|
||
new->current_window=NULL;
|
||
new->visible_windows=0;
|
||
#endif
|
||
last_input_screen = new;
|
||
|
||
if (!main_screen)
|
||
#ifdef GUI
|
||
gui_screen(new);
|
||
|
||
malloc_strcpy(&new->tty_name, "<GUI>");
|
||
gui_resize(new);
|
||
#else
|
||
{
|
||
extern char attach_ttyname[];
|
||
main_screen = new;
|
||
malloc_strcpy(&new->tty_name, attach_ttyname);
|
||
}
|
||
#endif
|
||
init_input();
|
||
return new;
|
||
}
|
||
|
||
#if defined(WINDOW_CREATE) && !defined(WINNT)
|
||
|
||
#ifndef GUI
|
||
extern Window *BX_create_additional_screen (void)
|
||
{
|
||
Window *win;
|
||
Screen *new;
|
||
char *displayvar,
|
||
*termvar;
|
||
int screen_type = ST_NOTHING;
|
||
struct sockaddr_in NewSock;
|
||
socklen_t NsZ;
|
||
int s;
|
||
fd_set fd_read;
|
||
struct timeval timeout;
|
||
pid_t child;
|
||
unsigned short port;
|
||
char buffer[IO_BUFFER_SIZE + 1];
|
||
|
||
|
||
if (!use_input)
|
||
return NULL;
|
||
|
||
|
||
/* Check for X first. */
|
||
if ((displayvar = getenv("DISPLAY")))
|
||
{
|
||
if (!(termvar = getenv("TERM")))
|
||
say("I don't know how to create new windows for this terminal");
|
||
else
|
||
screen_type = ST_XTERM;
|
||
}
|
||
/*
|
||
* Environment variable STY has to be set for screen to work.. so it is
|
||
* the best way to check screen.. regardless of what TERM is, the
|
||
* execpl() for screen won't just open a new window if STY isn't set,
|
||
* it will open a new screen process, and run the wserv in its first
|
||
* window, not what we want... -phone
|
||
*/
|
||
if (screen_type == ST_NOTHING && getenv("STY"))
|
||
screen_type = ST_SCREEN;
|
||
|
||
|
||
if (screen_type == ST_NOTHING)
|
||
{
|
||
say("I don't know how to create new windows for this terminal");
|
||
return NULL;
|
||
}
|
||
|
||
say("Opening new %s...",
|
||
screen_type == ST_XTERM ? "window" :
|
||
screen_type == ST_SCREEN ? "screen" :
|
||
"wound" );
|
||
port = 0;
|
||
s = connect_by_number("127.0.0.1", &port, SERVICE_SERVER, PROTOCOL_TCP, 0);
|
||
if (s < 0)
|
||
{
|
||
yell("Couldn't establish server side -- error [%d]", s);
|
||
return NULL;
|
||
}
|
||
|
||
new = create_new_screen();
|
||
|
||
switch ((child = fork()))
|
||
{
|
||
case -1:
|
||
{
|
||
kill_screen(new);
|
||
say("I couldn't fire up a new wserv process");
|
||
break;
|
||
}
|
||
|
||
case 0:
|
||
{
|
||
char *opts;
|
||
char *xterm;
|
||
static char *args[64], **args_ptr = args;
|
||
static char geom[32];
|
||
int i;
|
||
|
||
setgid(getgid());
|
||
setuid(getuid());
|
||
setsid();
|
||
|
||
/*
|
||
* Make sure that no inhereted file descriptors
|
||
* are left over past the exec. xterm will reopen
|
||
* any fds that it is interested in.
|
||
*/
|
||
for (i = 3; i < 256; i++)
|
||
close(i);
|
||
|
||
/*
|
||
* Try to restore some sanity to the signal
|
||
* handlers, since they're not really appropriate here
|
||
*/
|
||
my_signal(SIGINT, SIG_IGN, 0);
|
||
my_signal(SIGSEGV, SIG_DFL, 0);
|
||
my_signal(SIGBUS, SIG_DFL, 0);
|
||
my_signal(SIGABRT, SIG_DFL, 0);
|
||
|
||
if (screen_type == ST_SCREEN)
|
||
{
|
||
opts = LOCAL_COPY(get_string_var(SCREEN_OPTIONS_VAR));
|
||
*args_ptr++ = "screen";
|
||
while (opts && *opts)
|
||
*args_ptr++ = new_next_arg(opts, &opts);
|
||
}
|
||
else if (screen_type == ST_XTERM)
|
||
{
|
||
snprintf(geom, sizeof geom, "%dx%d", current_term->co, current_term->li);
|
||
opts = LOCAL_COPY(get_string_var(XTERM_OPTIONS_VAR));
|
||
if (!(xterm = getenv("XTERM")))
|
||
if (!(xterm = get_string_var(XTERM_VAR)))
|
||
xterm = "xterm";
|
||
|
||
*args_ptr++ = xterm;
|
||
*args_ptr++ = "-geometry";
|
||
*args_ptr++ = geom;
|
||
while (opts && *opts)
|
||
*args_ptr++ = new_next_arg(opts, &opts);
|
||
if (get_string_var(DEFAULT_FONT_VAR))
|
||
{
|
||
opts = LOCAL_COPY(get_string_var(DEFAULT_FONT_VAR));
|
||
while (opts && *opts)
|
||
*args_ptr++ = new_next_arg(opts, &opts);
|
||
}
|
||
*args_ptr++ = "-e";
|
||
}
|
||
|
||
*args_ptr++ = WSERV_PATH;
|
||
*args_ptr++ = "127.0.0.1";
|
||
*args_ptr++ = ltoa((long)port);
|
||
*args_ptr++ = NULL;
|
||
|
||
s = execvp(args[0], args);
|
||
_exit(0);
|
||
}
|
||
}
|
||
|
||
/* All the rest of this is the parent.... */
|
||
NsZ = sizeof(NewSock);
|
||
FD_ZERO(&fd_read);
|
||
FD_SET(s, &fd_read);
|
||
timeout.tv_sec = (time_t) 10;
|
||
timeout.tv_usec = 0;
|
||
/* using say(), yell() can be bad in this next section of code. */
|
||
|
||
for (;;)
|
||
|
||
switch (select(NFDBITS , &fd_read, NULL, NULL, &timeout))
|
||
{
|
||
case -1:
|
||
if ((errno == EINTR) || (errno == EAGAIN))
|
||
continue;
|
||
case 0:
|
||
{
|
||
int old_errno = errno;
|
||
int errnod = get_child_exit (child);
|
||
|
||
close(s);
|
||
kill_screen(new);
|
||
kill(child, SIGKILL);
|
||
yell("child %s with %d", (errnod < 1) ? "signaled" : "exited",
|
||
(errnod < 1) ? -errnod : errnod);
|
||
yell("Errno is %d", old_errno);
|
||
return NULL;
|
||
}
|
||
default:
|
||
{
|
||
new->fdin = new->fdout = my_accept(s, (struct sockaddr *) &NewSock, &NsZ);
|
||
if (new->fdin < 0)
|
||
{
|
||
yell("Couldn't accept the new connection!");
|
||
kill_screen(new);
|
||
return NULL;
|
||
}
|
||
new_open(new->fdin);
|
||
new->fpin = new->fpout = fdopen(new->fdin, "r+");
|
||
close(s);
|
||
|
||
FD_ZERO(&fd_read);
|
||
FD_SET(new->fdin, &fd_read);
|
||
select(NFDBITS, &fd_read, NULL, NULL, &timeout);
|
||
|
||
if ((s = dgets(buffer, new->fdin, 0, IO_BUFFER_SIZE, NULL)) < 1)
|
||
{
|
||
yell("Read [%s] from wserv, but dgets() returned [%d]",buffer, s);
|
||
new->fdin = new_close(new->fdin);
|
||
kill_screen(new);
|
||
kill(child, SIGKILL);
|
||
return NULL;
|
||
}
|
||
|
||
chop(buffer, 1);
|
||
malloc_strcpy(&new->tty_name, buffer);
|
||
|
||
win = new_window(new);
|
||
refresh_screen(0, NULL);
|
||
return win;
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
#else
|
||
extern Window *BX_create_additional_screen (void)
|
||
{
|
||
Window *win;
|
||
Screen *new;
|
||
|
||
new = create_new_screen();
|
||
win = new_window(new);
|
||
gui_new_window(new, win);
|
||
|
||
output_screen = new;
|
||
set_input_prompt(win, get_string_var(INPUT_PROMPT_VAR), 0);
|
||
|
||
recalculate_windows(new);
|
||
return win;
|
||
}
|
||
#endif
|
||
|
||
|
||
void BX_kill_screen(Screen *screen)
|
||
{
|
||
Window *window;
|
||
ChannelList *chan;
|
||
Screen *list;
|
||
|
||
if (main_screen == screen)
|
||
{
|
||
for (list = screen_list; list; list = list->next)
|
||
{
|
||
if (list->alive != 0 && list != main_screen)
|
||
{
|
||
main_screen = list;
|
||
break;
|
||
}
|
||
|
||
if (!list->next)
|
||
irc_exit(1, "BitchX: No windows left!", NULL);
|
||
}
|
||
}
|
||
#ifndef GUI
|
||
if (screen->fdin)
|
||
{
|
||
if (use_input)
|
||
screen->fdin = new_close(screen->fdin);
|
||
close(screen->fdout);
|
||
close(screen->fdin);
|
||
}
|
||
#endif
|
||
while ((window = screen->window_list))
|
||
{
|
||
screen->window_list = window->next;
|
||
if (get_int_var(WINDOW_DESTROY_PART_VAR))
|
||
{
|
||
for (chan = get_server_channels(window->server); chan;chan = chan->next)
|
||
{
|
||
if(window == chan->window)
|
||
my_send_to_server(window->server, "PART %s", window->current_channel);
|
||
}
|
||
delete_window(window);
|
||
}
|
||
else
|
||
add_to_invisible_list(window);
|
||
}
|
||
if (last_input_screen == screen)
|
||
last_input_screen = main_screen;
|
||
#ifndef GUI
|
||
say("The screen is now dead.");
|
||
#endif
|
||
screen->alive = 0;
|
||
make_window_current(NULL);
|
||
#ifdef GUI
|
||
gui_kill_window(screen);
|
||
#endif
|
||
}
|
||
|
||
#endif /* WINDOW_CREATE */
|
||
|
||
void set_screens (fd_set *rd, fd_set *wd)
|
||
{
|
||
Screen *screen;
|
||
if (use_input)
|
||
for (screen = screen_list; screen; screen = screen->next)
|
||
if (screen->alive)
|
||
FD_SET(screen->fdin, rd);
|
||
}
|
||
|
||
|
||
|
||
|
||
void do_screens (fd_set *rd)
|
||
{
|
||
Screen *screen;
|
||
|
||
if (!use_input)
|
||
return;
|
||
|
||
for (screen = screen_list; screen; screen = screen->next)
|
||
{
|
||
if (!screen->alive)
|
||
continue;
|
||
#ifdef GUI
|
||
if (gui_isset(screen, rd, 0))
|
||
#else
|
||
if (FD_ISSET(screen->fdin, rd))
|
||
#endif
|
||
{
|
||
/*
|
||
* This section of code handles all in put from the terminal(s).
|
||
* connected to ircII. Perhaps the idle time *shouldn't* be
|
||
* reset unless its not a screen-fd that was closed..
|
||
*/
|
||
time(&idle_time);
|
||
if (cpu_saver)
|
||
{
|
||
cpu_saver = 0;
|
||
update_all_status(current_window, NULL, 0);
|
||
}
|
||
|
||
if (dumb_mode)
|
||
{
|
||
char buffer[IO_BUFFER_SIZE + 1];
|
||
if (dgets(buffer, screen->fdin, 0, IO_BUFFER_SIZE, NULL))
|
||
{
|
||
*(buffer + strlen(buffer) - 1) = '\0';
|
||
if (get_int_var(INPUT_ALIASES_VAR))
|
||
parse_line(NULL, buffer, empty_string, 1, 0, 1);
|
||
else
|
||
parse_line(NULL, buffer, NULL, 1, 0, 1);
|
||
}
|
||
else
|
||
{
|
||
bitchsay("BitchX exiting on EOF from stdin");
|
||
irc_exit(1, "BitchX - EOF from stdin", NULL);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int server;
|
||
char loc_buffer[BIG_BUFFER_SIZE + 1];
|
||
int n, i;
|
||
|
||
server = from_server;
|
||
last_input_screen = screen;
|
||
output_screen = screen;
|
||
make_window_current(screen->current_window);
|
||
from_server = current_window->server;
|
||
|
||
#ifdef GUI
|
||
if ((n = gui_read(screen, loc_buffer, BIG_BUFFER_SIZE)) > 0)
|
||
#else
|
||
if ((n = read(screen->fdin, loc_buffer, BIG_BUFFER_SIZE)) > 0)
|
||
#endif
|
||
{
|
||
extended_handled = 0;
|
||
for (i = 0; i < n; i++)
|
||
{
|
||
#ifdef __EMXPM__
|
||
if (loc_buffer[i] == '\0')
|
||
loc_buffer[i] = '\x1b'; /* ESC */
|
||
#endif
|
||
if (!extended_handled)
|
||
edit_char(loc_buffer[i]);
|
||
}
|
||
}
|
||
#ifndef GUI
|
||
#ifdef WINDOW_CREATE
|
||
else if (screen != main_screen)
|
||
kill_screen(screen);
|
||
#endif /* WINDOW_CREATE */
|
||
else
|
||
irc_exit(1, "My damn controlling terminal disappeared!", NULL);
|
||
#endif
|
||
from_server = server;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* Change xterm's title to reflect current connection status.
|
||
*/
|
||
void BX_xterm_settitle(void)
|
||
{
|
||
char titlestring[BIG_BUFFER_SIZE+1];
|
||
char *c;
|
||
|
||
if (!(c = get_current_channel_by_refnum(0)))
|
||
c = "(none)";
|
||
|
||
#if defined(GUI) || defined(WINNT)
|
||
{
|
||
Screen *tmp;
|
||
ChannelList *tmpchanlist;
|
||
int wincount = 0;
|
||
char *s, *o = NULL, *tmptopic = NULL, *tmpwindowchan = NULL;
|
||
|
||
s = fget_string_var(FORMAT_XTERM_TITLE_FSET);
|
||
for (tmp = screen_list; tmp; tmp = tmp->next)
|
||
{
|
||
wincount++;
|
||
if(tmp->alive)
|
||
{
|
||
if (s) {
|
||
tmpwindowchan = tmptopic = NULL;
|
||
if (tmp->current_window->current_channel && (tmpchanlist = lookup_channel(tmp->current_window->current_channel, tmp->current_window->server, 0)))
|
||
tmptopic = tmpchanlist->topic;
|
||
if(!tmp->current_window->current_channel && tmp->current_window->query_nick)
|
||
tmpwindowchan=tmp->current_window->query_nick;
|
||
else
|
||
tmpwindowchan=tmp->current_window->current_channel;
|
||
if ((o = convert_output_format(s, "%d %s %s %s %s %s", wincount, is_server_connected(tmp->current_window->server) ?get_server_nickname(tmp->current_window->server):nickname,
|
||
tmpwindowchan ? tmpwindowchan : "<none>", irc_version, is_server_connected(from_server) ?get_server_itsname(from_server):"<unconnected>", tmptopic ? tmptopic : empty_string)))
|
||
chop(o, 4);
|
||
snprintf(titlestring, sizeof titlestring, "%s", o);
|
||
}
|
||
else
|
||
snprintf(titlestring, sizeof titlestring, "[%d] %s: %s - %s on %s", wincount, irc_version, tmp->current_window->current_channel ? tmp->current_window->current_channel : "<none>", is_server_connected(tmp->current_window->server) ?get_server_nickname(tmp->current_window->server):nickname,
|
||
is_server_connected(tmp->current_window->server) ?get_server_itsname(tmp->current_window->server):"<unconnected>");
|
||
#ifdef GUI
|
||
gui_settitle(stripansicodes(titlestring), tmp);
|
||
#else
|
||
if(current_window && current_window->screen == tmp)
|
||
SetConsoleTitle(titlestring);
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
#else
|
||
if (get_int_var(XTERM_TITLE_VAR) && (getenv("STY") || getenv("DISPLAY")))
|
||
{
|
||
extern char attach_ttyname[];
|
||
char *s, *o = NULL, *t;
|
||
s = fget_string_var(FORMAT_XTERM_TITLE_FSET);
|
||
if (s)
|
||
{
|
||
if ((t = strrchr(attach_ttyname, '/')))
|
||
t++;
|
||
else
|
||
t = attach_ttyname;
|
||
if ((o = convert_output_format(s, "%s %s %s", t, nickname, c ? c : empty_string)))
|
||
chop(o, 4);
|
||
snprintf(titlestring, sizeof titlestring, "\033]2;%s: %s", irc_version, o);
|
||
} else
|
||
snprintf(titlestring, sizeof titlestring, "\033]2;%s: %s on %s:%s", irc_version, is_server_connected(current_window->server) ?get_server_nickname(current_window->server):nickname,
|
||
is_server_connected(current_window->server) ?get_server_itsname(current_window->server):"<not connected>", c);
|
||
tputs_x(titlestring);
|
||
term_flush();
|
||
}
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
* add_wait_prompt: Given a prompt string, a function to call when
|
||
* the prompt is entered.. some other data to pass to the function,
|
||
* and the type of prompt.. either for a line, or a key, we add
|
||
* this to the prompt_list for the current screen.. and set the
|
||
* input prompt accordingly.
|
||
*/
|
||
void BX_add_wait_prompt(char *prompt, void (*func)(char *, char *), char *data, int type, int echo)
|
||
{
|
||
WaitPrompt **AddLoc,
|
||
*New;
|
||
|
||
New = (WaitPrompt *) new_malloc(sizeof(WaitPrompt));
|
||
New->prompt = m_strdup(prompt);
|
||
New->data = m_strdup(data);
|
||
New->type = type;
|
||
New->func = func;
|
||
New->next = NULL;
|
||
New->echo = echo;
|
||
for (AddLoc = ¤t_window->screen->promptlist; *AddLoc;
|
||
AddLoc = &(*AddLoc)->next);
|
||
*AddLoc = New;
|
||
if (AddLoc == ¤t_window->screen->promptlist)
|
||
change_input_prompt(1);
|
||
}
|
||
|
||
/* * * * * * * * * * * * * * * * * COLOR SUPPORT * * * * * * * * * * */
|
||
/*
|
||
* This parses out a ^C control sequence. Note that it is not acceptable
|
||
* to simply slurp up all digits after a ^C sequence (either by calling
|
||
* strtol(), or while (isdigit())), because people put ^C sequences right
|
||
* before legit output with numbers (like the time in your status bar.)
|
||
* Se we have to actually slurp up only those digits that comprise a legal
|
||
* ^C code.
|
||
*/
|
||
char *BX_skip_ctl_c_seq(const char *start, int *lhs, int *rhs, int proper)
|
||
{
|
||
const char *after = start;
|
||
char c1, c2;
|
||
int *val;
|
||
int lv1, rv1;
|
||
|
||
/*
|
||
* For our sanity, just use a placeholder if the caller doesn't
|
||
* care where the end of the ^C code is.
|
||
*/
|
||
if (!lhs)
|
||
lhs = &lv1;
|
||
if (!rhs)
|
||
rhs = &rv1;
|
||
|
||
*lhs = *rhs = -1;
|
||
|
||
/*
|
||
* If we're passed a non ^C code, don't do anything.
|
||
*/
|
||
if (*after != COLOR_CHAR)
|
||
return (char *)after;
|
||
|
||
/*
|
||
* This is a one-or-two-time-through loop. We find the maximum
|
||
* span that can compose a legit ^C sequence, then if the first
|
||
* nonvalid character is a comma, we grab the rhs of the code.
|
||
*/
|
||
val = lhs;
|
||
for (;;)
|
||
{
|
||
/*
|
||
* If its just a lonely old ^C, then its probably a terminator.
|
||
* Just skip over it and go on.
|
||
*/
|
||
after++;
|
||
if (*after == 0)
|
||
return (char *)after;
|
||
|
||
/*
|
||
* Check for the very special case of a definite terminator.
|
||
* If the argument to ^C is -1, then we absolutely know that
|
||
* this ends the code without starting a new one
|
||
*/
|
||
if (after[0] == '-' && after[1] == '1')
|
||
return (char *)after + 2;
|
||
|
||
/*
|
||
* Further checks against a lonely old naked ^C.
|
||
*/
|
||
if (!isdigit((unsigned char)after[0]) && after[0] != ',')
|
||
return (char *)after;
|
||
|
||
|
||
/*
|
||
* Code certainly can't have more than two chars in it
|
||
*/
|
||
c1 = after[0];
|
||
c2 = after[1];
|
||
|
||
/*
|
||
* Our action depends on the char immediately after the ^C.
|
||
*/
|
||
switch (c1)
|
||
{
|
||
/*
|
||
* 0X -> 0X <stop> for all numeric X
|
||
*/
|
||
case '0':
|
||
after++;
|
||
*val = c1 - '0';
|
||
if (c2 >= '0' && c2 <= '9')
|
||
{
|
||
after++;
|
||
*val = *val * 10 + (c2 - '0');
|
||
}
|
||
break;
|
||
|
||
/*
|
||
* 1X -> 1 <stop> X if X >= '6'
|
||
* 1X -> 1X <stop> if X < '6'
|
||
*/
|
||
case '1':
|
||
after++;
|
||
*val = c1 - '0';
|
||
if (c2 >= '0' && c2 < '7')
|
||
{
|
||
after++;
|
||
*val = *val * 10 + (c2 - '0');
|
||
}
|
||
else if (proper && c2 > '5' && c2 <= '9')
|
||
{
|
||
after++;
|
||
*val = *val * 10 + (c2 - '0');
|
||
*val = *val - 16;
|
||
}
|
||
break;
|
||
|
||
/*
|
||
* 3X -> 3 <stop> X if X >= '8'
|
||
* 3X -> 3X <stop> if X < '8'
|
||
* (Same for 4X and 5X)
|
||
*/
|
||
case '3':
|
||
case '4':
|
||
after++;
|
||
*val = c1 - '0';
|
||
if (c2 >= '0' && c2 < '8')
|
||
{
|
||
after++;
|
||
*val = *val * 10 + (c2 - '0');
|
||
}
|
||
break;
|
||
|
||
case '5':
|
||
after++;
|
||
*val = c1 - '0';
|
||
if (c2 >= '0' && c2 < '9')
|
||
{
|
||
after++;
|
||
*val = *val * 10 + (c2 - '0');
|
||
}
|
||
break;
|
||
|
||
/*
|
||
* Y -> Y <stop> for any other numeric Y.
|
||
*/
|
||
case '2':
|
||
case '6':
|
||
case '7':
|
||
case '8':
|
||
case '9':
|
||
*val = (c1 - '0');
|
||
after++;
|
||
break;
|
||
|
||
/*
|
||
* Y -> <stop> Y for any other nonnumeric Y
|
||
*/
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (val == lhs)
|
||
{
|
||
val = rhs;
|
||
if (*after == ',')
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return (char *)after;
|
||
}
|
||
|
||
/*
|
||
* This started off as a general ansi parser, and it worked for stuff that
|
||
* was going out to the display, but it couldnt deal properly with ansi codes,
|
||
* and so when i tried to use it for the status bar, it just all fell to
|
||
* pieces. After working it over, i came up with this. What this does is,
|
||
* (believe it or not) walk through and strip oUt all the ansi codes in the
|
||
* target string. Any codes that we recognize as being safe (pretty much
|
||
* just ^[[<number-list>m), are converted back into their logical characters
|
||
* (eg, ^B, ^R, ^_, etc), and everything else is completely blown away.
|
||
*/
|
||
|
||
/*
|
||
* These macros help keep 8 bit chars from sneaking into the output stream
|
||
* where they might be stripped out.
|
||
*/
|
||
#define this_char() (eightbit ? *str : (*str) & 0x7f)
|
||
#define next_char() (eightbit ? *str++ : (*str++) & 0x7f)
|
||
#define put_back() (str--)
|
||
|
||
char *BX_strip_ansi(const char *str)
|
||
{
|
||
/*
|
||
* Used as a translation table when we can't display graphics characters
|
||
* or we have been told to do translation. A no-brainer, with little attempt
|
||
* at being smart.
|
||
* (JKJ: perhaps we should allow a user to /set this?)
|
||
*/
|
||
const static char gcxlate[256] = {
|
||
'*', '*', '*', '*', '*', '*', '*', '*',
|
||
'#', '*', '#', '*', '*', '*', '*', '*',
|
||
'>', '<', '|', '!', '|', '$', '_', '|',
|
||
'^', 'v', '>', '<', '*', '=', '^', 'v',
|
||
' ', '!', '"', '#', '$', '%', '&', '\'',
|
||
'(', ')', '*', '+', ',', '_', '.', '/',
|
||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||
'8', '9', ':', ';', '<', '=', '>', '?',
|
||
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
||
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
||
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||
'Z', 'Y', 'X', '[', '\\', ']', '^', '_',
|
||
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
||
'x', 'y', 'z', '{', '|', '}', '~', '?',
|
||
'C', 'u', 'e', 'a', 'a', 'a', 'a', 'c',
|
||
'e', 'e', 'e', 'i', 'i', 'i', 'A', 'A',
|
||
'e', 'e', 'e', 'o', 'o', 'o', 'u', 'u',
|
||
'y', 'O', 'U', 'C', '#', 'Y', 'P', 'f',
|
||
'a', 'i', 'o', 'u', 'n', 'N', '^', '^',
|
||
'?', '<', '>', '2', '4', '!', '<', '>',
|
||
'#', '#', '#', '|', '|', '|', '|', '+',
|
||
'+', '+', '+', '|', '+', '+', '+', '+',
|
||
'+', '+', '+', '+', '-', '+', '+', '+',
|
||
'+', '+', '+', '+', '+', '=', '+', '+',
|
||
'+', '+', '+', '+', '+', '+', '+', '+',
|
||
'+', '+', '+', '#', '-', '|', '|', '-',
|
||
'a', 'b', 'P', 'p', 'Z', 'o', 'u', 't',
|
||
'#', 'O', '0', 'O', '-', 'o', 'e', 'U',
|
||
'*', '+', '>', '<', '|', '|', '/', '=',
|
||
'*', '*', '*', '*', 'n', '2', '*', '*'
|
||
};
|
||
|
||
/*
|
||
* State 0 is "normal, printable character"
|
||
* State 1 is "nonprintable character"
|
||
* State 2 is "Escape character" (033)
|
||
* State 3 is "Color code" (003)
|
||
* State 4 is "Special highlight character"
|
||
* State 5 is "ROM character" (022)
|
||
* State 6 is a character that should never be printed
|
||
*/
|
||
const static char ansi_state[256] = {
|
||
/* ^@ ^A ^B ^C ^D ^E ^F ^G */
|
||
6, 6, 4, 3, 6, 4, 4, 4, /* 000 */
|
||
/* ^H ^I ^J ^K ^D ^M ^N ^O */
|
||
6, 4, 4, 6, 0, 6, 6, 4, /* 010 */
|
||
/* ^P ^Q ^R ^S ^T ^U ^V ^W */
|
||
6, 6, 5, 4, 4, 6, 4, 6, /* 020 */
|
||
/* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
|
||
6, 6, 6, 2, 6, 6, 6, 4, /* 030 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 040 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 050 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 060 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 070 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 100 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 110 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 120 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 130 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 140 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 150 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 160 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 170 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 200 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 210 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 220 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 230 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 240 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 250 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 260 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 270 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 300 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 310 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 320 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 330 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 340 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 350 */
|
||
1, 1, 1, 1, 1, 1, 1, 1, /* 360 */
|
||
1, 1, 1, 1, 1, 1, 1, 1 /* 370 */
|
||
};
|
||
|
||
char *output;
|
||
char chr;
|
||
int pos, maxpos;
|
||
int args[10], nargs, i;
|
||
|
||
int bold = 0;
|
||
int underline = 0;
|
||
int reverse = 0;
|
||
int blink = 0;
|
||
int alt_mode = 0;
|
||
int fg_color = 0;
|
||
int bg_color = 1;
|
||
|
||
const int ansi = get_int_var(DISPLAY_ANSI_VAR);
|
||
const int gcmode = get_int_var(DISPLAY_PC_CHARACTERS_VAR);
|
||
const int eightbit = term_eight_bit();
|
||
int n;
|
||
|
||
/*
|
||
* The output string has a few extra chars on the end just
|
||
* in case you need to tack something else onto it.
|
||
*/
|
||
maxpos = strlen(str);
|
||
output = new_malloc(maxpos + 64);
|
||
pos = 0;
|
||
|
||
while ((chr = next_char()))
|
||
{
|
||
/* chr1 is the same as chr, but without the top bit masked. */
|
||
const char chr1 = *(str - 1);
|
||
|
||
if (pos > maxpos)
|
||
{
|
||
maxpos += 64; /* Extend 64 chars at a time */
|
||
RESIZE(output, char, maxpos + 64);
|
||
}
|
||
|
||
switch (ansi_state[(unsigned char)chr1])
|
||
{
|
||
/*
|
||
* State 0 is a normal, printable ascii character
|
||
*/
|
||
case 0:
|
||
output[pos++] = chr;
|
||
break;
|
||
|
||
/*
|
||
* State 1 is an unprintable character that may need
|
||
* to be translated first.
|
||
*/
|
||
case 1:
|
||
case 6:
|
||
{
|
||
int my_gcmode = gcmode;
|
||
/*
|
||
* This is a very paranoid check to make sure that
|
||
* the 8-bit escape code doesn't elude us.
|
||
*/
|
||
if (chr == '\x9b')
|
||
{
|
||
output[pos++] = REV_TOG;
|
||
output[pos++] = '[';
|
||
output[pos++] = REV_TOG;
|
||
continue;
|
||
}
|
||
if (ansi_state[(unsigned char)chr] == 6)
|
||
my_gcmode = 1;
|
||
if (strip_ansi_never_xlate)
|
||
my_gcmode = 4;
|
||
|
||
switch (my_gcmode)
|
||
{
|
||
/*
|
||
* In gcmode 5, translate all characters
|
||
*/
|
||
case 5:
|
||
{
|
||
output[pos++] = gcxlate[(unsigned char)chr];
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* In gcmode 4, accept all characters
|
||
*/
|
||
case 4:
|
||
{
|
||
output[pos++] = chr;
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* In gcmode 3, accept or translate chars
|
||
*/
|
||
case 3:
|
||
{
|
||
if (termfeatures & TERM_CAN_GCHAR)
|
||
output[pos++] = chr;
|
||
else
|
||
output[pos++] = gcxlate[(unsigned char)chr];
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* In gcmode 2, accept or highlight xlate
|
||
*/
|
||
case 2:
|
||
{
|
||
if (termfeatures & TERM_CAN_GCHAR)
|
||
output[pos++] = chr;
|
||
else
|
||
{
|
||
output[pos++] = REV_TOG;
|
||
output[pos++] = gcxlate[(unsigned char)chr];
|
||
output[pos++] = REV_TOG;
|
||
}
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* gcmode 1 is "accept or reverse mangle"
|
||
* If you're doing 8-bit, it accepts eight
|
||
* bit characters. If you're not doing 8 bit
|
||
* then it converts the char into something
|
||
* printable and then reverses it.
|
||
*/
|
||
case 1:
|
||
{
|
||
if ((termfeatures & TERM_CAN_GCHAR) || (chr & 0x80))
|
||
output[pos++] = chr;
|
||
else
|
||
{
|
||
output[pos++] = REV_TOG;
|
||
output[pos++] = chr | 0x40;
|
||
output[pos++] = REV_TOG;
|
||
}
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* gcmode 0 is "always strip out"
|
||
*/
|
||
case 0:
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
|
||
|
||
/*
|
||
* State 2 is the escape character
|
||
*/
|
||
case 2:
|
||
{
|
||
/*
|
||
* The next thing we do is dependant on what the character
|
||
* is after the escape. Be very conservative in what we
|
||
* allow. In general, escape sequences shouldnt be very
|
||
* complex at this point.
|
||
*/
|
||
if (!ansi && this_char() == 0)
|
||
{
|
||
#if 0
|
||
output[pos++] = REV_TOG;
|
||
output[pos++] = '[';
|
||
output[pos++] = REV_TOG;
|
||
#endif
|
||
continue;
|
||
}
|
||
|
||
switch ((chr = next_char()))
|
||
{
|
||
/*
|
||
* All these codes we just skip over. We're not
|
||
* interested in them.
|
||
*/
|
||
|
||
/*
|
||
* These are two-character commands. The second
|
||
* char is the argument.
|
||
*/
|
||
case ('#') : case ('(') : case (')') :
|
||
case ('*') : case ('+') : case ('$') :
|
||
case ('@') :
|
||
{
|
||
next_char(); /* Skip the argument */
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* These are just single-character commands.
|
||
*/
|
||
case ('7') : case ('8') : case ('=') :
|
||
case ('>') : case ('D') : case ('E') :
|
||
case ('F') : case ('H') : case ('M') :
|
||
case ('N') : case ('O') : case ('Z') :
|
||
case ('l') : case ('m') : case ('n') :
|
||
/* { */ case ('o') : case ('|') : case ('}') :
|
||
case ('~') : case ('c') :
|
||
{
|
||
break; /* Don't do anything */
|
||
}
|
||
|
||
/*
|
||
* Swallow up graphics sequences...
|
||
*/
|
||
case ('G'):
|
||
{
|
||
while (((chr = next_char()) != 0) && chr != ':')
|
||
;
|
||
if (chr == 0)
|
||
put_back();
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* Not sure what this is, its unimplemented in
|
||
* rxvt, but its supposed to end with an ESCape.
|
||
*/
|
||
case ('P') :
|
||
{
|
||
while (((chr = next_char()) != 0) && chr != '\033')
|
||
;
|
||
if (chr == 0)
|
||
put_back();
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* Anything else, we just munch the escape and
|
||
* leave it at that.
|
||
*/
|
||
default:
|
||
put_back();
|
||
break;
|
||
|
||
/*
|
||
* Strip out Xterm sequences
|
||
*/
|
||
case (']') :
|
||
{
|
||
while (((chr = next_char()) != 0) && chr != 7)
|
||
;
|
||
if (chr == 0)
|
||
put_back();
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* Now these we're interested in....
|
||
* (CSI sequences)
|
||
*/
|
||
case ('[') :
|
||
{
|
||
/*
|
||
* Set up the arguments list
|
||
*/
|
||
nargs = 0;
|
||
args[0] = args[1] = args[2] = args[3] = 0;
|
||
args[4] = args[5] = args[6] = args[7] = 0;
|
||
args[8] = args[9] = 0;
|
||
|
||
/*
|
||
* This stuff was taken/modified/inspired by
|
||
* rxvt. We do it this way in order to trap
|
||
* an esc sequence that is embedded in another
|
||
* (blah). We're being really really really
|
||
* paranoid doing this, but it is for the best.
|
||
*/
|
||
|
||
/*
|
||
* Check to see if the stuff after the
|
||
* command is a "private" modifier. If it is,
|
||
* then we definitely arent interested.
|
||
* '<' , '=' , '>' , '?'
|
||
*/
|
||
chr = this_char();
|
||
if (chr >= '<' && chr <= '?')
|
||
next_char(); /* skip it */
|
||
|
||
|
||
/*
|
||
* Now pull the arguments off one at a time.
|
||
* Keep pulling them off until we find a
|
||
* character that is not a number or a semi-
|
||
* colon. Skip everything else.
|
||
*/
|
||
for (nargs = 0; nargs < 10; str++)
|
||
{
|
||
n = 0;
|
||
for (n = 0;
|
||
isdigit((unsigned char)this_char());
|
||
next_char())
|
||
{
|
||
n = n * 10 + (this_char() - '0');
|
||
}
|
||
|
||
args[nargs++] = n;
|
||
|
||
/*
|
||
* If we run out of code here,
|
||
* then we're totally confused.
|
||
* just back out with whatever
|
||
* we have...
|
||
*/
|
||
if (!this_char())
|
||
{
|
||
output[pos] = output[pos+1] = 0;
|
||
return output;
|
||
}
|
||
if (this_char() != ';')
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* If we find a new ansi char, start all
|
||
* over from the top and strip it out too
|
||
*/
|
||
if (this_char() == '\033')
|
||
continue;
|
||
|
||
/*
|
||
* Support "spaces" (cursor right) code
|
||
*/
|
||
if (this_char() == 'a' || this_char() == 'C')
|
||
{
|
||
next_char();
|
||
if (nargs >= 1)
|
||
{
|
||
/*
|
||
* Keep this within reality.
|
||
*/
|
||
if (args[0] > 256)
|
||
args[0] = 256;
|
||
|
||
/* This is just sanity */
|
||
if (pos + args[0] > maxpos)
|
||
{
|
||
maxpos += args[0];
|
||
RESIZE(output, char, maxpos + 64);
|
||
}
|
||
while (args[0]-- > 0)
|
||
output[pos++] = ND_SPACE;
|
||
}
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* The 'm' command is the only one that
|
||
* we honor. All others are dumped.
|
||
*/
|
||
if (next_char() != 'm')
|
||
break;
|
||
|
||
if (!ansi)
|
||
break;
|
||
/*
|
||
* Walk all of the numeric arguments,
|
||
* plonking the appropriate commands into
|
||
* the output...
|
||
*/
|
||
for (i = 0; i < nargs; i++)
|
||
{
|
||
if (args[i] == 0)
|
||
{
|
||
output[pos++] = ALL_OFF;
|
||
bold = reverse = blink = 0;
|
||
underline = 0;
|
||
fg_color = bg_color = 0;
|
||
}
|
||
else if (args[i] == 1 && !bold)
|
||
{
|
||
output[pos++] = BOLD_TOG;
|
||
if (fg_color >= 30 &&
|
||
fg_color <= 37)
|
||
{
|
||
fg_color += 20;
|
||
output[pos++] = 3;
|
||
output[pos++] = '5';
|
||
output[pos++] = '0' +
|
||
fg_color % 10;
|
||
}
|
||
bold = 1;
|
||
}
|
||
else if (args[i] == 4 && !underline)
|
||
{
|
||
output[pos++] = UND_TOG;
|
||
underline = 1;
|
||
}
|
||
else if ((args[i] == 5 || args[i] == 26 )
|
||
&& !blink)
|
||
{
|
||
output[pos++] = BLINK_TOG;
|
||
if (bg_color >= 40 &&
|
||
bg_color <= 47)
|
||
{
|
||
bg_color += 10;
|
||
output[pos++] = 3;
|
||
output[pos++] = ',';
|
||
output[pos++] = '5';
|
||
output[pos++] = '0' +
|
||
bg_color % 10;
|
||
}
|
||
blink = 1;
|
||
}
|
||
else if ((args[i] == 6 || args[i] == 25)
|
||
&& blink)
|
||
{
|
||
output[pos++] = BLINK_TOG;
|
||
if (bg_color >= 50 &&
|
||
bg_color <= 57)
|
||
{
|
||
bg_color -= 10;
|
||
output[pos++] = 3;
|
||
output[pos++] = ',';
|
||
output[pos++] = '4';
|
||
output[pos++] = '0' +
|
||
bg_color % 10;
|
||
}
|
||
blink = 0;
|
||
}
|
||
else if (args[i] == 7 && !reverse)
|
||
{
|
||
output[pos++] = REV_TOG;
|
||
reverse = 1;
|
||
}
|
||
else if (args[i] == 21 && bold)
|
||
{
|
||
output[pos++] = BOLD_TOG;
|
||
if (fg_color >= 50 &&
|
||
fg_color <= 57)
|
||
{
|
||
fg_color -= 20;
|
||
output[pos++] = 3;
|
||
output[pos++] = '3';
|
||
output[pos++] = '0' +
|
||
fg_color % 10;
|
||
}
|
||
bold = 0;
|
||
}
|
||
else if (args[i] == 24 && underline)
|
||
{
|
||
output[pos++] = UND_TOG;
|
||
underline = 0;
|
||
}
|
||
else if (args[i] == 27 && reverse)
|
||
{
|
||
output[pos++] = REV_TOG;
|
||
reverse = 0;
|
||
}
|
||
else if (args[i] >= 30 && args[i] <= 37)
|
||
{
|
||
output[pos++] = COLOR_CHAR;
|
||
if (bold)
|
||
output[pos++] = '5';
|
||
else
|
||
output[pos++] = '3';
|
||
output[pos++] = args[i] - 30 + '0';
|
||
if (blink)
|
||
{
|
||
output[pos++] = ',';
|
||
output[pos++] = '5';
|
||
output[pos++] = '8';
|
||
}
|
||
fg_color = args[i];
|
||
}
|
||
else if (args[i] >= 40 && args[i] <= 47)
|
||
{
|
||
output[pos++] = COLOR_CHAR;
|
||
output[pos++] = ',';
|
||
if (blink)
|
||
output[pos++] = '5';
|
||
else
|
||
output[pos++] = '4';
|
||
output[pos++] = args[i] - 40 + '0';
|
||
bg_color = args[i];
|
||
}
|
||
|
||
} /* End of for (handling esc-[...m) */
|
||
} /* End of escape-[ code handling */
|
||
} /* End of ESC handling */
|
||
break;
|
||
} /* End of case 27 handling */
|
||
|
||
|
||
/*
|
||
* Skip over ^C codes, they're already normalized
|
||
* well, that's not totally true. We do some mangling
|
||
* in order to make it work better
|
||
*/
|
||
case 3:
|
||
{
|
||
const char *end;
|
||
int lhs, rhs;
|
||
|
||
put_back();
|
||
end = skip_ctl_c_seq (str, &lhs, &rhs, 0);
|
||
|
||
/*
|
||
* XXX - This is a short-term hack to prevent an
|
||
* infinite loop. I need to come back and fix
|
||
* this the right way in the future.
|
||
*
|
||
* The infinite loop can happen when a character
|
||
* 131 is encountered when eight bit chars is OFF.
|
||
* We see a character 3 (131 with the 8th bit off)
|
||
* and so we ask skip_ctl_c_seq where the end of
|
||
* that sequence is. But since it isn't a ^c sequence
|
||
* it just shrugs its shoulders and returns the
|
||
* pointer as-is. So we sit asking it where the end
|
||
* is and it says "its right here". So there is a
|
||
* need to check the retval of skip_ctl_c_seq to
|
||
* actually see if there is a sequence here. If there
|
||
* is not, then we just mangle this character. For
|
||
* the record, char 131 is a reverse block, so that
|
||
* seems the most appropriate thing to put here.
|
||
*/
|
||
if (end == str)
|
||
{
|
||
/* Turn on reverse if necessary */
|
||
if (reverse == 0)
|
||
output[pos++] = REV_TOG;
|
||
output[pos++] = ' ';
|
||
if (reverse == 0)
|
||
output[pos++] = REV_TOG;
|
||
next_char(); /* Munch it */
|
||
break;
|
||
}
|
||
|
||
|
||
/*
|
||
* If the user specifies an absolute value ^C
|
||
* code (eg, not an mirc order one), then we turn
|
||
* off bold or blink so that their color is rendered
|
||
* exactly like they specify.
|
||
*/
|
||
/*
|
||
* Turn off BOLD if the user specifies a lhs
|
||
*/
|
||
if (lhs != -1 && bold)
|
||
{
|
||
output[pos++] = BOLD_TOG;
|
||
bold = 0;
|
||
}
|
||
|
||
/*
|
||
* Ditto with BLINK for rhs
|
||
*/
|
||
if (rhs != -1 && blink)
|
||
{
|
||
output[pos++] = BLINK_TOG;
|
||
blink = 0;
|
||
}
|
||
|
||
while (str < end)
|
||
output[pos++] = next_char();
|
||
|
||
fg_color = lhs;
|
||
bg_color = rhs;
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* State 4 is for the special highlight characters
|
||
*/
|
||
case 4:
|
||
{
|
||
put_back();
|
||
switch (this_char())
|
||
{
|
||
case REV_TOG: reverse = !reverse; break;
|
||
case BOLD_TOG: bold = !bold; break;
|
||
case BLINK_TOG: blink = !blink; break;
|
||
case UND_TOG: underline = !underline; break;
|
||
case ALT_TOG: alt_mode = !alt_mode; break;
|
||
case ALL_OFF:
|
||
{
|
||
reverse = bold = blink = underline = 0;
|
||
alt_mode = 0;
|
||
break;
|
||
}
|
||
default: break;
|
||
}
|
||
|
||
output[pos++] = next_char();
|
||
break;
|
||
}
|
||
|
||
case 5:
|
||
{
|
||
put_back();
|
||
if (str[0] && str[1] && str[2] && str[3])
|
||
{
|
||
output[pos++] = next_char();
|
||
output[pos++] = next_char();
|
||
output[pos++] = next_char();
|
||
output[pos++] = next_char();
|
||
}
|
||
else
|
||
output[pos++] = next_char();
|
||
break;
|
||
}
|
||
|
||
default:
|
||
{
|
||
ircpanic("Unknown strip_ansi mode");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
output[pos] = output[pos + 1] = 0;
|
||
return output;
|
||
}
|
||
|
||
/*
|
||
* put_color()
|
||
*
|
||
* Converts ^C color code pairs into terminal specific color changes,
|
||
* and sends them to the terminal.
|
||
*
|
||
* Code foreground background
|
||
* -----------------------------------------------------------
|
||
* 0 to 15 MIRC-style color codes MIRC-style color codes
|
||
* 30 to 37 term colors 0 to 7 (invalid)
|
||
* 40 to 37 (invalid) term colors 0 to 7
|
||
* 50 to 57 term colors 8 to 15 term colors 8 to 15
|
||
* 58 (invalid) blinking on
|
||
*
|
||
* Setting both foreground and background to -1 sets the terminal
|
||
* to "normal", and setting both to -2 re-applies the last set colors.
|
||
*/
|
||
void put_color(int fore, int back)
|
||
{
|
||
char retbuf[512];
|
||
static int last_fore = -2, last_back = -2;
|
||
|
||
const static int fore_conv[] = {
|
||
15, 0, 4, 2, 1, 3, 5, 9, /* 0-7 */
|
||
11, 10, 6, 14, 12, 13, 8, 7, /* 8-15 */
|
||
15, 0, 0, 0, 0, 0, 0, 0, /* 16-23 */
|
||
0, 0, 0, 0, 0, 0, 0, 1, /* 24-31 */
|
||
2, 3, 4, 5, 6, 7, 0, 0, /* 32-39 */
|
||
0, 0, 0, 0, 0, 0, 0, 0, /* 40-47 */
|
||
0, 0, 8, 9, 10, 11, 12, 13, /* 48-55 */
|
||
14, 15 /* 56-57 */
|
||
};
|
||
const static int back_conv[] = {
|
||
7, 0, 4, 2, 1, 3, 5, 1,
|
||
3, 2, 6, 6, 4, 5, 0, 0,
|
||
7, 0, 0, 0, 0, 0, 0, 0,
|
||
0, 0, 0, 0, 0, 0, 0, 0,
|
||
0, 0, 0, 0, 0, 0, 0, 0,
|
||
0, 1, 2, 3, 4, 5, 6, 7,
|
||
0, 0, 8, 9, 10, 11, 12, 13,
|
||
14, 15
|
||
};
|
||
|
||
if (!get_int_var(COLOR_VAR))
|
||
return;
|
||
|
||
if (fore == -2 && back == -2)
|
||
{
|
||
back = fore = -1;
|
||
}
|
||
else if (fore == -1 && back == -1)
|
||
{
|
||
last_fore = last_back = -2;
|
||
tputs_x(current_term->TI_sgrstrs[TERM_SGR_NORMAL-1]);
|
||
return;
|
||
}
|
||
|
||
if (fore == -1)
|
||
fore = last_fore;
|
||
if (back == -1)
|
||
back = last_back;
|
||
|
||
retbuf[0] = '\0';
|
||
if (back == 58)
|
||
strcat(retbuf, current_term->TI_sgrstrs[TERM_SGR_BLINK_ON - 1]);
|
||
if (fore > -1 && fore < 58)
|
||
strcat(retbuf, current_term->TI_forecolors[fore_conv[fore]]);
|
||
if (back > -1 && back < 58)
|
||
strcat(retbuf, current_term->TI_backcolors[back_conv[back]]);
|
||
|
||
last_fore = fore;
|
||
last_back = back;
|
||
|
||
tputs_x(retbuf);
|
||
}
|