New silcconfig library and server parser. Merged silc-newconfig-final.patch.
[crypto.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 #define LINE_START_IRSSI_LEVEL \
553         (MSGLEVEL_CLIENTERROR | MSGLEVEL_CLIENTNOTICE)
554
555 #define NOT_LINE_START_LEVEL \
556         (MSGLEVEL_NEVER | MSGLEVEL_LASTLOG | MSGLEVEL_CLIENTCRAP | \
557         MSGLEVEL_MSGS | MSGLEVEL_PUBLIC | MSGLEVEL_DCC | MSGLEVEL_DCCMSGS | \
558         MSGLEVEL_ACTIONS | MSGLEVEL_NOTICES | MSGLEVEL_SNOTES | MSGLEVEL_CTCPS)
559
560 /* return the "-!- " text at the start of the line */
561 char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
562 {
563         int format;
564
565         /* check for flags if we want to override defaults */
566         if (dest->flags & PRINT_FLAG_UNSET_LINE_START)
567                 return NULL;
568
569         if (dest->flags & PRINT_FLAG_SET_LINE_START)
570                 format = TXT_LINE_START;
571         else if (dest->flags & PRINT_FLAG_SET_LINE_START_IRSSI)
572                 format = TXT_LINE_START_IRSSI;
573         else {
574                 /* use defaults */
575                 if (dest->level & LINE_START_IRSSI_LEVEL)
576                         format = TXT_LINE_START_IRSSI;
577                 else if ((dest->level & NOT_LINE_START_LEVEL) == 0)
578                         format = TXT_LINE_START;
579                 else
580                         return NULL;
581         }
582
583         return format_get_text_theme(theme, MODULE_NAME, dest, format);
584 }
585
586 static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
587 {
588         char *format, str[256];
589         struct tm *tm;
590         int diff;
591
592         if ((timestamp_level & dest->level) == 0)
593                 return NULL;
594
595         /* check for flags if we want to override defaults */
596         if (dest->flags & PRINT_FLAG_UNSET_TIMESTAMP)
597                 return NULL;
598
599         if ((dest->flags & PRINT_FLAG_SET_TIMESTAMP) == 0 &&
600             (dest->level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) != 0)
601                 return NULL;
602
603
604         if (timestamp_timeout > 0) {
605                 diff = t - dest->window->last_timestamp;
606                 dest->window->last_timestamp = t;
607                 if (diff < timestamp_timeout)
608                         return NULL;
609         }
610
611         tm = localtime(&t);
612         format = format_get_text_theme(theme, MODULE_NAME, dest,
613                                        TXT_TIMESTAMP);
614         if (strftime(str, sizeof(str), format, tm) <= 0)
615                 str[0] = '\0';
616         g_free(format);
617         return g_strdup(str);
618 }
619
620 static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
621 {
622         int count = 0;
623
624         if (dest->server_tag == NULL || hide_server_tags)
625                 return NULL;
626
627         /* check for flags if we want to override defaults */
628         if (dest->flags & PRINT_FLAG_UNSET_SERVERTAG)
629                 return NULL;
630
631         if ((dest->flags & PRINT_FLAG_SET_SERVERTAG) == 0) {
632                 if (dest->window->active != NULL &&
633                     dest->window->active->server == dest->server)
634                         return NULL;
635
636                 if (servers != NULL) {
637                         count++;
638                         if (servers->next != NULL)
639                                 count++;
640                 }
641                 if (count < 2 && lookup_servers != NULL) {
642                         count++;
643                         if (lookup_servers->next != NULL)
644                                 count++;
645                 }
646
647                 if (count < 2)
648                         return NULL;
649         }
650
651         return format_get_text_theme(theme, MODULE_NAME, dest,
652                                      TXT_SERVERTAG, dest->server_tag);
653 }
654
655 char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
656 {
657         char *timestamp, *servertag;
658         char *linestart;
659
660         timestamp = get_timestamp(theme, dest, t);
661         servertag = get_server_tag(theme, dest);
662
663         if (timestamp == NULL && servertag == NULL)
664                 return NULL;
665
666         linestart = g_strconcat(timestamp != NULL ? timestamp : "",
667                                 servertag, NULL);
668
669         g_free_not_null(timestamp);
670         g_free_not_null(servertag);
671         return linestart;
672 }
673
674 void format_newline(WINDOW_REC *window)
675 {
676         g_return_if_fail(window != NULL);
677
678         signal_emit_id(signal_gui_print_text, 6, window,
679                        GINT_TO_POINTER(-1), GINT_TO_POINTER(-1),
680                        GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE),
681                        "", GINT_TO_POINTER(-1));
682 }
683
684 /* parse ANSI color string */
685 static const char *get_ansi_color(THEME_REC *theme, const char *str,
686                                   int *fg_ret, int *bg_ret, int *flags_ret)
687 {
688         static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
689         const char *start;
690         int fg, bg, flags, num;
691
692         if (*str != '[')
693                 return str;
694         start = str++;
695
696         fg = fg_ret == NULL || *fg_ret < 0 ? theme->default_color : *fg_ret;
697         bg = bg_ret == NULL || *bg_ret < 0 ? -1 : *bg_ret;
698         flags = flags_ret == NULL ? 0 : *flags_ret;
699
700         num = 0;
701         for (;; str++) {
702                 if (*str == '\0') return start;
703
704                 if (i_isdigit(*str)) {
705                         num = num*10 + (*str-'0');
706                         continue;
707                 }
708
709                 if (*str != ';' && *str != 'm')
710                         return start;
711
712                 switch (num) {
713                 case 0:
714                         /* reset colors back to default */
715                         fg = theme->default_color;
716                         bg = -1;
717                         flags &= ~GUI_PRINT_FLAG_INDENT;
718                         break;
719                 case 1:
720                         /* hilight */
721                         flags |= GUI_PRINT_FLAG_BOLD;
722                         break;
723                 case 5:
724                         /* blink */
725                         flags |= GUI_PRINT_FLAG_BLINK;
726                         break;
727                 case 7:
728                         /* reverse */
729                         flags |= GUI_PRINT_FLAG_REVERSE;
730                         break;
731                 default:
732                         if (num >= 30 && num <= 37)
733                                 fg = (fg & 0xf8) | ansitab[num-30];
734                         if (num >= 40 && num <= 47) {
735                                 if (bg == -1) bg = 0;
736                                 bg = (bg & 0xf8) | ansitab[num-40];
737                         }
738                         break;
739                 }
740                 num = 0;
741
742                 if (*str == 'm') {
743                         if (fg_ret != NULL) *fg_ret = fg;
744                         if (bg_ret != NULL) *bg_ret = bg;
745                         if (flags_ret != NULL) *flags_ret = flags;
746
747                         str++;
748                         break;
749                 }
750         }
751
752         return str;
753 }
754
755 /* parse MIRC color string */
756 static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret)
757 {
758         int fg, bg;
759
760         fg = fg_ret == NULL ? -1 : *fg_ret;
761         bg = bg_ret == NULL ? -1 : *bg_ret;
762
763         if (!i_isdigit(**str) && **str != ',') {
764                 fg = -1;
765                 bg = -1;
766         } else {
767                 /* foreground color */
768                 if (**str != ',') {
769                         fg = **str-'0';
770                         (*str)++;
771                         if (i_isdigit(**str)) {
772                                 fg = fg*10 + (**str-'0');
773                                 (*str)++;
774                         }
775                 }
776                 if (**str == ',') {
777                         /* background color */
778                         (*str)++;
779                         if (!i_isdigit(**str))
780                                 bg = -1;
781                         else {
782                                 bg = **str-'0';
783                                 (*str)++;
784                                 if (i_isdigit(**str)) {
785                                         bg = bg*10 + (**str-'0');
786                                         (*str)++;
787                                 }
788                         }
789                 }
790         }
791
792         if (fg_ret) *fg_ret = fg;
793         if (bg_ret) *bg_ret = bg;
794 }
795
796 #define IS_COLOR_CODE(c) \
797         ((c) == 2 || (c) == 3 || (c) == 4 || (c) == 6 || (c) == 7 || \
798         (c) == 15 || (c) == 22 || (c) == 27 || (c) == 31)
799
800 /* Return how many characters in `str' must be skipped before `len'
801    characters of text is skipped. */
802 int strip_real_length(const char *str, int len,
803                       int *last_color_pos, int *last_color_len)
804 {
805         const char *start = str;
806
807         if (last_color_pos != NULL)
808                 *last_color_pos = -1;
809         if (last_color_len != NULL)
810                 *last_color_len = -1;
811
812         while (*str != '\0') {
813                 if (*str == 3) {
814                         const char *mircstart = str;
815
816                         if (last_color_pos != NULL)
817                                 *last_color_pos = (int) (str-start);
818                         str++;
819                         get_mirc_color(&str, NULL, NULL);
820                         if (last_color_len != NULL)
821                                 *last_color_len = (int) (str-mircstart);
822
823                 } else if (*str == 4 && str[1] != '\0') {
824                         if (str[1] < FORMAT_STYLE_SPECIAL && str[2] != '\0') {
825                                 if (last_color_pos != NULL)
826                                         *last_color_pos = (int) (str-start);
827                                 if (last_color_len != NULL)
828                                         *last_color_len = 3;
829                                 str++;
830                         } else if (str[1] == FORMAT_STYLE_DEFAULTS) {
831                                 if (last_color_pos != NULL)
832                                         *last_color_pos = (int) (str-start);
833                                 if (last_color_len != NULL)
834                                         *last_color_len = 2;
835                         }
836                         str += 2;
837                 } else {
838                         if (!IS_COLOR_CODE(*str)) {
839                                 if (len-- == 0)
840                                         break;
841                         }
842                         str++;
843                 }
844         }
845
846         return (int) (str-start);
847 }
848
849 char *strip_codes(const char *input)
850 {
851         const char *p;
852         char *str, *out;
853
854         out = str = g_strdup(input);
855         for (p = input; *p != '\0'; p++) {
856                 if (*p == 3) {
857                         p++;  
858
859                         /* mirc color */
860                         get_mirc_color(&p, NULL, NULL);
861                         p--;
862                         continue;
863                 }
864
865                 if (*p == 4 && p[1] != '\0') {
866                         if (p[1] >= FORMAT_STYLE_SPECIAL) {
867                                 p++;
868                                 continue;
869                         }
870
871                         /* irssi color */
872                         if (p[2] != '\0') {
873                                 p += 2;
874                                 continue;
875                         }
876                 }
877
878                 if (*p == 27 && p[1] != '\0')
879                         p = get_ansi_color(current_theme, p, NULL, NULL, NULL);
880
881                 if (!IS_COLOR_CODE(*p))
882                         *out++ = *p;   
883         }
884
885         *out = '\0';
886         return str; 
887 }
888
889 /* send a fully parsed text string for GUI to print */
890 void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
891 {
892         char *dup, *str, *ptr, type;
893         int fgcolor, bgcolor;
894         int flags;
895
896         dup = str = g_strdup(text);
897
898         flags = 0; fgcolor = -1; bgcolor = -1;
899         while (*str != '\0') {
900                 type = '\0';
901                 for (ptr = str; *ptr != '\0'; ptr++) {
902                         if (IS_COLOR_CODE(*ptr) || *ptr == '\n') {
903                                 type = *ptr;
904                                 *ptr++ = '\0';
905                                 break;
906                         }
907
908                         *ptr = (char) translation_in[(int) (unsigned char) *ptr];
909                 }
910
911                 if (type == 7) {
912                         /* bell */
913                         if (settings_get_bool("bell_beeps"))
914                                 signal_emit("beep", 0);
915                 } else if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) {
916                         /* clear to end of line */
917                         flags |= GUI_PRINT_FLAG_CLRTOEOL;
918                 }
919
920                 if (*str != '\0' || (flags & GUI_PRINT_FLAG_CLRTOEOL)) {
921                         /* send the text to gui handler */
922                         signal_emit_id(signal_gui_print_text, 6, dest->window,
923                                        GINT_TO_POINTER(fgcolor),
924                                        GINT_TO_POINTER(bgcolor),
925                                        GINT_TO_POINTER(flags), str,
926                                        dest->level);
927                         flags &= ~(GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_CLRTOEOL);
928                 }
929
930                 if (type == '\n')
931                         format_newline(dest->window);
932
933                 if (*ptr == '\0')
934                         break;
935
936                 switch (type)
937                 {
938                 case 2:
939                         /* bold */
940                         if (!hide_text_style)
941                                 flags ^= GUI_PRINT_FLAG_BOLD;
942                         break;
943                 case 3:
944                         /* MIRC color */
945                         get_mirc_color((const char **) &ptr,
946                                         hide_mirc_colors || hide_text_style ? NULL : &fgcolor,
947                                         hide_mirc_colors || hide_text_style ? NULL : &bgcolor);
948                         if (!hide_mirc_colors && !hide_text_style)
949                                 flags |= GUI_PRINT_FLAG_MIRC_COLOR;
950                         break;
951                 case 4:
952                         /* user specific colors */
953                         flags &= ~GUI_PRINT_FLAG_MIRC_COLOR;
954                         switch (*ptr) {
955                         case FORMAT_STYLE_BLINK:
956                                 flags ^= GUI_PRINT_FLAG_BLINK;
957                                 break;
958                         case FORMAT_STYLE_UNDERLINE:
959                                 flags ^= GUI_PRINT_FLAG_UNDERLINE;
960                                 break;
961                         case FORMAT_STYLE_BOLD:
962                                 flags ^= GUI_PRINT_FLAG_BOLD;
963                                 break;
964                         case FORMAT_STYLE_REVERSE:
965                                 flags ^= GUI_PRINT_FLAG_REVERSE;
966                                 break;
967                         case FORMAT_STYLE_INDENT:
968                                 flags |= GUI_PRINT_FLAG_INDENT;
969                                 break;
970                         case FORMAT_STYLE_INDENT_FUNC: {
971                                 const char *start = ptr;
972                                 while (*ptr != ',' && *ptr != '\0')
973                                         ptr++;
974                                 if (*ptr != '\0') *ptr++ = '\0';
975                                 signal_emit_id(signal_gui_print_text, 6,
976                                                dest->window, NULL, NULL,
977                                                GINT_TO_POINTER(GUI_PRINT_FLAG_INDENT_FUNC),
978                                                str, start, dest->level);
979                                 break;
980                         }
981                         case FORMAT_STYLE_DEFAULTS:
982                                 fgcolor = bgcolor = -1;
983                                 flags &= GUI_PRINT_FLAG_INDENT;
984                                 break;
985                         case FORMAT_STYLE_CLRTOEOL:
986                                 break;
987                         default:
988                                 if (*ptr != FORMAT_COLOR_NOCHANGE) {
989                                         fgcolor = (unsigned char) *ptr-'0';
990                                         if (fgcolor <= 7)
991                                                 flags &= ~GUI_PRINT_FLAG_BOLD;
992                                         else {
993                                                 /* bold */
994                                                 if (fgcolor != 8) fgcolor -= 8;
995                                                 flags |= GUI_PRINT_FLAG_BOLD;
996                                         }
997                                 }
998                                 ptr++;
999                                 if (*ptr != FORMAT_COLOR_NOCHANGE) {
1000                                         bgcolor = *ptr-'0';
1001                                         if (bgcolor <= 7)
1002                                                 flags &= ~GUI_PRINT_FLAG_BLINK;
1003                                         else {
1004                                                 /* blink */
1005                                                 bgcolor -= 8;
1006                                                 flags |= GUI_PRINT_FLAG_BLINK;
1007                                         }
1008                                 }
1009                         }
1010                         ptr++;
1011                         break;
1012                 case 6:
1013                         /* blink */
1014                         if (!hide_text_style)
1015                                 flags ^= GUI_PRINT_FLAG_BLINK;
1016                         break;
1017                 case 15:
1018                         /* remove all styling */
1019                         fgcolor = bgcolor = -1;
1020                         flags &= GUI_PRINT_FLAG_INDENT;
1021                         break;
1022                 case 22:
1023                         /* reverse */
1024                         if (!hide_text_style)
1025                                 flags ^= GUI_PRINT_FLAG_REVERSE;
1026                         break;
1027                 case 31:
1028                         /* underline */
1029                         if (!hide_text_style)
1030                                 flags ^= GUI_PRINT_FLAG_UNDERLINE;
1031                         break;
1032                 case 27:
1033                         /* ansi color code */
1034                         ptr = (char *)
1035                                 get_ansi_color(dest->window == NULL || dest->window->theme == NULL ?
1036                                                current_theme : dest->window->theme,
1037                                                ptr,
1038                                                hide_text_style ? NULL : &fgcolor,
1039                                                hide_text_style ? NULL : &bgcolor,
1040                                                hide_text_style ? NULL : &flags);
1041                         break;
1042                 }
1043
1044                 str = ptr;
1045         }
1046
1047         g_free(dup);
1048 }
1049
1050 static void read_settings(void)
1051 {
1052         timestamp_level = settings_get_bool("timestamps") ? MSGLEVEL_ALL : 0;
1053         if (timestamp_level > 0) {
1054                 timestamp_level =
1055                         level2bits(settings_get_str("timestamp_level"));
1056         }
1057         timestamp_timeout = settings_get_int("timestamp_timeout");
1058
1059         hide_server_tags = settings_get_bool("hide_server_tags");
1060         hide_text_style = settings_get_bool("hide_text_style");
1061         hide_mirc_colors = settings_get_bool("hide_mirc_colors");
1062 }
1063
1064 void formats_init(void)
1065 {
1066         signal_gui_print_text = signal_get_uniq_id("gui print text");
1067
1068         read_settings();
1069         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
1070 }
1071
1072 void formats_deinit(void)
1073 {
1074         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
1075 }