addition of silc.css
[runtime.git] / apps / irssi / src / fe-common / core / chat-completion.c
1 /*
2  chat-completion.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 "signals.h"
23 #include "commands.h"
24 #include "misc.h"
25 #include "settings.h"
26
27 #include "chatnets.h"
28 #include "servers-setup.h"
29 #include "channels.h"
30 #include "channels-setup.h"
31 #include "queries.h"
32 #include "nicklist.h"
33
34 #include "completion.h"
35 #include "window-items.h"
36
37 static int keep_privates_count, keep_publics_count;
38 static int completion_lowercase;
39 static const char *completion_char, *cmdchars;
40 static GSList *global_lastmsgs;
41 static int completion_auto, completion_strict;
42
43 #define SERVER_LAST_MSG_ADD(server, nick) \
44         last_msg_add(&((MODULE_SERVER_REC *) MODULE_DATA(server))->lastmsgs, \
45                      nick, TRUE, keep_privates_count)
46
47 #define CHANNEL_LAST_MSG_ADD(channel, nick, own) \
48         last_msg_add(&((MODULE_CHANNEL_REC *) MODULE_DATA(channel))->lastmsgs, \
49                      nick, own, keep_publics_count)
50
51 static LAST_MSG_REC *last_msg_find(GSList *list, const char *nick)
52 {
53         while (list != NULL) {
54                 LAST_MSG_REC *rec = list->data;
55
56                 if (g_strcasecmp(rec->nick, nick) == 0)
57                         return rec;
58                 list = list->next;
59         }
60
61         return NULL;
62 }
63
64 static void last_msg_dec_owns(GSList *list)
65 {
66         LAST_MSG_REC *rec;
67
68         while (list != NULL) {
69                 rec = list->data;
70                 if (rec->own) rec->own--;
71
72                 list = list->next;
73         }
74 }
75
76 static void last_msg_add(GSList **list, const char *nick, int own, int max)
77 {
78         LAST_MSG_REC *rec;
79
80         rec = last_msg_find(*list, nick);
81         if (rec != NULL) {
82                 /* msg already exists, update it */
83                 *list = g_slist_remove(*list, rec);
84                 if (own)
85                         rec->own = max;
86                 else if (rec->own)
87                         rec->own--;
88         } else {
89                 rec = g_new(LAST_MSG_REC, 1);
90                 rec->nick = g_strdup(nick);
91
92                 if ((int)g_slist_length(*list) == max) {
93                         *list = g_slist_remove(*list,
94                                                g_slist_last(*list)->data);
95                 }
96
97                 rec->own = own ? max : 0;
98         }
99         rec->time = time(NULL);
100
101         last_msg_dec_owns(*list);
102
103         *list = g_slist_prepend(*list, rec);
104 }
105
106 static void last_msg_destroy(GSList **list, LAST_MSG_REC *rec)
107 {
108         *list = g_slist_remove(*list, rec);
109
110         g_free(rec->nick);
111         g_free(rec);
112 }
113
114 void completion_last_message_add(const char *nick)
115 {
116         g_return_if_fail(nick != NULL);
117
118         last_msg_add(&global_lastmsgs, nick, TRUE, keep_privates_count);
119 }
120
121 void completion_last_message_remove(const char *nick)
122 {
123         LAST_MSG_REC *rec;
124
125         g_return_if_fail(nick != NULL);
126
127         rec = last_msg_find(global_lastmsgs, nick);
128         if (rec != NULL) last_msg_destroy(&global_lastmsgs, rec);
129 }
130
131 void completion_last_message_rename(const char *oldnick, const char *newnick)
132 {
133         LAST_MSG_REC *rec;
134
135         g_return_if_fail(oldnick != NULL);
136         g_return_if_fail(newnick != NULL);
137
138         rec = last_msg_find(global_lastmsgs, oldnick);
139         if (rec != NULL) {
140                 g_free(rec->nick);
141                 rec->nick = g_strdup(newnick);
142         }
143 }
144
145 static void sig_message_public(SERVER_REC *server, const char *msg,
146                                const char *nick, const char *address,
147                                const char *target)
148 {
149         CHANNEL_REC *channel;
150         int own;
151
152         channel = channel_find(server, target);
153         if (channel != NULL) {
154                 own = nick_match_msg(channel, msg, server->nick);
155                 CHANNEL_LAST_MSG_ADD(channel, nick, own);
156         }
157 }
158
159 static void sig_message_private(SERVER_REC *server, const char *msg,
160                                 const char *nick, const char *address)
161 {
162         g_return_if_fail(server != NULL);
163         g_return_if_fail(nick != NULL);
164
165         SERVER_LAST_MSG_ADD(server, nick);
166 }
167
168 static void sig_message_own_public(SERVER_REC *server, const char *msg,
169                                    const char *target, const char *origtarget)
170 {
171         CHANNEL_REC *channel;
172         NICK_REC *nick;
173         char *p, *msgnick;
174
175         g_return_if_fail(server != NULL);
176         g_return_if_fail(msg != NULL);
177         if (target == NULL) return;
178
179         channel = channel_find(server, target);
180         if (channel == NULL)
181                 return;
182
183         /* channel msg - if first word in line is nick,
184            add it to lastmsgs */
185         p = strchr(msg, ' ');
186         if (p != NULL && p != msg) {
187                 msgnick = g_strndup(msg, (int) (p-msg));
188                 nick = nicklist_find(channel, msgnick);
189                 if (nick == NULL && msgnick[1] != '\0') {
190                         /* probably ':' or ',' or some other
191                            char after nick, try without it */
192                         msgnick[strlen(msgnick)-1] = '\0';
193                         nick = nicklist_find(channel, msgnick);
194                 }
195                 g_free(msgnick);
196                 if (nick != NULL && nick != channel->ownnick)
197                         CHANNEL_LAST_MSG_ADD(channel, nick->nick, TRUE);
198         }
199 }
200
201 static void sig_message_own_private(SERVER_REC *server, const char *msg,
202                                     const char *target, const char *origtarget)
203 {
204         g_return_if_fail(server != NULL);
205         g_return_if_fail(target != NULL);
206
207         if (target != NULL && query_find(server, target) == NULL)
208                 SERVER_LAST_MSG_ADD(server, target);
209 }
210
211 static void sig_nick_removed(CHANNEL_REC *channel, NICK_REC *nick)
212 {
213         MODULE_CHANNEL_REC *mchannel;
214         LAST_MSG_REC *rec;
215
216         mchannel = MODULE_DATA(channel);
217         rec = last_msg_find(mchannel->lastmsgs, nick->nick);
218         if (rec != NULL) last_msg_destroy(&mchannel->lastmsgs, rec);
219 }
220
221 static void sig_nick_changed(CHANNEL_REC *channel, NICK_REC *nick,
222                              const char *oldnick)
223 {
224         MODULE_CHANNEL_REC *mchannel;
225         LAST_MSG_REC *rec;
226
227         mchannel = MODULE_DATA(channel);
228         rec = last_msg_find(mchannel->lastmsgs, oldnick);
229         if (rec != NULL) {
230                 g_free(rec->nick);
231                 rec->nick = g_strdup(nick->nick);
232         }
233 }
234
235 static int last_msg_cmp(LAST_MSG_REC *m1, LAST_MSG_REC *m2)
236 {
237         return m1->time < m2->time ? 1 : -1;
238 }
239
240 /* Complete /MSG from specified server, or from
241    global_lastmsgs if server is NULL */
242 static void completion_msg_server(GSList **list, SERVER_REC *server,
243                                   const char *nick, const char *prefix)
244 {
245         LAST_MSG_REC *msg;
246         GSList *tmp;
247         int len;
248
249         g_return_if_fail(nick != NULL);
250
251         len = strlen(nick);
252         tmp = server == NULL ? global_lastmsgs :
253                 ((MODULE_SERVER_REC *) MODULE_DATA(server))->lastmsgs;
254         for (; tmp != NULL; tmp = tmp->next) {
255                 LAST_MSG_REC *rec = tmp->data;
256
257                 if (len != 0 && g_strncasecmp(rec->nick, nick, len) != 0)
258                         continue;
259
260                 msg = g_new(LAST_MSG_REC, 1);
261                 msg->time = rec->time;
262                 msg->nick = prefix == NULL || *prefix == '\0' ?
263                         g_strdup(rec->nick) :
264                         g_strconcat(prefix, " ", rec->nick, NULL);
265                 *list = g_slist_insert_sorted(*list, msg,
266                                               (GCompareFunc) last_msg_cmp);
267         }
268 }
269
270 /* convert list of LAST_MSG_REC's to list of char* nicks. */
271 static GList *convert_msglist(GSList *msglist)
272 {
273         GList *list;
274
275         list = NULL;
276         while (msglist != NULL) {
277                 LAST_MSG_REC *rec = msglist->data;
278
279                 list = g_list_append(list, rec->nick);
280                 msglist = g_slist_remove(msglist, rec);
281                 g_free(rec);
282         }
283
284         return list;
285 }
286
287 /* Complete /MSG - if `find_server' is NULL, complete nicks from all servers */
288 static GList *completion_msg(SERVER_REC *win_server,
289                              SERVER_REC *find_server,
290                              const char *nick, const char *prefix)
291 {
292         GSList *tmp, *list;
293         char *newprefix;
294
295         g_return_val_if_fail(nick != NULL, NULL);
296         if (servers == NULL) return NULL;
297
298         list = NULL;
299         if (find_server != NULL) {
300                 completion_msg_server(&list, find_server, nick, prefix);
301                 return convert_msglist(list);
302         }
303
304         completion_msg_server(&list, NULL, nick, prefix);
305         for (tmp = servers; tmp != NULL; tmp = tmp->next) {
306                 SERVER_REC *rec = tmp->data;
307
308                 if (rec == win_server)
309                         newprefix = g_strdup(prefix);
310                 else {
311                         newprefix = prefix == NULL ?
312                                 g_strdup_printf("-%s", rec->tag) :
313                                 g_strdup_printf("%s -%s", prefix, rec->tag);
314                 }
315
316                 completion_msg_server(&list, rec, nick, newprefix);
317                 g_free_not_null(newprefix);
318         }
319
320         return convert_msglist(list);
321 }
322
323 static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
324                                    const char *nick, const char *suffix)
325 {
326         MODULE_CHANNEL_REC *mchannel;
327         GSList *tmp;
328         GList *ownlist;
329         char *str;
330         int len;
331
332         /* go through the last x nicks who have said something in the channel.
333            nicks of all the "own messages" are placed before others */
334         ownlist = NULL;
335         len = strlen(nick);
336         mchannel = MODULE_DATA(channel);
337         for (tmp = mchannel->lastmsgs; tmp != NULL; tmp = tmp->next) {
338                 LAST_MSG_REC *rec = tmp->data;
339
340                 if (g_strncasecmp(rec->nick, nick, len) == 0 &&
341                     glist_find_icase_string(*outlist, rec->nick) == NULL) {
342                         str = g_strconcat(rec->nick, suffix, NULL);
343                         if (completion_lowercase) g_strdown(str);
344                         if (rec->own)
345                                 ownlist = g_list_append(ownlist, str);
346                         else
347                                 *outlist = g_list_append(*outlist, str);
348                 }
349         }
350
351         *outlist = g_list_concat(ownlist, *outlist);
352 }
353
354 static GList *completion_nicks_nonstrict(CHANNEL_REC *channel,
355                                          const char *nick,
356                                          const char *suffix)
357 {
358         GSList *nicks, *tmp;
359         GList *list;
360         char *tnick, *str, *in, *out;
361         int len, str_len, tmplen;
362
363         g_return_val_if_fail(channel != NULL, NULL);
364
365         list = NULL;
366
367         /* get all nicks from current channel, strip non alnum chars,
368            compare again and add to completion list on matching */
369         len = strlen(nick);
370         nicks = nicklist_getnicks(channel);
371
372         str_len = 80; str = g_malloc(str_len+1);
373         for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
374                 NICK_REC *rec = tmp->data;
375
376                 tmplen = strlen(rec->nick);
377                 if (tmplen > str_len) {
378                         str_len = tmplen*2;
379                         str = g_realloc(str, str_len+1);
380                 }
381
382                 /* remove non alnum chars from nick */
383                 in = rec->nick; out = str;
384                 while (*in != '\0') {
385                         if (isalnum(*in))
386                                 *out++ = *in;
387                         in++;
388                 }
389                 *out = '\0';
390
391                 /* add to list if 'cleaned' nick matches */
392                 if (g_strncasecmp(str, nick, len) == 0) {
393                         tnick = g_strconcat(rec->nick, suffix, NULL);
394                         if (completion_lowercase)
395                                 g_strdown(tnick);
396
397                         if (glist_find_icase_string(list, tnick) == NULL)
398                                 list = g_list_append(list, tnick);
399                         else
400                                 g_free(tnick);
401                 }
402
403         }
404         g_free(str);
405         g_slist_free(nicks);
406
407         return list;
408 }
409
410 static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
411                                        const char *suffix)
412 {
413         GSList *nicks, *tmp;
414         GList *list;
415         char *str;
416         int len;
417
418         g_return_val_if_fail(channel != NULL, NULL);
419         g_return_val_if_fail(nick != NULL, NULL);
420         if (*nick == '\0') return NULL;
421
422         if (suffix != NULL && *suffix == '\0')
423                 suffix = NULL;
424
425         /* put first the nicks who have recently said something */
426         list = NULL;
427         complete_from_nicklist(&list, channel, nick, suffix);
428
429         /* and add the rest of the nicks too */
430         len = strlen(nick);
431         nicks = nicklist_getnicks(channel);
432         for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
433                 NICK_REC *rec = tmp->data;
434
435                 if (g_strncasecmp(rec->nick, nick, len) == 0 &&
436                     rec != channel->ownnick) {
437                         str = g_strconcat(rec->nick, suffix, NULL);
438                         if (completion_lowercase)
439                                 g_strdown(str);
440                         if (glist_find_icase_string(list, str) == NULL)
441                                 list = g_list_append(list, str);
442                         else
443                                 g_free(str);
444                 }
445         }
446         g_slist_free(nicks);
447
448         /* remove non alphanum chars from nick and search again in case
449            list is still NULL ("foo<tab>" would match "_foo_" f.e.) */
450         if (!completion_strict)
451                 list = g_list_concat(list, completion_nicks_nonstrict(channel, nick, suffix));
452         return list;
453 }
454
455 /* append all strings in list2 to list1 that already aren't there and
456    free list2 */
457 static GList *completion_joinlist(GList *list1, GList *list2)
458 {
459         GList *old;
460
461         old = list2;
462         while (list2 != NULL) {
463                 if (!glist_find_icase_string(list1, list2->data))
464                         list1 = g_list_append(list1, list2->data);
465                 else
466                         g_free(list2->data);
467
468                 list2 = list2->next;
469         }
470
471         g_list_free(old);
472         return list1;
473 }
474
475 GList *completion_get_channels(SERVER_REC *server, const char *word)
476 {
477         GList *list;
478         GSList *tmp;
479         int len;
480
481         g_return_val_if_fail(word != NULL, NULL);
482         g_return_val_if_fail(*word != '\0', NULL);
483
484         len = strlen(word);
485         list = NULL;
486
487         /* first get the joined channels */
488         tmp = server == NULL ? NULL : server->channels;
489         for (; tmp != NULL; tmp = tmp->next) {
490                 CHANNEL_REC *rec = tmp->data;
491
492                 if (g_strncasecmp(rec->name, word, len) == 0)
493                         list = g_list_append(list, g_strdup(rec->name));
494         }
495
496         /* get channels from setup */
497         for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
498                 CHANNEL_SETUP_REC *rec = tmp->data;
499
500                 if (g_strncasecmp(rec->name, word, len) == 0 &&
501                     glist_find_icase_string(list, rec->name) == NULL)
502                         list = g_list_append(list, g_strdup(rec->name));
503
504         }
505
506         return list;
507 }
508
509 static void complete_window_nicks(GList **list, WINDOW_REC *window,
510                                   const char *word, const char *linestart)
511 {
512         CHANNEL_REC *channel;
513         GList *tmplist;
514         GSList *tmp;
515         const char *nicksuffix;
516
517         nicksuffix = *linestart != '\0' ? NULL : completion_char;
518
519         channel = CHANNEL(window->active);
520
521         /* first the active channel */
522         if (channel != NULL) {
523                 tmplist = completion_channel_nicks(channel, word, nicksuffix);
524                 *list = completion_joinlist(*list, tmplist);
525         }
526
527         if (nicksuffix != NULL) {
528                 /* completing nick at the start of line - probably answering
529                    to some other nick, don't even try to complete from
530                    non-active channels */
531                 return;
532         }
533
534         /* then the rest */
535         for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
536                 channel = CHANNEL(tmp->data);
537                 if (channel != NULL && tmp->data != window->active) {
538                         tmplist = completion_channel_nicks(channel, word,
539                                                            nicksuffix);
540                         *list = completion_joinlist(*list, tmplist);
541                 }
542         }
543 }
544
545 static void sig_complete_word(GList **list, WINDOW_REC *window,
546                               const char *word, const char *linestart)
547 {
548         SERVER_REC *server;
549         CHANNEL_REC *channel;
550         QUERY_REC *query;
551         char *prefix;
552
553         g_return_if_fail(list != NULL);
554         g_return_if_fail(window != NULL);
555         g_return_if_fail(word != NULL);
556         g_return_if_fail(linestart != NULL);
557
558         server = window->active_server;
559         if (server == NULL && servers != NULL)
560                 server = servers->data;
561
562         if (server != NULL && server->ischannel(word)) {
563                 /* probably completing a channel name */
564                 *list = completion_get_channels(window->active_server, word);
565                 return;
566         }
567
568         server = window->active_server;
569         if (server == NULL || !server->connected)
570                 return;
571
572         if (*linestart == '\0' && *word == '\0') {
573                 /* pressed TAB at the start of line - add /MSG */
574                 prefix = g_strdup_printf("%cmsg", *cmdchars);
575                 *list = completion_msg(server, NULL, "", prefix);
576                 if (*list == NULL)
577                         *list = g_list_append(*list, g_strdup(prefix));
578                 g_free(prefix);
579
580                 signal_stop();
581                 return;
582         }
583
584         channel = CHANNEL(window->active);
585         query = QUERY(window->active);
586         if (channel == NULL && query != NULL &&
587             g_strncasecmp(word, query->name, strlen(word)) == 0) {
588                 /* completion in query */
589                 *list = g_list_append(*list, g_strdup(query->name));
590         } else if (channel != NULL) {
591                 /* nick completion .. we could also be completing a nick
592                    after /MSG from nicks in channel */
593                 complete_window_nicks(list, window, word, linestart);
594         }
595
596         if (*list != NULL) signal_stop();
597 }
598
599 static SERVER_REC *line_get_server(const char *line)
600 {
601         SERVER_REC *server;
602         char *tag, *ptr;
603
604         g_return_val_if_fail(line != NULL, NULL);
605         if (*line != '-') return NULL;
606
607         /* -option found - should be server tag */
608         tag = g_strdup(line+1);
609         ptr = strchr(tag, ' ');
610         if (ptr != NULL) *ptr = '\0';
611
612         server = server_find_tag(tag);
613
614         g_free(tag);
615         return server;
616 }
617
618 static void sig_complete_msg(GList **list, WINDOW_REC *window,
619                              const char *word, const char *line,
620                              int *want_space)
621 {
622         SERVER_REC *server, *msgserver;
623
624         g_return_if_fail(list != NULL);
625         g_return_if_fail(word != NULL);
626         g_return_if_fail(line != NULL);
627
628         server = window->active_server;
629         if (server == NULL || !server->connected)
630                 return;
631
632         msgserver = line_get_server(line);
633         *list = completion_msg(server, msgserver, word, NULL);
634         if (*list != NULL) signal_stop();
635 }
636
637 GList *completion_get_chatnets(const char *word)
638 {
639         GList *list;
640         GSList *tmp;
641         int len;
642
643         g_return_val_if_fail(word != NULL, NULL);
644
645         len = strlen(word);
646         list = NULL;
647
648         for (tmp = chatnets; tmp != NULL; tmp = tmp->next) {
649                 CHATNET_REC *rec = tmp->data;
650
651                 if (g_strncasecmp(rec->name, word, len) == 0)
652                         list = g_list_append(list, g_strdup(rec->name));
653         }
654
655         return list;
656 }
657
658 GList *completion_get_servers(const char *word)
659 {
660         GList *list;
661         GSList *tmp;
662         int len;
663
664         g_return_val_if_fail(word != NULL, NULL);
665
666         len = strlen(word);
667         list = NULL;
668
669         for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
670                 SERVER_SETUP_REC *rec = tmp->data;
671
672                 if (g_strncasecmp(rec->address, word, len) == 0) 
673                         list = g_list_append(list, g_strdup(rec->address));
674         }
675
676         return list;
677 }
678
679 static void sig_complete_connect(GList **list, WINDOW_REC *window,
680                                  const char *word, const char *line, 
681                                  int *want_space)
682 {
683         g_return_if_fail(list != NULL);
684         g_return_if_fail(word != NULL);
685
686         *list = completion_get_chatnets(word);
687         *list = g_list_concat(*list, completion_get_servers(word));
688         if (*list != NULL) signal_stop();
689 }
690
691 /* expand \n, \t and \\ */
692 static char *expand_escapes(const char *line, SERVER_REC *server,
693                             WI_ITEM_REC *item)
694 {
695         char *ptr, *ret;
696
697         ret = ptr = g_malloc(strlen(line)+1);
698         for (; *line != '\0'; line++) {
699                 if (*line != '\\') {
700                         *ptr++ = *line;
701                         continue;
702                 }
703
704                 line++;
705                 if (*line == '\0') {
706                         *ptr++ = '\\';
707                         break;
708                 }
709
710                 switch (*line) {
711                 case 'n':
712                         /* newline .. we need to send another "send text"
713                            event to handle it (or actually the text before
714                            the newline..) */
715                         *ptr = '\0';
716                         signal_emit("send text", 3, ret, server, item);
717                         ptr = ret;
718                         break;
719                 case 't':
720                         *ptr++ = '\t';
721                         break;
722                 case '\\':
723                         *ptr++ = '\\';
724                         break;
725                 default:
726                         *ptr++ = '\\';
727                         *ptr++ = *line;
728                         break;
729                 }
730         }
731
732         *ptr = '\0';
733         return ret;
734 }
735
736 static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
737 {
738         CHANNEL_REC *channel;
739         GList *comp;
740         char *line, *str, *ptr, comp_char;
741
742         g_return_if_fail(data != NULL);
743         if (item == NULL) return;
744
745         line = settings_get_bool("expand_escapes") ?
746                 expand_escapes(data, server, item) : g_strdup(data);
747         comp_char = *completion_char;
748
749         /* check for automatic nick completion */
750         ptr = NULL;
751         comp = NULL;
752         channel = CHANNEL(item);
753
754         if (completion_auto && channel != NULL && comp_char != '\0') {
755                 ptr = strchr(line, comp_char);
756                 if (ptr != NULL) {
757                         *ptr++ = '\0';
758                         if (nicklist_find(channel, line) == NULL) {
759                                 comp = completion_channel_nicks(channel,
760                                                                 line, NULL);
761                         }
762                 }
763         }
764
765         str = g_strdup_printf(ptr == NULL ? "%s %s" : "%s %s%c%s", item->name,
766                               comp != NULL ? (char *) comp->data : line,
767                               comp_char, ptr);
768         signal_emit("command msg", 3, str, server, item);
769
770         g_free(str);
771         g_free(line);
772
773         if (comp != NULL) {
774                 g_list_foreach(comp, (GFunc) g_free, NULL);
775                 g_list_free(comp);
776         }
777
778         signal_stop();
779 }
780
781 static void sig_server_disconnected(SERVER_REC *server)
782 {
783         MODULE_SERVER_REC *mserver;
784
785         g_return_if_fail(server != NULL);
786
787         mserver = MODULE_DATA(server);
788         while (mserver->lastmsgs)
789                 last_msg_destroy(&mserver->lastmsgs, mserver->lastmsgs->data);
790 }
791
792 static void sig_channel_destroyed(CHANNEL_REC *channel)
793 {
794         MODULE_CHANNEL_REC *mchannel;
795
796         g_return_if_fail(channel != NULL);
797
798         mchannel = MODULE_DATA(channel);
799         while (mchannel->lastmsgs != NULL) {
800                 last_msg_destroy(&mchannel->lastmsgs,
801                                  mchannel->lastmsgs->data);
802         }
803 }
804
805 static void read_settings(void)
806 {
807         keep_privates_count = settings_get_int("completion_keep_privates");
808         keep_publics_count = settings_get_int("completion_keep_publics");
809         completion_lowercase = settings_get_bool("completion_nicks_lowercase");
810         completion_char = settings_get_str("completion_char");
811         cmdchars = settings_get_str("cmdchars");
812         completion_auto = settings_get_bool("completion_auto");
813         completion_strict = settings_get_bool("completion_strict");
814 }
815
816 void chat_completion_init(void)
817 {
818         settings_add_str("completion", "completion_char", ":");
819         settings_add_bool("completion", "completion_auto", FALSE);
820         settings_add_int("completion", "completion_keep_publics", 50);
821         settings_add_int("completion", "completion_keep_privates", 10);
822         settings_add_bool("completion", "expand_escapes", FALSE);
823         settings_add_bool("completion", "completion_nicks_lowercase", FALSE);
824         settings_add_bool("completion", "completion_strict", FALSE);
825
826         read_settings();
827         signal_add("complete word", (SIGNAL_FUNC) sig_complete_word);
828         signal_add("complete command msg", (SIGNAL_FUNC) sig_complete_msg);
829         signal_add("complete command connect", (SIGNAL_FUNC) sig_complete_connect);
830         signal_add("complete command server", (SIGNAL_FUNC) sig_complete_connect);
831         signal_add("message public", (SIGNAL_FUNC) sig_message_public);
832         signal_add("message private", (SIGNAL_FUNC) sig_message_private);
833         signal_add("message own_public", (SIGNAL_FUNC) sig_message_own_public);
834         signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private);
835         signal_add("nicklist remove", (SIGNAL_FUNC) sig_nick_removed);
836         signal_add("nicklist changed", (SIGNAL_FUNC) sig_nick_changed);
837         signal_add("send text", (SIGNAL_FUNC) event_text);
838         signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
839         signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
840         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
841 }
842
843 void chat_completion_deinit(void)
844 {
845         while (global_lastmsgs != NULL)
846                 last_msg_destroy(&global_lastmsgs, global_lastmsgs->data);
847
848         signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word);
849         signal_remove("complete command msg", (SIGNAL_FUNC) sig_complete_msg);
850         signal_remove("complete command connect", (SIGNAL_FUNC) sig_complete_connect);
851         signal_remove("complete command server", (SIGNAL_FUNC) sig_complete_connect);
852         signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
853         signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
854         signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public);
855         signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private);
856         signal_remove("nicklist remove", (SIGNAL_FUNC) sig_nick_removed);
857         signal_remove("nicklist changed", (SIGNAL_FUNC) sig_nick_changed);
858         signal_remove("send text", (SIGNAL_FUNC) event_text);
859         signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
860         signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
861         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
862 }