Merged Irssi 0.8.2 from irssi.org cvs.
[silc.git] / apps / irssi / src / fe-common / core / formats.c
1 /*
2  formats.c : irssi
3
4     Copyright (C) 1999-2000 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "module-formats.h"
23 #include "signals.h"
24 #include "special-vars.h"
25 #include "settings.h"
26
27 #include "levels.h"
28 #include "servers.h"
29
30 #include "fe-windows.h"
31 #include "window-items.h"
32 #include "formats.h"
33 #include "themes.h"
34 #include "translation.h"
35
36 static const char *format_backs = "04261537";
37 static const char *format_fores = "kbgcrmyw";
38 static const char *format_boldfores = "KBGCRMYW";
39
40 static int signal_gui_print_text;
41 static int hide_text_style, hide_server_tags, hide_mirc_colors;
42
43 static int timestamp_level;
44 static int timestamp_timeout;
45
46 int format_find_tag(const char *module, const char *tag)
47 {
48         FORMAT_REC *formats;
49         int n;
50
51         formats = g_hash_table_lookup(default_formats, module);
52         if (formats == NULL)
53                 return -1;
54
55         for (n = 0; formats[n].def != NULL; n++) {
56                 if (formats[n].tag != NULL &&
57                     g_strcasecmp(formats[n].tag, tag) == 0)
58                         return n;
59         }
60
61         return -1;
62 }
63
64 static void format_expand_code(const char **format, GString *out, int *flags)
65 {
66         int set;
67
68         if (flags == NULL) {
69                 /* flags are being ignored - skip the code */
70                 while (**format != ']')
71                         (*format)++;
72                 return;
73         }
74
75         set = TRUE;
76         (*format)++;
77         while (**format != ']' && **format != '\0') {
78                 if (**format == '+')
79                         set = TRUE;
80                 else if (**format == '-')
81                         set = FALSE;
82                 else switch (**format) {
83                 case 'i':
84                         /* indent function */
85                         (*format)++;
86                         if (**format == '=')
87                                 (*format)++;
88
89                         g_string_append_c(out, 4);
90                         g_string_append_c(out, FORMAT_STYLE_INDENT_FUNC);
91                         while (**format != ']' && **format != '\0' &&
92                                **format != ',') {
93                                 g_string_append_c(out, **format);
94                                 (*format)++;
95                         }
96                         g_string_append_c(out, ',');
97                         (*format)--;
98                         break;
99                 case 's':
100                 case 'S':
101                         *flags |= !set ? PRINT_FLAG_UNSET_LINE_START :
102                                 **format == 's' ? PRINT_FLAG_SET_LINE_START :
103                                 PRINT_FLAG_SET_LINE_START_IRSSI;
104                         break;
105                 case 't':
106                         *flags |= set ? PRINT_FLAG_SET_TIMESTAMP :
107                                 PRINT_FLAG_UNSET_TIMESTAMP;
108                         break;
109                 case 'T':
110                         *flags |= set ? PRINT_FLAG_SET_SERVERTAG :
111                                 PRINT_FLAG_UNSET_SERVERTAG;
112                         break;
113                 }
114
115                 (*format)++;
116         }
117 }
118
119 int format_expand_styles(GString *out, const char **format, int *flags)
120 {
121         char *p, fmt;
122
123         fmt = **format;
124         switch (fmt) {
125         case '{':
126         case '}':
127         case '%':
128                 /* escaped char */
129                 g_string_append_c(out, fmt);
130                 break;
131         case 'U':
132                 /* Underline on/off */
133                 g_string_append_c(out, 4);
134                 g_string_append_c(out, FORMAT_STYLE_UNDERLINE);
135                 break;
136         case '9':
137         case '_':
138                 /* bold on/off */
139                 g_string_append_c(out, 4);
140                 g_string_append_c(out, FORMAT_STYLE_BOLD);
141                 break;
142         case '8':
143                 /* reverse */
144                 g_string_append_c(out, 4);
145                 g_string_append_c(out, FORMAT_STYLE_REVERSE);
146                 break;
147         case ':':
148                 /* Newline */
149                 g_string_append_c(out, '\n');
150                 break;
151         case '|':
152                 /* Indent here */
153                 g_string_append_c(out, 4);
154                 g_string_append_c(out, FORMAT_STYLE_INDENT);
155                 break;
156         case 'F':
157                 /* blink */
158                 g_string_append_c(out, 4);
159                 g_string_append_c(out, FORMAT_STYLE_BLINK);
160                 break;
161         case 'n':
162         case 'N':
163                 /* default color */
164                 g_string_append_c(out, 4);
165                 g_string_append_c(out, FORMAT_STYLE_DEFAULTS);
166                 break;
167         case '>':
168                 /* clear to end of line */
169                 g_string_append_c(out, 4);
170                 g_string_append_c(out, FORMAT_STYLE_CLRTOEOL);
171                 break;
172         case '[':
173                 /* code */
174                 format_expand_code(format, out, flags);
175                 break;
176         default:
177                 /* check if it's a background color */
178                 p = strchr(format_backs, fmt);
179                 if (p != NULL) {
180                         g_string_append_c(out, 4);
181                         g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
182                         g_string_append_c(out, (char) ((int) (p-format_backs)+'0'));
183                         break;
184                 }
185
186                 /* check if it's a foreground color */
187                 if (fmt == 'p') fmt = 'm';
188                 p = strchr(format_fores, fmt);
189                 if (p != NULL) {
190                         g_string_append_c(out, 4);
191                         g_string_append_c(out, (char) ((int) (p-format_fores)+'0'));
192                         g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
193                         break;
194                 }
195
196                 /* check if it's a bold foreground color */
197                 if (fmt == 'P') fmt = 'M';
198                 p = strchr(format_boldfores, fmt);
199                 if (p != NULL) {
200                         g_string_append_c(out, 4);
201                         g_string_append_c(out, (char) (8+(int) (p-format_boldfores)+'0'));
202                         g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
203                         break;
204                 }
205
206                 return FALSE;
207         }
208
209         return TRUE;
210 }
211
212 void format_read_arglist(va_list va, FORMAT_REC *format,
213                          char **arglist, int arglist_size,
214                          char *buffer, int buffer_size)
215 {
216         int num, len, bufpos;
217
218         g_return_if_fail(format->params < arglist_size);
219
220         bufpos = 0;
221         arglist[format->params] = NULL;
222         for (num = 0; num < format->params; num++) {
223                 switch (format->paramtypes[num]) {
224                 case FORMAT_STRING:
225                         arglist[num] = (char *) va_arg(va, char *);
226                         if (arglist[num] == NULL) {
227                                 g_warning("format_read_arglist() : parameter %d is NULL", num);
228                                 arglist[num] = "";
229                         }
230                         break;
231                 case FORMAT_INT: {
232                         int d = (int) va_arg(va, int);
233
234                         if (bufpos >= buffer_size) {
235                                 arglist[num] = "";
236                                 break;
237                         }
238
239                         arglist[num] = buffer+bufpos;
240                         len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
241                                          "%d", d);
242                         bufpos += len+1;
243                         break;
244                 }
245                 case FORMAT_LONG: {
246                         long l = (long) va_arg(va, long);
247
248                         if (bufpos >= buffer_size) {
249                                 arglist[num] = "";
250                                 break;
251                         }
252
253                         arglist[num] = buffer+bufpos;
254                         len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
255                                          "%ld", l);
256                         bufpos += len+1;
257                         break;
258                 }
259                 case FORMAT_FLOAT: {
260                         double f = (double) va_arg(va, double);
261
262                         if (bufpos >= buffer_size) {
263                                 arglist[num] = "";
264                                 break;
265                         }
266
267                         arglist[num] = buffer+bufpos;
268                         len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
269                                          "%0.2f", f);
270                         bufpos += len+1;
271                         break;
272                 }
273                 }
274         }
275 }
276
277 void format_create_dest(TEXT_DEST_REC *dest,
278                         void *server, const char *target,
279                         int level, WINDOW_REC *window)
280 {
281         format_create_dest_tag(dest, server, NULL, target, level, window);
282 }
283
284 void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
285                             const char *server_tag, const char *target,
286                             int level, WINDOW_REC *window)
287 {
288         memset(dest, 0, sizeof(TEXT_DEST_REC));
289
290         dest->server = server;
291         dest->server_tag = server != NULL ? SERVER(server)->tag : server_tag;
292         dest->target = target;
293         dest->level = level;
294         dest->window = window != NULL ? window :
295                 window_find_closest(server, target, level);
296 }
297
298 /* Return length of text part in string (ie. without % codes) */
299 int format_get_length(const char *str)
300 {
301         GString *tmp;
302         int len;
303
304         g_return_val_if_fail(str != NULL, 0);
305
306         tmp = g_string_new(NULL);
307         len = 0;
308         while (*str != '\0') {
309                 if (*str == '%' && str[1] != '\0') {
310                         str++;
311                         if (*str != '%' &&
312                             format_expand_styles(tmp, &str, NULL)) {
313                                 str++;
314                                 continue;
315                         }
316
317                         /* %% or unknown %code, written as-is */
318                         if (*str != '%')
319                                 len++;
320                 }
321
322                 len++;
323                 str++;
324         }
325
326         g_string_free(tmp, TRUE);
327         return len;
328 }
329
330 /* Return how many characters in `str' must be skipped before `len'
331    characters of text is skipped. Like strip_real_length(), except this
332    handles %codes. */
333 int format_real_length(const char *str, int len)
334 {
335         GString *tmp;
336         const char *start;
337
338         g_return_val_if_fail(str != NULL, 0);
339         g_return_val_if_fail(len >= 0, 0);
340
341         start = str;
342         tmp = g_string_new(NULL);
343         while (*str != '\0' && len > 0) {
344                 if (*str == '%' && str[1] != '\0') {
345                         str++;
346                         if (*str != '%' &&
347                             format_expand_styles(tmp, &str, NULL)) {
348                                 str++;
349                                 continue;
350                         }
351
352                         /* %% or unknown %code, written as-is */
353                         if (*str != '%') {
354                                 if (--len == 0)
355                                         break;
356                         }
357                 }
358
359                 len--;
360                 str++;
361         }
362
363         g_string_free(tmp, TRUE);
364         return (int) (str-start);
365 }
366
367 char *format_string_expand(const char *text, int *flags)
368 {
369         GString *out;
370         char code, *ret;
371
372         g_return_val_if_fail(text != NULL, NULL);
373
374         out = g_string_new(NULL);
375
376         if (flags != NULL) *flags = 0;
377         code = 0;
378         while (*text != '\0') {
379                 if (code == '%') {
380                         /* color code */
381                         if (!format_expand_styles(out, &text, flags)) {
382                                 g_string_append_c(out, '%');
383                                 g_string_append_c(out, '%');
384                                 g_string_append_c(out, *text);
385                         }
386                         code = 0;
387                 } else {
388                         if (*text == '%')
389                                 code = *text;
390                         else
391                                 g_string_append_c(out, *text);
392                 }
393
394                 text++;
395         }
396
397         ret = out->str;
398         g_string_free(out, FALSE);
399         return ret;
400 }
401
402 static char *format_get_text_args(TEXT_DEST_REC *dest,
403                                   const char *text, char **arglist)
404 {
405         GString *out;
406         char code, *ret;
407         int need_free;
408
409         out = g_string_new(NULL);
410
411         code = 0;
412         while (*text != '\0') {
413                 if (code == '%') {
414                         /* color code */
415                         if (!format_expand_styles(out, &text, &dest->flags)) {
416                                 g_string_append_c(out, '%');
417                                 g_string_append_c(out, '%');
418                                 g_string_append_c(out, *text);
419                         }
420                         code = 0;
421                 } else if (code == '$') {
422                         /* argument */
423                         char *ret;
424
425                         ret = parse_special((char **) &text, dest->server,
426                                             dest->target == NULL ? NULL :
427                                             window_item_find(dest->server, dest->target),
428                                             arglist, &need_free, NULL, 0);
429
430                         if (ret != NULL) {
431                                 /* string shouldn't end with \003 or it could
432                                    mess up the next one or two characters */
433                                 int diff;
434                                 int len = strlen(ret);
435                                 while (len > 0 && ret[len-1] == 3) len--;
436                                 diff = strlen(ret)-len;
437
438                                 g_string_append(out, ret);
439                                 if (diff > 0)
440                                         g_string_truncate(out, out->len-diff);
441                                 if (need_free) g_free(ret);
442                         }
443                         code = 0;
444                 } else {
445                         if (*text == '%' || *text == '$')
446                                 code = *text;
447                         else
448                                 g_string_append_c(out, *text);
449                 }
450
451                 text++;
452         }
453
454         ret = out->str;
455         g_string_free(out, FALSE);
456         return ret;
457 }
458
459 char *format_get_text_theme(THEME_REC *theme, const char *module,
460                             TEXT_DEST_REC *dest, int formatnum, ...)
461 {
462         va_list va;
463         char *str;
464
465         if (theme == NULL)
466                 theme = window_get_theme(dest->window);
467
468         va_start(va, formatnum);
469         str = format_get_text_theme_args(theme, module, dest, formatnum, va);
470         va_end(va);
471
472         return str;
473 }
474
475 char *format_get_text_theme_args(THEME_REC *theme, const char *module,
476                                  TEXT_DEST_REC *dest, int formatnum,
477                                  va_list va)
478 {
479         char *arglist[MAX_FORMAT_PARAMS];
480         char buffer[DEFAULT_FORMAT_ARGLIST_SIZE];
481         FORMAT_REC *formats;
482
483         formats = g_hash_table_lookup(default_formats, module);
484         format_read_arglist(va, &formats[formatnum],
485                             arglist, sizeof(arglist)/sizeof(char *),
486                             buffer, sizeof(buffer));
487
488         return format_get_text_theme_charargs(theme, module, dest,
489                                               formatnum, arglist);
490 }
491
492 char *format_get_text_theme_charargs(THEME_REC *theme, const char *module,
493                                      TEXT_DEST_REC *dest, int formatnum,
494                                      char **args)
495 {
496         MODULE_THEME_REC *module_theme;
497         char *text;
498
499         module_theme = g_hash_table_lookup(theme->modules, module);
500         if (module_theme == NULL)
501                 return NULL;
502
503         text = module_theme->expanded_formats[formatnum];
504         return format_get_text_args(dest, text, args);
505 }
506
507 char *format_get_text(const char *module, WINDOW_REC *window,
508                       void *server, const char *target,
509                       int formatnum, ...)
510 {
511         TEXT_DEST_REC dest;
512         THEME_REC *theme;
513         va_list va;
514         char *str;
515
516         format_create_dest(&dest, server, target, 0, window);
517         theme = window_get_theme(dest.window);
518
519         va_start(va, formatnum);
520         str = format_get_text_theme_args(theme, module, &dest, formatnum, va);
521         va_end(va);
522
523         return str;
524 }
525
526 /* add `linestart' to start of each line in `text'. `text' may contain
527    multiple lines separated with \n. */
528 char *format_add_linestart(const char *text, const char *linestart)
529 {
530         GString *str;
531         char *ret;
532
533         if (linestart == NULL)
534                 return g_strdup(text);
535
536         if (strchr(text, '\n') == NULL)
537                 return g_strconcat(linestart, text, NULL);
538
539         str = g_string_new(linestart);
540         while (*text != '\0') {
541                 g_string_append_c(str, *text);
542                 if (*text == '\n')
543                         g_string_append(str, linestart);
544                 text++;
545         }
546
547         ret = str->str;
548         g_string_free(str, FALSE);
549         return ret;
550 }
551
552 char *format_add_lineend(const char *text, const char *linestart)
553 {
554         GString *str;
555         char *ret;
556
557         if (linestart == NULL)
558                 return g_strdup(text);
559
560         if (strchr(text, '\n') == NULL)
561                 return g_strconcat(text, linestart, NULL);
562
563         str = g_string_new(NULL);
564         while (*text != '\0') {
565                 if (*text == '\n')
566                         g_string_append(str, linestart);
567                 g_string_append_c(str, *text);
568                 text++;
569         }
570         g_string_append(str, linestart);
571
572         ret = str->str;
573         g_string_free(str, FALSE);
574         return ret;
575 }
576
577 #define LINE_START_IRSSI_LEVEL \
578         (MSGLEVEL_CLIENTERROR | MSGLEVEL_CLIENTNOTICE)
579
580 #define NOT_LINE_START_LEVEL \
581         (MSGLEVEL_NEVER | MSGLEVEL_LASTLOG | MSGLEVEL_CLIENTCRAP | \
582         MSGLEVEL_MSGS | MSGLEVEL_PUBLIC | MSGLEVEL_DCC | MSGLEVEL_DCCMSGS | \
583         MSGLEVEL_ACTIONS | MSGLEVEL_NOTICES | MSGLEVEL_SNOTES | MSGLEVEL_CTCPS)
584
585 /* return the "-!- " text at the start of the line */
586 char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
587 {
588         int format;
589
590         /* check for flags if we want to override defaults */
591         if (dest->flags & PRINT_FLAG_UNSET_LINE_START)
592                 return NULL;
593
594         if (dest->flags & PRINT_FLAG_SET_LINE_START)
595                 format = TXT_LINE_START;
596         else if (dest->flags & PRINT_FLAG_SET_LINE_START_IRSSI)
597                 format = TXT_LINE_START_IRSSI;
598         else {
599                 /* use defaults */
600                 if (dest->level & LINE_START_IRSSI_LEVEL)
601                         format = TXT_LINE_START_IRSSI;
602                 else if ((dest->level & NOT_LINE_START_LEVEL) == 0)
603                         format = TXT_LINE_START;
604                 else
605                         return NULL;
606         }
607
608         return format_get_text_theme(theme, MODULE_NAME, dest, format);
609 }
610
611 static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
612 {
613         char *format, str[256];
614         struct tm *tm;
615         int diff;
616
617         if ((timestamp_level & dest->level) == 0)
618                 return NULL;
619
620         /* check for flags if we want to override defaults */
621         if (dest->flags & PRINT_FLAG_UNSET_TIMESTAMP)
622                 return NULL;
623
624         if ((dest->flags & PRINT_FLAG_SET_TIMESTAMP) == 0 &&
625             (dest->level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) != 0)
626                 return NULL;
627
628
629         if (timestamp_timeout > 0) {
630                 diff = t - dest->window->last_timestamp;
631                 dest->window->last_timestamp = t;
632                 if (diff < timestamp_timeout)
633                         return NULL;
634         }
635
636         tm = localtime(&t);
637         format = format_get_text_theme(theme, MODULE_NAME, dest,
638                                        TXT_TIMESTAMP);
639         if (strftime(str, sizeof(str), format, tm) <= 0)
640                 str[0] = '\0';
641         g_free(format);
642         return g_strdup(str);
643 }
644
645 static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
646 {
647         int count = 0;
648
649         if (dest->server_tag == NULL || hide_server_tags)
650                 return NULL;
651
652         /* check for flags if we want to override defaults */
653         if (dest->flags & PRINT_FLAG_UNSET_SERVERTAG)
654                 return NULL;
655
656         if ((dest->flags & PRINT_FLAG_SET_SERVERTAG) == 0) {
657                 if (dest->window->active != NULL &&
658                     dest->window->active->server == dest->server)
659                         return NULL;
660
661                 if (servers != NULL) {
662                         count++;
663                         if (servers->next != NULL)
664                                 count++;
665                 }
666                 if (count < 2 && lookup_servers != NULL) {
667                         count++;
668                         if (lookup_servers->next != NULL)
669                                 count++;
670                 }
671
672                 if (count < 2)
673                         return NULL;
674         }
675
676         return format_get_text_theme(theme, MODULE_NAME, dest,
677                                      TXT_SERVERTAG, dest->server_tag);
678 }
679
680 char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
681 {
682         char *timestamp, *servertag;
683         char *linestart;
684
685         timestamp = get_timestamp(theme, dest, t);
686         servertag = get_server_tag(theme, dest);
687
688         if (timestamp == NULL && servertag == NULL)
689                 return NULL;
690
691         linestart = g_strconcat(timestamp != NULL ? timestamp : "",
692                                 servertag, NULL);
693
694         g_free_not_null(timestamp);
695         g_free_not_null(servertag);
696         return linestart;
697 }
698
699 void format_newline(WINDOW_REC *window)
700 {
701         g_return_if_fail(window != NULL);
702
703         signal_emit_id(signal_gui_print_text, 6, window,
704                        GINT_TO_POINTER(-1), GINT_TO_POINTER(-1),
705                        GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE),
706                        "", GINT_TO_POINTER(-1));
707 }
708
709 /* parse ANSI color string */
710 static const char *get_ansi_color(THEME_REC *theme, const char *str,
711                                   int *fg_ret, int *bg_ret, int *flags_ret)
712 {
713         static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
714         const char *start;
715         int fg, bg, flags, num;
716
717         if (*str != '[')
718                 return str;
719         start = str++;
720
721         fg = fg_ret == NULL || *fg_ret < 0 ? theme->default_color : *fg_ret;
722         bg = bg_ret == NULL || *bg_ret < 0 ? -1 : *bg_ret;
723         flags = flags_ret == NULL ? 0 : *flags_ret;
724
725         num = 0;
726         for (;; str++) {
727                 if (*str == '\0') return start;
728
729                 if (i_isdigit(*str)) {
730                         num = num*10 + (*str-'0');
731                         continue;
732                 }
733
734                 if (*str != ';' && *str != 'm')
735                         return start;
736
737                 switch (num) {
738                 case 0:
739                         /* reset colors back to default */
740                         fg = theme->default_color;
741                         bg = -1;
742                         flags &= ~GUI_PRINT_FLAG_INDENT;
743                         break;
744                 case 1:
745                         /* hilight */
746                         flags |= GUI_PRINT_FLAG_BOLD;
747                         break;
748                 case 5:
749                         /* blink */
750                         flags |= GUI_PRINT_FLAG_BLINK;
751                         break;
752                 case 7:
753                         /* reverse */
754                         flags |= GUI_PRINT_FLAG_REVERSE;
755                         break;
756                 default:
757                         if (num >= 30 && num <= 37) {
758                                 if (fg == -1) fg = 0;
759                                 fg = (fg & 0xf8) | ansitab[num-30];
760                         }
761                         if (num >= 40 && num <= 47) {
762                                 if (bg == -1) bg = 0;
763                                 bg = (bg & 0xf8) | ansitab[num-40];
764                         }
765                         break;
766                 }
767                 num = 0;
768
769                 if (*str == 'm') {
770                         if (fg_ret != NULL) *fg_ret = fg;
771                         if (bg_ret != NULL) *bg_ret = bg;
772                         if (flags_ret != NULL) *flags_ret = flags;
773
774                         str++;
775                         break;
776                 }
777         }
778
779         return str;
780 }
781
782 /* parse MIRC color string */
783 static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret)
784 {
785         int fg, bg;
786
787         fg = fg_ret == NULL ? -1 : *fg_ret;
788         bg = bg_ret == NULL ? -1 : *bg_ret;
789
790         if (!i_isdigit(**str) && **str != ',') {
791                 fg = -1;
792                 bg = -1;
793         } else {
794                 /* foreground color */
795                 if (**str != ',') {
796                         fg = **str-'0';
797                         (*str)++;
798                         if (i_isdigit(**str)) {
799                                 fg = fg*10 + (**str-'0');
800                                 (*str)++;
801                         }
802                 }
803                 if (**str == ',') {
804                         /* background color */
805                         (*str)++;
806                         if (!i_isdigit(**str))
807                                 bg = -1;
808                         else {
809                                 bg = **str-'0';
810                                 (*str)++;
811                                 if (i_isdigit(**str)) {
812                                         bg = bg*10 + (**str-'0');
813                                         (*str)++;
814                                 }
815                         }
816                 }
817         }
818
819         if (fg_ret) *fg_ret = fg;
820         if (bg_ret) *bg_ret = bg;
821 }
822
823 #define IS_COLOR_CODE(c) \
824         ((c) == 2 || (c) == 3 || (c) == 4 || (c) == 6 || (c) == 7 || \
825         (c) == 15 || (c) == 22 || (c) == 27 || (c) == 31)
826
827 /* Return how many characters in `str' must be skipped before `len'
828    characters of text is skipped. */
829 int strip_real_length(const char *str, int len,
830                       int *last_color_pos, int *last_color_len)
831 {
832         const char *start = str;
833
834         if (last_color_pos != NULL)
835                 *last_color_pos = -1;
836         if (last_color_len != NULL)
837                 *last_color_len = -1;
838
839         while (*str != '\0') {
840                 if (*str == 3) {
841                         const char *mircstart = str;
842
843                         if (last_color_pos != NULL)
844                                 *last_color_pos = (int) (str-start);
845                         str++;
846                         get_mirc_color(&str, NULL, NULL);
847                         if (last_color_len != NULL)
848                                 *last_color_len = (int) (str-mircstart);
849
850                 } else if (*str == 4 && str[1] != '\0') {
851                         if (str[1] < FORMAT_STYLE_SPECIAL && str[2] != '\0') {
852                                 if (last_color_pos != NULL)
853                                         *last_color_pos = (int) (str-start);
854                                 if (last_color_len != NULL)
855                                         *last_color_len = 3;
856                                 str++;
857                         } else if (str[1] == FORMAT_STYLE_DEFAULTS) {
858                                 if (last_color_pos != NULL)
859                                         *last_color_pos = (int) (str-start);
860                                 if (last_color_len != NULL)
861                                         *last_color_len = 2;
862                         }
863                         str += 2;
864                 } else {
865                         if (!IS_COLOR_CODE(*str)) {
866                                 if (len-- == 0)
867                                         break;
868                         }
869                         str++;
870                 }
871         }
872
873         return (int) (str-start);
874 }
875
876 char *strip_codes(const char *input)
877 {
878         const char *p;
879         char *str, *out;
880
881         out = str = g_strdup(input);
882         for (p = input; *p != '\0'; p++) {
883                 if (*p == 3) {
884                         p++;  
885
886                         /* mirc color */
887                         get_mirc_color(&p, NULL, NULL);
888                         p--;
889                         continue;
890                 }
891
892                 if (*p == 4 && p[1] != '\0') {
893                         if (p[1] >= FORMAT_STYLE_SPECIAL) {
894                                 p++;
895                                 continue;
896                         }
897
898                         /* irssi color */
899                         if (p[2] != '\0') {
900                                 p += 2;
901                                 continue;
902                         }
903                 }
904
905                 if (*p == 27 && p[1] != '\0') {
906                         p++;
907                         p = get_ansi_color(current_theme, p, NULL, NULL, NULL);
908                 }
909
910                 if (!IS_COLOR_CODE(*p))
911                         *out++ = *p;   
912         }
913
914         *out = '\0';
915         return str; 
916 }
917
918 /* send a fully parsed text string for GUI to print */
919 void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
920 {
921         THEME_REC *theme;
922         char *dup, *str, *ptr, type;
923         int fgcolor, bgcolor;
924         int flags;
925
926         theme = dest->window != NULL && dest->window->theme != NULL ?
927                 dest->window->theme : current_theme;
928
929         dup = str = g_strdup(text);
930
931         flags = 0; fgcolor = theme->default_color; bgcolor = -1;
932         while (*str != '\0') {
933                 type = '\0';
934                 for (ptr = str; *ptr != '\0'; ptr++) {
935                         if (IS_COLOR_CODE(*ptr) || *ptr == '\n') {
936                                 type = *ptr;
937                                 *ptr++ = '\0';
938                                 break;
939                         }
940
941                         *ptr = (char) translation_in[(int) (unsigned char) *ptr];
942                 }
943
944                 if (type == 7) {
945                         /* bell */
946                         if (settings_get_bool("bell_beeps"))
947                                 signal_emit("beep", 0);
948                 } else if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) {
949                         /* clear to end of line */
950                         flags |= GUI_PRINT_FLAG_CLRTOEOL;
951                 }
952
953                 if (*str != '\0' || (flags & GUI_PRINT_FLAG_CLRTOEOL)) {
954                         /* send the text to gui handler */
955                         signal_emit_id(signal_gui_print_text, 6, dest->window,
956                                        GINT_TO_POINTER(fgcolor),
957                                        GINT_TO_POINTER(bgcolor),
958                                        GINT_TO_POINTER(flags), str,
959                                        dest->level);
960                         flags &= ~(GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_CLRTOEOL);
961                 }
962
963                 if (type == '\n')
964                         format_newline(dest->window);
965
966                 if (*ptr == '\0')
967                         break;
968
969                 switch (type)
970                 {
971                 case 2:
972                         /* bold */
973                         if (!hide_text_style)
974                                 flags ^= GUI_PRINT_FLAG_BOLD;
975                         break;
976                 case 3:
977                         /* MIRC color */
978                         get_mirc_color((const char **) &ptr,
979                                         hide_mirc_colors || hide_text_style ? NULL : &fgcolor,
980                                         hide_mirc_colors || hide_text_style ? NULL : &bgcolor);
981                         if (!hide_mirc_colors && !hide_text_style)
982                                 flags |= GUI_PRINT_FLAG_MIRC_COLOR;
983                         break;
984                 case 4:
985                         /* user specific colors */
986                         flags &= ~GUI_PRINT_FLAG_MIRC_COLOR;
987                         switch (*ptr) {
988                         case FORMAT_STYLE_BLINK:
989                                 flags ^= GUI_PRINT_FLAG_BLINK;
990                                 break;
991                         case FORMAT_STYLE_UNDERLINE:
992                                 flags ^= GUI_PRINT_FLAG_UNDERLINE;
993                                 break;
994                         case FORMAT_STYLE_BOLD:
995                                 flags ^= GUI_PRINT_FLAG_BOLD;
996                                 break;
997                         case FORMAT_STYLE_REVERSE:
998                                 flags ^= GUI_PRINT_FLAG_REVERSE;
999                                 break;
1000                         case FORMAT_STYLE_INDENT:
1001                                 flags |= GUI_PRINT_FLAG_INDENT;
1002                                 break;
1003                         case FORMAT_STYLE_INDENT_FUNC: {
1004                                 const char *start = ptr;
1005                                 while (*ptr != ',' && *ptr != '\0')
1006                                         ptr++;
1007                                 if (*ptr != '\0') *ptr++ = '\0';
1008                                 ptr--;
1009                                 signal_emit_id(signal_gui_print_text, 6,
1010                                                dest->window, NULL, NULL,
1011                                                GINT_TO_POINTER(GUI_PRINT_FLAG_INDENT_FUNC),
1012                                                str, start, dest->level);
1013                                 break;
1014                         }
1015                         case FORMAT_STYLE_DEFAULTS:
1016                                 fgcolor = theme->default_color;
1017                                 bgcolor = -1;
1018                                 flags &= GUI_PRINT_FLAG_INDENT;
1019                                 break;
1020                         case FORMAT_STYLE_CLRTOEOL:
1021                                 break;
1022                         default:
1023                                 if (*ptr != FORMAT_COLOR_NOCHANGE) {
1024                                         fgcolor = (unsigned char) *ptr-'0';
1025                                         if (fgcolor <= 7)
1026                                                 flags &= ~GUI_PRINT_FLAG_BOLD;
1027                                         else {
1028                                                 /* bold */
1029                                                 if (fgcolor != 8) fgcolor -= 8;
1030                                                 flags |= GUI_PRINT_FLAG_BOLD;
1031                                         }
1032                                 }
1033                                 if (ptr[1] == '\0')
1034                                         break;
1035
1036                                 ptr++;
1037                                 if (*ptr != FORMAT_COLOR_NOCHANGE) {
1038                                         bgcolor = *ptr-'0';
1039                                         if (bgcolor <= 7)
1040                                                 flags &= ~GUI_PRINT_FLAG_BLINK;
1041                                         else {
1042                                                 /* blink */
1043                                                 bgcolor -= 8;
1044                                                 flags |= GUI_PRINT_FLAG_BLINK;
1045                                         }
1046                                 }
1047                         }
1048                         ptr++;
1049                         break;
1050                 case 6:
1051                         /* blink */
1052                         if (!hide_text_style)
1053                                 flags ^= GUI_PRINT_FLAG_BLINK;
1054                         break;
1055                 case 15:
1056                         /* remove all styling */
1057                         fgcolor = theme->default_color;
1058                         bgcolor = -1;
1059                         flags &= GUI_PRINT_FLAG_INDENT;
1060                         break;
1061                 case 22:
1062                         /* reverse */
1063                         if (!hide_text_style)
1064                                 flags ^= GUI_PRINT_FLAG_REVERSE;
1065                         break;
1066                 case 31:
1067                         /* underline */
1068                         if (!hide_text_style)
1069                                 flags ^= GUI_PRINT_FLAG_UNDERLINE;
1070                         break;
1071                 case 27:
1072                         /* ansi color code */
1073                         ptr = (char *)
1074                                 get_ansi_color(theme, ptr,
1075                                                hide_text_style ? NULL : &fgcolor,
1076                                                hide_text_style ? NULL : &bgcolor,
1077                                                hide_text_style ? NULL : &flags);
1078                         break;
1079                 }
1080
1081                 str = ptr;
1082         }
1083
1084         g_free(dup);
1085 }
1086
1087 static void read_settings(void)
1088 {
1089         timestamp_level = settings_get_bool("timestamps") ? MSGLEVEL_ALL : 0;
1090         if (timestamp_level > 0) {
1091                 timestamp_level =
1092                         level2bits(settings_get_str("timestamp_level"));
1093         }
1094         timestamp_timeout = settings_get_int("timestamp_timeout");
1095
1096         hide_server_tags = settings_get_bool("hide_server_tags");
1097         hide_text_style = settings_get_bool("hide_text_style");
1098         hide_mirc_colors = settings_get_bool("hide_mirc_colors");
1099 }
1100
1101 void formats_init(void)
1102 {
1103         signal_gui_print_text = signal_get_uniq_id("gui print text");
1104
1105         read_settings();
1106         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
1107 }
1108
1109 void formats_deinit(void)
1110 {
1111         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
1112 }