2019-05-01 22:08:45 +02:00

1442 lines
32 KiB
C

/*
formats.c : irssi
Copyright (C) 1999-2000 Timo Sirainen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include <irssi/src/fe-common/core/module-formats.h>
#include <irssi/src/core/signals.h>
#include <irssi/src/core/special-vars.h>
#include <irssi/src/core/settings.h>
#include <irssi/src/core/levels.h>
#include <irssi/src/core/servers.h>
#include <irssi/src/fe-common/core/fe-windows.h>
#include <irssi/src/fe-common/core/window-items.h>
#include <irssi/src/fe-common/core/formats.h>
#include <irssi/src/fe-common/core/themes.h>
#include <irssi/src/core/recode.h>
#include <irssi/src/core/utf8.h>
#include <irssi/src/core/misc.h>
static const char *format_backs = "04261537";
static const char *format_fores = "kbgcrmyw";
static const char *format_boldfores = "KBGCRMYW";
static int signal_gui_print_text;
static int hide_text_style, hide_server_tags, hide_colors;
static int timestamp_level;
static int timestamp_timeout;
int format_find_tag(const char *module, const char *tag)
{
FORMAT_REC *formats;
int n;
formats = g_hash_table_lookup(default_formats, module);
if (formats == NULL)
return -1;
for (n = 0; formats[n].def != NULL; n++) {
if (formats[n].tag != NULL &&
g_ascii_strcasecmp(formats[n].tag, tag) == 0)
return n;
}
return -1;
}
static void format_expand_code(const char **format, GString *out, int *flags)
{
int set;
if (flags == NULL) {
/* flags are being ignored - skip the code */
while (**format != ']' && **format != '\0')
(*format)++;
return;
}
set = TRUE;
(*format)++;
while (**format != ']' && **format != '\0') {
if (**format == '+')
set = TRUE;
else if (**format == '-')
set = FALSE;
else switch (**format) {
case 's':
case 'S':
*flags |= !set ? PRINT_FLAG_UNSET_LINE_START :
**format == 's' ? PRINT_FLAG_SET_LINE_START :
PRINT_FLAG_SET_LINE_START_IRSSI;
break;
case 't':
*flags |= set ? PRINT_FLAG_SET_TIMESTAMP :
PRINT_FLAG_UNSET_TIMESTAMP;
break;
case 'T':
*flags |= set ? PRINT_FLAG_SET_SERVERTAG :
PRINT_FLAG_UNSET_SERVERTAG;
break;
}
(*format)++;
}
}
void format_ext_color(GString *out, int bg, int color)
{
g_string_append_c(out, 4);
if (bg && color < 0x10)
g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
if (color < 0x10)
g_string_append_c(out, color+'0');
else {
if (color < 0x60)
g_string_append_c(out, bg ? FORMAT_COLOR_EXT1_BG
: FORMAT_COLOR_EXT1);
else if (color < 0xb0)
g_string_append_c(out, bg ? FORMAT_COLOR_EXT2_BG
: FORMAT_COLOR_EXT2);
else
g_string_append_c(out, bg ? FORMAT_COLOR_EXT3_BG
: FORMAT_COLOR_EXT3);
g_string_append_c(out, FORMAT_COLOR_NOCHANGE + ((color-0x10)%0x50));
}
if (!bg && color < 0x10)
g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
}
#ifdef TERM_TRUECOLOR
void unformat_24bit_color(char **ptr, int off, int *fgcolor, int *bgcolor, int *flags)
{
unsigned int color;
unsigned char rgbx[4];
unsigned int i;
for (i = 0; i < 4; ++i) {
if ((*ptr)[i + off] == '\0')
return;
rgbx[i] = (*ptr)[i + off];
}
rgbx[3] -= 0x20;
*ptr += 4;
for (i = 0; i < 3; ++i) {
if (rgbx[3] & (0x10 << i))
rgbx[i] -= 0x20;
}
color = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
if (rgbx[3] & 0x1) {
*bgcolor = color;
*flags |= GUI_PRINT_FLAG_COLOR_24_BG;
}
else {
*fgcolor = color;
*flags |= GUI_PRINT_FLAG_COLOR_24_FG;
}
}
#endif
void format_24bit_color(GString *out, int bg, unsigned int color)
{
unsigned char rgb[] = { color >> 16, color >> 8, color };
#ifdef TERM_TRUECOLOR
unsigned char x = bg ? 0x1 : 0;
unsigned int i;
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_COLOR_24);
for (i = 0; i < 3; ++i) {
if (rgb[i] > 0x20)
g_string_append_c(out, rgb[i]);
else {
g_string_append_c(out, 0x20 + rgb[i]);
x |= 0x10 << i;
}
}
g_string_append_c(out, 0x20 + x);
#else /* !TERM_TRUECOLOR */
format_ext_color(out, bg, color_24bit_256(rgb));
#endif /* TERM_TRUECOLOR */
}
int format_expand_styles(GString *out, const char **format, int *flags)
{
int retval = 1;
char *p, fmt;
/* storage for numerical parsing code for %x/X formats. */
int tmp;
unsigned int tmp2;
fmt = **format;
switch (fmt) {
case '{':
case '}':
case '%':
/* escaped char */
g_string_append_c(out, fmt);
break;
case 'U':
/* Underline on/off */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_UNDERLINE);
break;
case '9':
case '_':
/* bold on/off */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_BOLD);
break;
case '8':
/* reverse */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_REVERSE);
break;
case 'I':
/* italic */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_ITALIC);
break;
case ':':
/* Newline */
g_string_append_c(out, '\n');
break;
case '|':
/* Indent here */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_INDENT);
break;
case 'F':
/* blink */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_BLINK);
break;
case 'n':
case 'N':
/* default color */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_DEFAULTS);
break;
case '>':
/* clear to end of line */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_CLRTOEOL);
break;
case '#':
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_MONOSPACE);
break;
case '[':
/* code */
format_expand_code(format, out, flags);
if ((*format)[0] == '\0')
/* oops, reached end prematurely */
(*format)--;
break;
case 'x':
case 'X':
if ((*format)[1] < '0' || (*format)[1] > '7')
break;
tmp = 16 + ((*format)[1]-'0'-1)*36;
if (tmp > 231) {
if (!isalpha((*format)[2]))
break;
tmp += (*format)[2] >= 'a' ? (*format)[2] - 'a' : (*format)[2] - 'A';
if (tmp > 255)
break;
}
else if (tmp > 0) {
if (!isalnum((*format)[2]))
break;
if ((*format)[2] >= 'a')
tmp += 10 + (*format)[2] - 'a';
else if ((*format)[2] >= 'A')
tmp += 10 + (*format)[2] - 'A';
else
tmp += (*format)[2] - '0';
}
else {
if (!isxdigit((*format)[2]))
break;
tmp = g_ascii_xdigit_value((*format)[2]);
}
retval += 2;
format_ext_color(out, fmt == 'x', tmp);
break;
case 'z':
case 'Z':
tmp2 = 0;
for (tmp = 1; tmp < 7; ++tmp) {
if (!isxdigit((*format)[tmp])) {
tmp2 = UINT_MAX;
break;
}
tmp2 <<= 4;
tmp2 |= g_ascii_xdigit_value((*format)[tmp]);
}
if (tmp2 == UINT_MAX)
break;
retval += 6;
format_24bit_color(out, fmt == 'z', tmp2);
break;
default:
/* check if it's a background color */
p = strchr(format_backs, fmt);
if (p != NULL) {
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
g_string_append_c(out, (char) ((int) (p-format_backs)+'0'));
break;
}
/* check if it's a foreground color */
if (fmt == 'p') fmt = 'm';
p = strchr(format_fores, fmt);
if (p != NULL) {
g_string_append_c(out, 4);
g_string_append_c(out, (char) ((int) (p-format_fores)+'0'));
g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
break;
}
/* check if it's a bold foreground color */
if (fmt == 'P') fmt = 'M';
p = strchr(format_boldfores, fmt);
if (p != NULL) {
g_string_append_c(out, 4);
g_string_append_c(out, (char) (8+(int) (p-format_boldfores)+'0'));
g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
break;
}
return FALSE;
}
return retval;
}
void format_read_arglist(va_list va, FORMAT_REC *format,
char **arglist, int arglist_size,
char *buffer, int buffer_size)
{
int num, len, bufpos;
g_return_if_fail(format->params < arglist_size);
bufpos = 0;
arglist[format->params] = NULL;
for (num = 0; num < format->params; num++) {
switch (format->paramtypes[num]) {
case FORMAT_STRING:
arglist[num] = (char *) va_arg(va, char *);
if (arglist[num] == NULL)
arglist[num] = "";
break;
case FORMAT_INT: {
int d = (int) va_arg(va, int);
if (bufpos >= buffer_size) {
arglist[num] = "";
break;
}
arglist[num] = buffer+bufpos;
len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
"%d", d);
bufpos += len+1;
break;
}
case FORMAT_LONG: {
long l = (long) va_arg(va, long);
if (bufpos >= buffer_size) {
arglist[num] = "";
break;
}
arglist[num] = buffer+bufpos;
len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
"%ld", l);
bufpos += len+1;
break;
}
case FORMAT_FLOAT: {
double f = (double) va_arg(va, double);
if (bufpos >= buffer_size) {
arglist[num] = "";
break;
}
arglist[num] = buffer+bufpos;
len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
"%0.2f", f);
bufpos += len+1;
break;
}
}
}
}
void format_create_dest(TEXT_DEST_REC *dest,
void *server, const char *target,
int level, WINDOW_REC *window)
{
format_create_dest_tag(dest, server, NULL, target, level, window);
}
void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
const char *server_tag, const char *target,
int level, WINDOW_REC *window)
{
memset(dest, 0, sizeof(TEXT_DEST_REC));
dest->server = server;
dest->server_tag = server != NULL ? SERVER(server)->tag : server_tag;
dest->target = target;
dest->level = level;
dest->window = window != NULL ? window :
window_find_closest(server, target, level);
}
/* Return length of text part in string (ie. without % codes) */
int format_get_length(const char *str)
{
GString *tmp;
int len;
int utf8;
int adv = 0;
g_return_val_if_fail(str != NULL, 0);
utf8 = string_policy(str);
tmp = g_string_new(NULL);
len = 0;
while (*str != '\0') {
if (*str == '%' && str[1] != '\0') {
str++;
if (*str != '%') {
adv = format_expand_styles(tmp, &str, NULL);
str += adv;
if (adv)
continue;
}
/* %% or unknown %code, written as-is */
if (*str != '%')
len++;
}
len += string_advance(&str, utf8);
}
g_string_free(tmp, TRUE);
return len;
}
/* Return how many characters in `str' must be skipped before `len'
characters of text is skipped. Like strip_real_length(), except this
handles %codes. */
int format_real_length(const char *str, int len)
{
GString *tmp;
const char *start;
const char *oldstr;
int utf8;
int adv = 0;
g_return_val_if_fail(str != NULL, 0);
g_return_val_if_fail(len >= 0, 0);
utf8 = string_policy(str);
start = str;
tmp = g_string_new(NULL);
while (*str != '\0') {
oldstr = str;
if (*str == '%' && str[1] != '\0') {
str++;
if (*str != '%') {
adv = format_expand_styles(tmp, &str, NULL);
if (adv) {
str += adv;
continue;
}
/* discount for unknown % */
if (--len < 0) {
str = oldstr;
break;
}
oldstr = str;
}
}
len -= string_advance(&str, utf8);
if (len < 0) {
str = oldstr;
break;
}
}
g_string_free(tmp, TRUE);
return (int) (str-start);
}
char *format_string_expand(const char *text, int *flags)
{
GString *out;
char code, *ret;
int adv;
g_return_val_if_fail(text != NULL, NULL);
out = g_string_new(NULL);
if (flags != NULL) *flags = 0;
code = 0;
while (*text != '\0') {
if (code == '%') {
/* color code */
adv = format_expand_styles(out, &text, flags);
if (!adv) {
g_string_append_c(out, '%');
g_string_append_c(out, '%');
g_string_append_c(out, *text);
} else {
text += adv - 1;
}
code = 0;
} else {
if (*text == '%')
code = *text;
else
g_string_append_c(out, *text);
}
text++;
}
ret = out->str;
g_string_free(out, FALSE);
return ret;
}
static char *format_get_text_args(TEXT_DEST_REC *dest,
const char *text, char **arglist)
{
GString *out;
char code, *ret;
int need_free;
int adv;
out = g_string_new(NULL);
code = 0;
while (*text != '\0') {
if (code == '%') {
/* color code */
adv = format_expand_styles(out, &text, &dest->flags);
if (!adv) {
g_string_append_c(out, '%');
g_string_append_c(out, '%');
g_string_append_c(out, *text);
} else {
text += adv - 1;
}
code = 0;
} else if (code == '$') {
/* argument */
char *ret;
ret = parse_special((char **) &text, dest->server,
dest->target == NULL ? NULL :
window_item_find(dest->server, dest->target),
arglist, &need_free, NULL, 0);
if (ret != NULL) {
/* string shouldn't end with \003 or it could
mess up the next one or two characters */
int diff;
int len = strlen(ret);
while (len > 0 && ret[len-1] == 3) len--;
diff = strlen(ret)-len;
g_string_append(out, ret);
if (diff > 0)
g_string_truncate(out, out->len-diff);
if (need_free) g_free(ret);
}
code = 0;
} else {
if (*text == '%' || *text == '$')
code = *text;
else
g_string_append_c(out, *text);
}
text++;
}
ret = out->str;
g_string_free(out, FALSE);
return ret;
}
char *format_get_text_theme(THEME_REC *theme, const char *module,
TEXT_DEST_REC *dest, int formatnum, ...)
{
va_list va;
char *str;
if (theme == NULL)
theme = window_get_theme(dest->window);
va_start(va, formatnum);
str = format_get_text_theme_args(theme, module, dest, formatnum, va);
va_end(va);
return str;
}
char *format_get_text_theme_args(THEME_REC *theme, const char *module,
TEXT_DEST_REC *dest, int formatnum,
va_list va)
{
char *arglist[MAX_FORMAT_PARAMS];
char buffer[DEFAULT_FORMAT_ARGLIST_SIZE];
FORMAT_REC *formats;
formats = g_hash_table_lookup(default_formats, module);
format_read_arglist(va, &formats[formatnum],
arglist, sizeof(arglist)/sizeof(char *),
buffer, sizeof(buffer));
return format_get_text_theme_charargs(theme, module, dest,
formatnum, arglist);
}
char *format_get_text_theme_charargs(THEME_REC *theme, const char *module,
TEXT_DEST_REC *dest, int formatnum,
char **args)
{
MODULE_THEME_REC *module_theme;
char *text;
module_theme = g_hash_table_lookup(theme->modules, module);
if (module_theme == NULL)
return NULL;
text = module_theme->expanded_formats[formatnum];
return format_get_text_args(dest, text, args);
}
char *format_get_text(const char *module, WINDOW_REC *window,
void *server, const char *target,
int formatnum, ...)
{
TEXT_DEST_REC dest;
THEME_REC *theme;
va_list va;
char *str;
format_create_dest(&dest, server, target, 0, window);
theme = window_get_theme(dest.window);
va_start(va, formatnum);
str = format_get_text_theme_args(theme, module, &dest, formatnum, va);
va_end(va);
return str;
}
/* add `linestart' to start of each line in `text'. `text' may contain
multiple lines separated with \n. */
char *format_add_linestart(const char *text, const char *linestart)
{
GString *str;
char *ret;
if (linestart == NULL)
return g_strdup(text);
if (strchr(text, '\n') == NULL)
return g_strconcat(linestart, text, NULL);
str = g_string_new(linestart);
while (*text != '\0') {
g_string_append_c(str, *text);
if (*text == '\n')
g_string_append(str, linestart);
text++;
}
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
char *format_add_lineend(const char *text, const char *linestart)
{
GString *str;
char *ret;
if (linestart == NULL)
return g_strdup(text);
if (strchr(text, '\n') == NULL)
return g_strconcat(text, linestart, NULL);
str = g_string_new(NULL);
while (*text != '\0') {
if (*text == '\n')
g_string_append(str, linestart);
g_string_append_c(str, *text);
text++;
}
g_string_append(str, linestart);
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
#define LINE_START_IRSSI_LEVEL \
(MSGLEVEL_CLIENTERROR | MSGLEVEL_CLIENTNOTICE)
#define NOT_LINE_START_LEVEL \
(MSGLEVEL_NEVER | MSGLEVEL_LASTLOG | MSGLEVEL_CLIENTCRAP | \
MSGLEVEL_MSGS | MSGLEVEL_PUBLIC | MSGLEVEL_DCC | MSGLEVEL_DCCMSGS | \
MSGLEVEL_ACTIONS | MSGLEVEL_NOTICES | MSGLEVEL_SNOTES | MSGLEVEL_CTCPS)
/* return the "-!- " text at the start of the line */
char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
{
int format;
/* check for flags if we want to override defaults */
if (dest->flags & PRINT_FLAG_UNSET_LINE_START)
return NULL;
if (dest->flags & PRINT_FLAG_SET_LINE_START)
format = TXT_LINE_START;
else if (dest->flags & PRINT_FLAG_SET_LINE_START_IRSSI)
format = TXT_LINE_START_IRSSI;
else {
/* use defaults */
if (dest->level & LINE_START_IRSSI_LEVEL)
format = TXT_LINE_START_IRSSI;
else if ((dest->level & NOT_LINE_START_LEVEL) == 0)
format = TXT_LINE_START;
else
return NULL;
}
return format_get_text_theme(theme, MODULE_NAME, dest, format);
}
static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
{
char *format, str[256];
struct tm *tm;
int diff;
if ((timestamp_level & dest->level) == 0)
return NULL;
/* check for flags if we want to override defaults */
if (dest->flags & PRINT_FLAG_UNSET_TIMESTAMP)
return NULL;
if ((dest->flags & PRINT_FLAG_SET_TIMESTAMP) == 0 &&
(dest->level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) != 0)
return NULL;
if (timestamp_timeout > 0) {
diff = t - dest->window->last_timestamp;
dest->window->last_timestamp = t;
if (diff < timestamp_timeout)
return NULL;
}
tm = localtime(&t);
format = format_get_text_theme(theme, MODULE_NAME, dest,
TXT_TIMESTAMP);
if (strftime(str, sizeof(str), format, tm) <= 0)
str[0] = '\0';
g_free(format);
return g_strdup(str);
}
static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
{
int count = 0;
if (dest->server_tag == NULL || hide_server_tags)
return NULL;
/* check for flags if we want to override defaults */
if (dest->flags & PRINT_FLAG_UNSET_SERVERTAG)
return NULL;
if ((dest->flags & PRINT_FLAG_SET_SERVERTAG) == 0) {
if (dest->window->active != NULL &&
dest->window->active->server == dest->server)
return NULL;
if (servers != NULL) {
count++;
if (servers->next != NULL)
count++;
}
if (count < 2 && lookup_servers != NULL) {
count++;
if (lookup_servers->next != NULL)
count++;
}
if (count < 2)
return NULL;
}
return format_get_text_theme(theme, MODULE_NAME, dest,
TXT_SERVERTAG, dest->server_tag);
}
char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
{
char *timestamp, *servertag;
char *linestart;
timestamp = get_timestamp(theme, dest, t);
servertag = get_server_tag(theme, dest);
if (timestamp == NULL && servertag == NULL)
return NULL;
linestart = g_strconcat(timestamp != NULL ? timestamp : "",
servertag, NULL);
g_free_not_null(timestamp);
g_free_not_null(servertag);
return linestart;
}
void format_newline(WINDOW_REC *window)
{
g_return_if_fail(window != NULL);
signal_emit_id(signal_gui_print_text, 6, window,
GINT_TO_POINTER(-1), GINT_TO_POINTER(-1),
GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE),
"", NULL);
}
#ifndef TERM_TRUECOLOR
inline static int color_24bit_256_int(unsigned int color)
{
unsigned char rgb[] = { color >> 16, color >> 8, color };
return color_24bit_256(rgb);
}
#endif /* !TERM_TRUECOLOR */
/* parse ANSI color string */
static const char *get_ansi_color(THEME_REC *theme, const char *str,
int *fg_ret, int *bg_ret, int *flags_ret)
{
static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
const char *start;
char *endptr;
int fg, bg, flags, i;
guint num, num2;
if (*str != '[')
return str;
start = str++;
fg = fg_ret == NULL || *fg_ret < 0 ? theme->default_color : *fg_ret;
bg = bg_ret == NULL || *bg_ret < 0 ? -1 : *bg_ret;
flags = flags_ret == NULL ? 0 : *flags_ret;
num = 0;
for (;; str++) {
if (*str == '\0') return start;
if (i_isdigit(*str)) {
if (!parse_uint(str, &endptr, 10, &num)) {
return start;
}
str = endptr;
}
if (*str != ';' && *str != 'm')
return start;
switch (num) {
case 0:
/* reset colors and attributes back to default */
fg = theme->default_color;
bg = -1;
flags &= ~(GUI_PRINT_FLAG_INDENT |
GUI_PRINT_FLAG_BOLD | GUI_PRINT_FLAG_ITALIC | GUI_PRINT_FLAG_UNDERLINE |
GUI_PRINT_FLAG_BLINK | GUI_PRINT_FLAG_REVERSE |
GUI_PRINT_FLAG_COLOR_24_FG | GUI_PRINT_FLAG_COLOR_24_BG);
break;
case 1:
/* hilight */
flags |= GUI_PRINT_FLAG_BOLD;
break;
case 22:
/* normal */
flags &= ~GUI_PRINT_FLAG_BOLD;
break;
case 3:
/* italic */
flags |= GUI_PRINT_FLAG_ITALIC;
break;
case 23:
/* not italic */
flags &= ~GUI_PRINT_FLAG_ITALIC;
break;
case 4:
/* underline */
flags |= GUI_PRINT_FLAG_UNDERLINE;
break;
case 24:
/* not underline */
flags &= ~GUI_PRINT_FLAG_UNDERLINE;
break;
case 5:
/* blink */
flags |= GUI_PRINT_FLAG_BLINK;
break;
case 25:
/* steady */
flags &= ~GUI_PRINT_FLAG_BLINK;
break;
case 7:
/* reverse */
flags |= GUI_PRINT_FLAG_REVERSE;
break;
case 27:
/* positive */
flags &= ~GUI_PRINT_FLAG_REVERSE;
break;
case 39:
/* reset fg */
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fg = theme->default_color;
break;
case 49:
/* reset bg */
bg = -1;
flags &= ~(GUI_PRINT_FLAG_COLOR_24_BG | GUI_PRINT_FLAG_INDENT);
break;
case 38:
case 48:
/* ANSI indexed color or RGB color */
if (*str != ';') break;
str++;
if (!parse_uint(str, &endptr, 10, &num2)) {
return start;
}
str = endptr;
if (*str == '\0') return start;
switch (num2) {
case 2:
/* RGB */
num2 = 0;
for (i = 0; i < 3; ++i) {
num2 <<= 8;
if (*str != ';' && *str != ':') {
i = -1;
break;
}
str++;
for (; i_isdigit(*str); str++)
num2 = (num2&~0xff) |
(((num2&0xff) * 10 + (*str-'0'))&0xff);
if (*str == '\0') return start;
}
if (i == -1) break;
#ifdef TERM_TRUECOLOR
if (num == 38) {
flags |= GUI_PRINT_FLAG_COLOR_24_FG;
fg = num2;
} else if (num == 48) {
flags |= GUI_PRINT_FLAG_COLOR_24_BG;
bg = num2;
}
#else /* !TERM_TRUECOLOR */
if (num == 38) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fg = color_24bit_256_int(num2);
} else if (num == 48) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
bg = color_24bit_256_int(num2);
}
#endif
break;
case 5:
/* indexed */
if (*str != ';') break;
str++;
if (!parse_uint(str, &endptr, 10, &num2)) {
return start;
}
str = endptr;
if (*str == '\0') return start;
if (num == 38) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fg = num2;
} else if (num == 48) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
bg = num2;
}
break;
}
break;
default:
if (num >= 30 && num <= 37) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fg = ansitab[num-30];
} else if (num >= 40 && num <= 47) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
bg = ansitab[num-40];
} else if (num >= 90 && num <= 97) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fg = 8 + ansitab[num-90];
} else if (num >= 100 && num <= 107) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
bg = 8 + ansitab[num-100];
}
break;
}
num = 0;
if (*str == 'm') {
if (fg_ret != NULL) *fg_ret = fg;
if (bg_ret != NULL) *bg_ret = bg;
if (flags_ret != NULL) *flags_ret = flags;
str++;
break;
}
}
return str;
}
/* parse MIRC color string */
static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret)
{
int fg, bg;
fg = fg_ret == NULL ? -1 : *fg_ret;
bg = bg_ret == NULL ? -1 : *bg_ret;
if (!i_isdigit(**str)) {
/* turn off color */
fg = -1;
bg = -1;
} else {
/* foreground color */
fg = **str-'0';
(*str)++;
if (i_isdigit(**str)) {
fg = fg*10 + (**str-'0');
(*str)++;
}
if ((*str)[0] == ',' && i_isdigit((*str)[1])) {
/* background color */
(*str)++;
bg = **str-'0';
(*str)++;
if (i_isdigit(**str)) {
bg = bg*10 + (**str-'0');
(*str)++;
}
}
}
if (fg_ret) *fg_ret = fg;
if (bg_ret) *bg_ret = bg;
}
#define IS_COLOR_CODE(c) \
((c) == 2 || (c) == 3 || (c) == 4 || (c) == 6 || (c) == 7 || \
(c) == 15 || (c) == 22 || (c) == 27 || (c) == 29 || (c) == 31)
/* Return how many characters in `str' must be skipped before `len'
characters of text is skipped. */
int strip_real_length(const char *str, int len,
int *last_color_pos, int *last_color_len)
{
const char *start = str;
if (last_color_pos != NULL)
*last_color_pos = -1;
if (last_color_len != NULL)
*last_color_len = -1;
while (*str != '\0') {
if (*str == 3) { /* mIRC color */
const char *mircstart = str;
if (last_color_pos != NULL)
*last_color_pos = (int) (str-start);
str++;
get_mirc_color(&str, NULL, NULL);
if (last_color_len != NULL)
*last_color_len = (int) (str-mircstart);
} else if (*str == 4 && str[1] != '\0') {
/* We expect 4 to indicate an internal Irssi color code. However 4
* also means hex color, an alternative to mIRC color codes. We
* don't support those. */
#ifdef TERM_TRUECOLOR
if (str[1] == FORMAT_COLOR_24 && str[2] != '\0') {
if (str[3] == '\0') str++;
else if (str[4] == '\0') str += 2;
else if (str[5] == '\0') str += 3;
else {
if (last_color_pos != NULL)
*last_color_pos = (int) (str-start);
if (last_color_len != NULL)
*last_color_len = 6;
str+=4;
}
} else
#endif
if (str[1] < FORMAT_STYLE_SPECIAL && str[2] != '\0') {
if (last_color_pos != NULL)
*last_color_pos = (int) (str-start);
if (last_color_len != NULL)
*last_color_len = 3;
str++;
} else if (str[1] == FORMAT_STYLE_DEFAULTS) {
if (last_color_pos != NULL)
*last_color_pos = (int) (str-start);
if (last_color_len != NULL)
*last_color_len = 2;
}
str += 2;
} else {
if (!IS_COLOR_CODE(*str)) {
if (len-- == 0)
break;
}
str++;
}
}
return (int) (str-start);
}
char *strip_codes(const char *input)
{
const char *p;
char *str, *out;
out = str = g_strdup(input);
for (p = input; *p != '\0'; p++) {
if (*p == 3) {
p++;
/* mirc color */
get_mirc_color(&p, NULL, NULL);
p--;
continue;
}
if (*p == 4 && p[1] != '\0') {
if (p[1] >= FORMAT_STYLE_SPECIAL) {
p++;
continue;
}
/* irssi color */
if (p[2] != '\0') {
#ifdef TERM_TRUECOLOR
if (p[1] == FORMAT_COLOR_24) {
if (p[3] == '\0') p += 2;
else if (p[4] == '\0') p += 3;
else if (p[5] == '\0') p += 4;
else p += 5;
} else
#endif /* TERM_TRUECOLOR */
p += 2;
continue;
}
}
if (*p == 27 && p[1] != '\0') {
p++;
p = get_ansi_color(current_theme, p, NULL, NULL, NULL);
p--;
} else if (!IS_COLOR_CODE(*p))
*out++ = *p;
}
*out = '\0';
return str;
}
/* send a fully parsed text string for GUI to print */
void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
{
THEME_REC *theme;
char *dup, *str, *ptr, type;
int fgcolor, bgcolor;
int flags;
theme = window_get_theme(dest->window);
dup = str = g_strdup(text);
flags = 0; fgcolor = theme->default_color; bgcolor = -1;
if (*str == '\0') {
/* empty line, write line info only */
signal_emit_id(signal_gui_print_text, 6, dest->window, GINT_TO_POINTER(fgcolor),
GINT_TO_POINTER(bgcolor), GINT_TO_POINTER(flags), str, dest);
}
while (*str != '\0') {
type = '\0';
for (ptr = str; *ptr != '\0'; ptr++) {
if (IS_COLOR_CODE(*ptr) || *ptr == '\n') {
type = *ptr;
*ptr++ = '\0';
break;
}
}
if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) {
/* clear to end of line */
flags |= GUI_PRINT_FLAG_CLRTOEOL;
}
if (*str != '\0' || (flags & GUI_PRINT_FLAG_CLRTOEOL)) {
/* send the text to gui handler */
signal_emit_id(signal_gui_print_text, 6, dest->window,
GINT_TO_POINTER(fgcolor),
GINT_TO_POINTER(bgcolor),
GINT_TO_POINTER(flags), str,
dest);
flags &= ~(GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_CLRTOEOL);
}
if (type == '\n') {
format_newline(dest->window);
fgcolor = theme->default_color;
bgcolor = -1;
flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE;
}
if (*ptr == '\0')
break;
switch (type)
{
case 2:
/* bold */
if (!hide_text_style)
flags ^= GUI_PRINT_FLAG_BOLD;
break;
case 3:
/* MIRC color */
get_mirc_color((const char **) &ptr,
hide_colors ? NULL : &fgcolor,
hide_colors ? NULL : &bgcolor);
if (!hide_colors)
flags |= GUI_PRINT_FLAG_MIRC_COLOR;
break;
case 4:
/* user specific colors */
flags &= ~GUI_PRINT_FLAG_MIRC_COLOR;
switch (*ptr) {
case FORMAT_STYLE_BLINK:
flags ^= GUI_PRINT_FLAG_BLINK;
break;
case FORMAT_STYLE_UNDERLINE:
flags ^= GUI_PRINT_FLAG_UNDERLINE;
break;
case FORMAT_STYLE_BOLD:
flags ^= GUI_PRINT_FLAG_BOLD;
break;
case FORMAT_STYLE_REVERSE:
flags ^= GUI_PRINT_FLAG_REVERSE;
break;
case FORMAT_STYLE_ITALIC:
flags ^= GUI_PRINT_FLAG_ITALIC;
break;
case FORMAT_STYLE_MONOSPACE:
flags ^= GUI_PRINT_FLAG_MONOSPACE;
break;
case FORMAT_STYLE_INDENT:
flags |= GUI_PRINT_FLAG_INDENT;
break;
case FORMAT_STYLE_DEFAULTS:
fgcolor = theme->default_color;
bgcolor = -1;
flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE;
break;
case FORMAT_STYLE_CLRTOEOL:
break;
case FORMAT_COLOR_EXT1:
fgcolor = 0x10 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
break;
case FORMAT_COLOR_EXT1_BG:
bgcolor = 0x10 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
break;
case FORMAT_COLOR_EXT2:
fgcolor = 0x60 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
break;
case FORMAT_COLOR_EXT2_BG:
bgcolor = 0x60 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
break;
case FORMAT_COLOR_EXT3:
fgcolor = 0xb0 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
break;
case FORMAT_COLOR_EXT3_BG:
bgcolor = 0xb0 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
break;
#ifdef TERM_TRUECOLOR
case FORMAT_COLOR_24:
unformat_24bit_color(&ptr, 1, &fgcolor, &bgcolor, &flags);
break;
#endif
default:
if (*ptr != FORMAT_COLOR_NOCHANGE) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fgcolor = *ptr==(char)0xff ? -1 : (unsigned char) *ptr-'0';
}
if (ptr[1] == '\0')
break;
ptr++;
if (*ptr != FORMAT_COLOR_NOCHANGE) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
bgcolor = *ptr==(char)0xff ? -1 : *ptr-'0';
}
}
if (*ptr == '\0')
break;
ptr++;
break;
case 6:
/* blink */
if (!hide_text_style)
flags ^= GUI_PRINT_FLAG_BLINK;
break;
case 15:
/* remove all styling */
fgcolor = theme->default_color;
bgcolor = -1;
flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE;
break;
case 22:
/* reverse */
if (!hide_text_style)
flags ^= GUI_PRINT_FLAG_REVERSE;
break;
case 29:
/* italic */
if (!hide_text_style)
flags ^= GUI_PRINT_FLAG_ITALIC;
break;
case 31:
/* underline */
if (!hide_text_style)
flags ^= GUI_PRINT_FLAG_UNDERLINE;
break;
case 27:
/* ansi color code */
ptr = (char *)
get_ansi_color(theme, ptr,
hide_colors ? NULL : &fgcolor,
hide_colors ? NULL : &bgcolor,
hide_colors ? NULL : &flags);
break;
}
str = ptr;
}
g_free(dup);
}
static void read_settings(void)
{
timestamp_level = settings_get_bool("timestamps") ? MSGLEVEL_ALL : 0;
if (timestamp_level > 0)
timestamp_level = settings_get_level("timestamp_level");
timestamp_timeout = settings_get_time("timestamp_timeout")/1000;
hide_server_tags = settings_get_bool("hide_server_tags");
hide_text_style = settings_get_bool("hide_text_style");
hide_colors = hide_text_style || settings_get_bool("hide_colors");
}
void formats_init(void)
{
signal_gui_print_text = signal_get_uniq_id("gui print text");
read_settings();
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
}
void formats_deinit(void)
{
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
}