merges from irssi.org cvs.
[silc.git] / apps / irssi / src / fe-common / core / fe-log.c
1 /*
2  fe-log.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 "commands.h"
25 #include "chat-protocols.h"
26 #include "servers.h"
27 #include "levels.h"
28 #include "misc.h"
29 #include "log.h"
30 #include "special-vars.h"
31 #include "settings.h"
32 #include "lib-config/iconfig.h"
33
34 #include "fe-windows.h"
35 #include "window-items.h"
36 #include "formats.h"
37 #include "themes.h"
38 #include "printtext.h"
39
40 /* close autologs after 5 minutes of inactivity */
41 #define AUTOLOG_INACTIVITY_CLOSE (60*5)
42
43 static int autolog_level;
44 static int autoremove_tag;
45 static const char *autolog_path;
46
47 static THEME_REC *log_theme;
48 static int skip_next_printtext;
49 static const char *log_theme_name;
50
51 static char *log_colorizer_strip(const char *str)
52 {
53         return strip_codes(str);
54 }
55
56 static void log_add_targets(LOG_REC *log, const char *targets, const char *tag)
57 {
58         char **tmp, **items;
59
60         g_return_if_fail(log != NULL);
61         g_return_if_fail(targets != NULL);
62
63         items = g_strsplit(targets, " ", -1);
64
65         for (tmp = items; *tmp != NULL; tmp++)
66                 log_item_add(log, LOG_ITEM_TARGET, *tmp, tag);
67
68         g_strfreev(items);
69 }
70
71 /* SYNTAX: LOG OPEN [-noopen] [-autoopen] [-window] [-<server tag>]
72                     [-targets <targets>] [-colors]
73                     <fname> [<levels>] */
74 static void cmd_log_open(const char *data)
75 {
76         SERVER_REC *server;
77         GHashTable *optlist;
78         char *targetarg, *fname, *levels, *servertag;
79         void *free_arg;
80         char window[MAX_INT_STRLEN];
81         LOG_REC *log;
82         int level;
83
84         if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
85                             PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_OPTIONS,
86                             "log open", &optlist, &fname, &levels))
87                 return;
88         if (*fname == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
89
90         level = level2bits(levels);
91         log = log_create_rec(fname, level != 0 ? level : MSGLEVEL_ALL);
92
93         /* -<server tag> */
94         server = cmd_options_get_server("log open", optlist, NULL);
95         servertag = server == NULL ? NULL : server->tag;
96
97         if (g_hash_table_lookup(optlist, "window")) {
98                 /* log by window ref# */
99                 targetarg = g_hash_table_lookup(optlist, "targets");
100                 if (targetarg == NULL || !is_numeric(targetarg, '\0')) {
101                         ltoa(window, active_win->refnum);
102                         targetarg = window;
103                 }
104                 log_item_add(log, LOG_ITEM_WINDOW_REFNUM, targetarg,
105                              servertag);
106         } else {
107                 targetarg = g_hash_table_lookup(optlist, "targets");
108                 if (targetarg != NULL && *targetarg != '\0')
109                         log_add_targets(log, targetarg, servertag);
110         }
111
112         if (g_hash_table_lookup(optlist, "autoopen"))
113                 log->autoopen = TRUE;
114
115         if (g_hash_table_lookup(optlist, "colors") == NULL)
116                 log->colorizer = log_colorizer_strip;
117
118         log_update(log);
119
120         if (log->handle == -1 && g_hash_table_lookup(optlist, "noopen") == NULL) {
121                 /* start logging */
122                 if (log_start_logging(log)) {
123                         printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
124                                     TXT_LOG_OPENED, fname);
125                 } else {
126                         log_close(log);
127                 }
128         }
129
130         cmd_params_free(free_arg);
131 }
132
133 static LOG_REC *log_find_from_data(const char *data)
134 {
135         GSList *tmp;
136
137         if (!is_numeric(data, ' '))
138                 return log_find(data);
139
140         /* with index number */
141         tmp = g_slist_nth(logs, atoi(data)-1);
142         return tmp == NULL ? NULL : tmp->data;
143 }
144
145 /* SYNTAX: LOG CLOSE <id>|<file> */
146 static void cmd_log_close(const char *data)
147 {
148         LOG_REC *log;
149
150         log = log_find_from_data(data);
151         if (log == NULL)
152                 printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_NOT_OPEN, data);
153         else {
154                 log_close(log);
155                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, data);
156         }
157 }
158
159 /* SYNTAX: LOG START <id>|<file> */
160 static void cmd_log_start(const char *data)
161 {
162         LOG_REC *log;
163
164         log = log_find_from_data(data);
165         if (log != NULL) {
166                 log_start_logging(log);
167                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_OPENED, data);
168         }
169 }
170
171 /* SYNTAX: LOG STOP <id>|<file> */
172 static void cmd_log_stop(const char *data)
173 {
174         LOG_REC *log;
175
176         log = log_find_from_data(data);
177         if (log == NULL || log->handle == -1)
178                 printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_NOT_OPEN, data);
179         else {
180                 log_stop_logging(log);
181                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, data);
182         }
183 }
184
185 static char *log_items_get_list(LOG_REC *log)
186 {
187         GSList *tmp;
188         GString *str;
189         char *ret;
190
191         g_return_val_if_fail(log != NULL, NULL);
192         g_return_val_if_fail(log->items != NULL, NULL);
193
194         str = g_string_new(NULL);
195         for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
196                 LOG_ITEM_REC *rec = tmp->data;
197
198                 g_string_sprintfa(str, "%s, ", rec->name);
199         }
200         g_string_truncate(str, str->len-2);
201
202         ret = str->str;
203         g_string_free(str, FALSE);
204         return ret;
205 }
206
207 static void cmd_log_list(void)
208 {
209         GSList *tmp;
210         char *levelstr, *items;
211         int index;
212
213         printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST_HEADER);
214         for (tmp = logs, index = 1; tmp != NULL; tmp = tmp->next, index++) {
215                 LOG_REC *rec = tmp->data;
216
217                 levelstr = bits2level(rec->level);
218                 items = rec->items == NULL ? NULL :
219                         log_items_get_list(rec);
220
221                 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST,
222                             index, rec->fname, items != NULL ? items : "",
223                             levelstr, rec->autoopen ? " -autoopen" : "");
224
225                 g_free_not_null(items);
226                 g_free(levelstr);
227         }
228         printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST_FOOTER);
229 }
230
231 static void cmd_log(const char *data, SERVER_REC *server, void *item)
232 {
233         if (*data == '\0')
234                 cmd_log_list();
235         else
236                 command_runsub("log", data, server, item);
237 }
238
239 static LOG_REC *logs_find_item(int type, const char *item,
240                                const char *servertag, LOG_ITEM_REC **ret_item)
241 {
242         LOG_ITEM_REC *logitem;
243         GSList *tmp;
244
245         for (tmp = logs; tmp != NULL; tmp = tmp->next) {
246                 LOG_REC *log = tmp->data;
247
248                 logitem = log_item_find(log, type, item, servertag);
249                 if (logitem != NULL) {
250                         if (ret_item != NULL) *ret_item = logitem;
251                         return log;
252                 }
253         }
254
255         return NULL;
256 }
257
258 /* SYNTAX: WINDOW LOG on|off|toggle [<filename>] */
259 static void cmd_window_log(const char *data)
260 {
261         LOG_REC *log;
262         char *set, *fname, window[MAX_INT_STRLEN];
263         void *free_arg;
264         int open_log, close_log;
265
266         if (!cmd_get_params(data, &free_arg, 2, &set, &fname))
267                 return;
268
269         ltoa(window, active_win->refnum);
270         log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, window, NULL, NULL);
271
272         open_log = close_log = FALSE;
273         if (g_strcasecmp(set, "ON") == 0)
274                 open_log = TRUE;
275         else if (g_strcasecmp(set, "OFF") == 0) {
276                 close_log = TRUE;
277         } else if (g_strcasecmp(set, "TOGGLE") == 0) {
278                 open_log = log == NULL;
279                 close_log = log != NULL;
280         } else {
281                 printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_NOT_TOGGLE);
282                 cmd_params_free(free_arg);
283                 return;
284         }
285
286         if (open_log && log == NULL) {
287                 /* irc.log.<windowname> or irc.log.Window<ref#> */
288                 fname = *fname != '\0' ? g_strdup(fname) :
289                         g_strdup_printf("~/irc.log.%s%s",
290                                         active_win->name != NULL ? active_win->name : "Window",
291                                         active_win->name != NULL ? "" : window);
292                 log = log_create_rec(fname, MSGLEVEL_ALL);
293                 log->colorizer = log_colorizer_strip;
294                 log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
295                 log_update(log);
296                 g_free(fname);
297         }
298
299         if (open_log && log != NULL) {
300                 log_start_logging(log);
301                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_OPENED, log->fname);
302         } else if (close_log && log != NULL && log->handle != -1) {
303                 log_stop_logging(log);
304                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, log->fname);
305         }
306
307         cmd_params_free(free_arg);
308 }
309
310 /* Create log file entry to window, but don't start logging */
311 /* SYNTAX: WINDOW LOGFILE <file> */
312 static void cmd_window_logfile(const char *data)
313 {
314         LOG_REC *log;
315         char window[MAX_INT_STRLEN];
316
317         ltoa(window, active_win->refnum);
318         log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, window, NULL, NULL);
319
320         if (log != NULL) {
321                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWLOG_FILE_LOGGING);
322                 return;
323         }
324
325         log = log_create_rec(data, MSGLEVEL_ALL);
326         log->colorizer = log_colorizer_strip;
327         log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
328         log_update(log);
329
330         printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWLOG_FILE, data);
331 }
332
333 /* window's refnum changed - update the logs to log the new window refnum */
334 static void sig_window_refnum_changed(WINDOW_REC *window, gpointer old_refnum)
335 {
336         char winnum[MAX_INT_STRLEN];
337         LOG_REC *log;
338         LOG_ITEM_REC *item;
339
340         ltoa(winnum, GPOINTER_TO_INT(old_refnum));
341         log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, winnum, NULL, &item);
342
343         if (log != NULL) {
344                 ltoa(winnum, window->refnum);
345
346                 g_free(item->name);
347                 item->name = g_strdup(winnum);
348         }
349 }
350
351 static void sig_server_disconnected(SERVER_REC *server)
352 {
353         LOG_ITEM_REC *logitem;
354         GSList *tmp, *next;
355
356         for (tmp = logs; tmp != NULL; tmp = next) {
357                 LOG_REC *log = tmp->data;
358                 next = tmp->next;
359
360                 if (!log->temp || log->items == NULL)
361                         continue;
362
363                 logitem = log->items->data;
364                 if (logitem->type == LOG_ITEM_TARGET &&
365                     logitem->servertag != NULL &&
366                     g_strcasecmp(logitem->servertag, server->tag) == 0 &&
367                     server_ischannel(server, logitem->name)) /* kludge again.. so we won't close dcc chats */
368                         log_close(log);
369         }
370 }
371
372 static void autologs_close_all(void)
373 {
374         GSList *tmp, *next;
375
376         for (tmp = logs; tmp != NULL; tmp = next) {
377                 LOG_REC *rec = tmp->data;
378
379                 next = tmp->next;
380                 if (rec->temp) log_close(rec);
381         }
382 }
383
384 /* '%' -> '%%', '/' -> '_' */
385 static char *escape_target(const char *target)
386 {
387         char *str, *p;
388
389         p = str = g_malloc(strlen(target)*2+1);
390         while (*target != '\0') {
391                 if (*target == '/')
392                         *p++ = '_';
393                 else {
394                         if (*target == '%')
395                                 *p++ = '%';
396                         *p++ = *target;
397                 }
398
399                 target++;
400         }
401         *p = '\0';
402
403         return str;
404 }
405
406 static void autolog_open(SERVER_REC *server, const char *server_tag,
407                          const char *target)
408 {
409         LOG_REC *log;
410         char *fname, *dir, *fixed_target, *params;
411
412         log = logs_find_item(LOG_ITEM_TARGET, target, server_tag, NULL);
413         if (log != NULL && !log->failed) {
414                 log_start_logging(log);
415                 return;
416         }
417
418         /* '/' -> '_' - don't even accidentally try to log to
419            #../../../file if you happen to join to such channel..
420
421            '%' -> '%%' - so strftime() won't mess with them */
422         fixed_target = escape_target(target);
423         if (CHAT_PROTOCOL(server)->case_insensitive)
424                 g_strdown(fixed_target);
425
426         /* $0 = target, $1 = server tag */
427         params = g_strconcat(fixed_target, " ", server_tag, NULL);
428         g_free(fixed_target);
429
430         fname = parse_special_string(autolog_path, server, NULL,
431                                      params, NULL, 0);
432         g_free(params);
433
434         if (log_find(fname) == NULL) {
435                 log = log_create_rec(fname, autolog_level);
436                 if (!settings_get_bool("autolog_colors"))
437                         log->colorizer = log_colorizer_strip;
438                 log_item_add(log, LOG_ITEM_TARGET, target, server_tag);
439
440                 dir = g_dirname(log->real_fname);
441                 mkpath(dir, LOG_DIR_CREATE_MODE);
442                 g_free(dir);
443
444                 log->temp = TRUE;
445                 log_update(log);
446                 log_start_logging(log);
447         }
448         g_free(fname);
449 }
450
451 static void autolog_open_check(SERVER_REC *server, const char *server_tag,
452                                const char *target, int level)
453 {
454         char **targets, **tmp;
455
456         /* FIXME: kind of a kludge, but we don't want to reopen logs when
457            we're parting the channel with /WINDOW CLOSE.. Maybe a small
458            timeout would be nice instead of immediately closing the log file
459            after "window item destroyed" */
460         if (level == MSGLEVEL_PARTS ||
461             (autolog_level & level) == 0 || target == NULL || *target == '\0')
462                 return;
463
464         /* there can be multiple targets separated with comma */
465         targets = g_strsplit(target, ",", -1);
466         for (tmp = targets; *tmp != NULL; tmp++)
467                 autolog_open(server, server_tag, *tmp);
468         g_strfreev(targets);
469 }
470
471 static void log_single_line(WINDOW_REC *window, const char *server_tag,
472                             const char *target, int level, const char *text)
473 {
474         char windownum[MAX_INT_STRLEN];
475         char **targets, **tmp;
476         LOG_REC *log;
477
478         /* save to log created with /WINDOW LOG */
479         ltoa(windownum, window->refnum);
480         log = logs_find_item(LOG_ITEM_WINDOW_REFNUM,
481                              windownum, NULL, NULL);
482         if (log != NULL) {
483                 log_write_rec(log, text, level);
484         }
485
486         if (target == NULL)
487                 log_file_write(server_tag, NULL, level, text, FALSE);
488         else {
489                 /* there can be multiple items separated with comma */
490                 targets = g_strsplit(target, ",", -1);
491                 for (tmp = targets; *tmp != NULL; tmp++)
492                         log_file_write(server_tag, *tmp, level, text, FALSE);
493                 g_strfreev(targets);
494         }
495 }
496
497 static void log_line(TEXT_DEST_REC *dest, const char *text)
498 {
499         char **lines, **tmp;
500
501         if (dest->level == MSGLEVEL_NEVER)
502                 return;
503
504         /* let autolog open the log records */
505         autolog_open_check(dest->server, dest->server_tag,
506                            dest->target, dest->level);
507
508         if (logs == NULL)
509                 return;
510
511         /* text may contain one or more lines, log wants to eat them one
512            line at a time */
513         lines = g_strsplit(text, "\n", -1);
514         for (tmp = lines; *tmp != NULL; tmp++)
515                 log_single_line(dest->window, dest->server_tag,
516                                 dest->target, dest->level, *tmp);
517         g_strfreev(lines);
518 }
519
520 static void sig_printtext(TEXT_DEST_REC *dest, const char *text,
521                           const char *stripped)
522 {
523         if (skip_next_printtext) {
524                 skip_next_printtext = FALSE;
525                 return;
526         }
527
528         log_line(dest, text);
529 }
530
531 static void sig_print_format(THEME_REC *theme, const char *module,
532                              TEXT_DEST_REC *dest, void *formatnum, char **args)
533 {
534         char *str, *linestart, *tmp;
535
536         if (log_theme == NULL) {
537                 /* theme isn't loaded for some reason (/reload destroys it),
538                    reload it. */
539                 log_theme = theme_load(log_theme_name);
540                 if (log_theme == NULL) return;
541         }
542
543         if (theme == log_theme)
544                 return;
545
546         str = format_get_text_theme_charargs(log_theme, module, dest,
547                                              GPOINTER_TO_INT(formatnum), args);
548         skip_next_printtext = TRUE;
549
550         if (*str != '\0') {
551                 /* add the line start format */
552                 linestart = format_get_level_tag(log_theme, dest);
553                 tmp = str;
554                 str = format_add_linestart(tmp, linestart);
555                 g_free_not_null(linestart);
556                 g_free(tmp);
557
558                 /* strip colors from text, log it. */
559                 log_line(dest, str);
560         }
561         g_free(str);
562
563 }
564
565 static int sig_autoremove(void)
566 {
567         SERVER_REC *server;
568         LOG_ITEM_REC *logitem;
569         GSList *tmp, *next;
570         time_t removetime;
571
572         removetime = time(NULL)-AUTOLOG_INACTIVITY_CLOSE;
573         for (tmp = logs; tmp != NULL; tmp = next) {
574                 LOG_REC *log = tmp->data;
575
576                 next = tmp->next;
577
578                 if (!log->temp || log->last > removetime || log->items == NULL)
579                         continue;
580
581                 /* Close only logs with private messages */
582                 logitem = log->items->data;
583                 if (logitem->servertag == NULL)
584                         continue;
585
586                 server = server_find_tag(logitem->servertag);
587                 if (logitem->type == LOG_ITEM_TARGET &&
588                     server != NULL && !server_ischannel(server, logitem->name))
589                         log_close(log);
590         }
591         return 1;
592 }
593
594 static void sig_window_item_destroy(WINDOW_REC *window, WI_ITEM_REC *item)
595 {
596         LOG_REC *log;
597
598         log = logs_find_item(LOG_ITEM_TARGET, item->name,
599                              item->server == NULL ? NULL :
600                              item->server->tag, NULL);
601         if (log != NULL && log->temp)
602                 log_close(log);
603 }
604
605 static void sig_log_locked(LOG_REC *log)
606 {
607         printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
608                     TXT_LOG_LOCKED, log->fname);
609 }
610
611 static void sig_log_create_failed(LOG_REC *log)
612 {
613         printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
614                     TXT_LOG_CREATE_FAILED,
615                     log->real_fname, g_strerror(errno));
616 }
617
618 static void sig_log_new(LOG_REC *log)
619 {
620         if (!settings_get_bool("awaylog_colors") &&
621             strcmp(log->fname, settings_get_str("awaylog_file")) == 0)
622                 log->colorizer = log_colorizer_strip;
623 }
624
625 static void sig_log_config_read(LOG_REC *log, CONFIG_NODE *node)
626 {
627         if (!config_node_get_bool(node, "colors", FALSE))
628                 log->colorizer = log_colorizer_strip;
629 }
630
631 static void sig_log_config_save(LOG_REC *log, CONFIG_NODE *node)
632 {
633         if (log->colorizer == NULL)
634                 iconfig_node_set_bool(node, "colors", TRUE);
635         else
636                 iconfig_node_set_str(node, "colors", NULL);
637 }
638
639 static void sig_awaylog_show(LOG_REC *log, gpointer pmsgs, gpointer pfilepos)
640 {
641         char *str;
642         int msgs, filepos;
643
644         msgs = GPOINTER_TO_INT(pmsgs);
645         filepos = GPOINTER_TO_INT(pfilepos);
646
647         if (msgs == 0)
648                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_NO_AWAY_MSGS, log->fname);
649         else {
650                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_AWAY_MSGS, log->fname, msgs);
651
652                 str = g_strdup_printf("\"%s\" %d", log->fname, filepos);
653                 signal_emit("command cat", 1, str);
654                 g_free(str);
655         }
656 }
657
658 static void sig_theme_destroyed(THEME_REC *theme)
659 {
660         if (theme == log_theme)
661                 log_theme = NULL;
662 }
663
664 static void read_settings(void)
665 {
666         int old_autolog = autolog_level;
667
668         autolog_path = settings_get_str("autolog_path");
669         autolog_level = !settings_get_bool("autolog") ? 0 :
670                 level2bits(settings_get_str("autolog_level"));
671
672         if (old_autolog && !autolog_level)
673                 autologs_close_all();
674
675         /* write to log files with different theme? */
676         if (log_theme_name != NULL)
677                 signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
678         log_theme_name = settings_get_str("log_theme");
679         if (*log_theme_name == '\0')
680                 log_theme_name = NULL;
681         else
682                 signal_add("print format", (SIGNAL_FUNC) sig_print_format);
683
684         log_theme = log_theme_name == NULL ? NULL :
685                 theme_load(log_theme_name);
686 }
687
688 void fe_log_init(void)
689 {
690         autoremove_tag = g_timeout_add(60000, (GSourceFunc) sig_autoremove, NULL);
691         skip_next_printtext = FALSE;
692
693         settings_add_bool("log", "awaylog_colors", TRUE);
694         settings_add_bool("log", "autolog", FALSE);
695         settings_add_bool("log", "autolog_colors", FALSE);
696         settings_add_str("log", "autolog_path", "~/irclogs/$tag/$0.log");
697         settings_add_str("log", "autolog_level", "all -crap -clientcrap -ctcps");
698         settings_add_str("log", "log_theme", "");
699
700         autolog_level = 0;
701         log_theme_name = NULL;
702         read_settings();
703
704         command_bind("log", NULL, (SIGNAL_FUNC) cmd_log);
705         command_bind("log open", NULL, (SIGNAL_FUNC) cmd_log_open);
706         command_bind("log close", NULL, (SIGNAL_FUNC) cmd_log_close);
707         command_bind("log start", NULL, (SIGNAL_FUNC) cmd_log_start);
708         command_bind("log stop", NULL, (SIGNAL_FUNC) cmd_log_stop);
709         command_bind("window log", NULL, (SIGNAL_FUNC) cmd_window_log);
710         command_bind("window logfile", NULL, (SIGNAL_FUNC) cmd_window_logfile);
711         signal_add_first("print text", (SIGNAL_FUNC) sig_printtext);
712         signal_add("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy);
713         signal_add("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
714         signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
715         signal_add("log locked", (SIGNAL_FUNC) sig_log_locked);
716         signal_add("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
717         signal_add("log new", (SIGNAL_FUNC) sig_log_new);
718         signal_add("log config read", (SIGNAL_FUNC) sig_log_config_read);
719         signal_add("log config save", (SIGNAL_FUNC) sig_log_config_save);
720         signal_add("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
721         signal_add("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
722         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
723
724         command_set_options("log open", "noopen autoopen -targets window colors");
725 }
726
727 void fe_log_deinit(void)
728 {
729         g_source_remove(autoremove_tag);
730         if (log_theme_name != NULL)
731                 signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
732
733         command_unbind("log", (SIGNAL_FUNC) cmd_log);
734         command_unbind("log open", (SIGNAL_FUNC) cmd_log_open);
735         command_unbind("log close", (SIGNAL_FUNC) cmd_log_close);
736         command_unbind("log start", (SIGNAL_FUNC) cmd_log_start);
737         command_unbind("log stop", (SIGNAL_FUNC) cmd_log_stop);
738         command_unbind("window log", (SIGNAL_FUNC) cmd_window_log);
739         command_unbind("window logfile", (SIGNAL_FUNC) cmd_window_logfile);
740         signal_remove("print text", (SIGNAL_FUNC) sig_printtext);
741         signal_remove("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy);
742         signal_remove("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
743         signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
744         signal_remove("log locked", (SIGNAL_FUNC) sig_log_locked);
745         signal_remove("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
746         signal_remove("log new", (SIGNAL_FUNC) sig_log_new);
747         signal_remove("log config read", (SIGNAL_FUNC) sig_log_config_read);
748         signal_remove("log config save", (SIGNAL_FUNC) sig_log_config_save);
749         signal_remove("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
750         signal_remove("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
751         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
752 }