/* 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 "module-formats.h" #include "signals.h" #include "special-vars.h" #include "settings.h" #include "levels.h" #include "servers.h" #include "fe-windows.h" #include "window-items.h" #include "formats.h" #include "themes.h" #include "recode.h" #include "utf8.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)++; 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); 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' && len > 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 != '%') { if (--len == 0) break; } } oldstr = str; len -= string_advance(&str, utf8); if (len < 0) str = oldstr; } 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; int fg, bg, flags, num, i; unsigned int 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)) { num = num*10 + (*str-'0'); continue; } 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++; for (num2 = 0; i_isdigit(*str); str++) num2 = num2*10 + (*str-'0'); 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 (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++; for (num2 = 0; i_isdigit(*str); str++) num2 = num2*10 + (*str-'0'); 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) && **str != ',') { fg = -1; bg = -1; } else { /* foreground color */ if (**str != ',') { fg = **str-'0'; (*str)++; if (i_isdigit(**str)) { fg = fg*10 + (**str-'0'); (*str)++; } } if (**str == ',') { /* background color */ if (!i_isdigit((*str)[1])) bg = -1; else { (*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) { 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') { #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; 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); }