Added SILC Thread Queue API
[crypto.git] / apps / irssi / src / core / commands.c
1 /*
2  commands.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 "special-vars.h"
26 #include "window-item-def.h"
27
28 #include "servers.h"
29 #include "channels.h"
30
31 #include "lib-config/iconfig.h"
32 #include "settings.h"
33
34 GSList *commands;
35 char *current_command;
36
37 static int signal_default_command;
38
39 static GSList *alias_runstack;
40
41 COMMAND_REC *command_find(const char *cmd)
42 {
43         GSList *tmp;
44
45         g_return_val_if_fail(cmd != NULL, NULL);
46
47         for (tmp = commands; tmp != NULL; tmp = tmp->next) {
48                 COMMAND_REC *rec = tmp->data;
49
50                 if (g_strcasecmp(rec->cmd, cmd) == 0)
51                         return rec;
52         }
53
54         return NULL;
55 }
56
57 static COMMAND_MODULE_REC *command_module_find(COMMAND_REC *rec,
58                                                const char *module)
59 {
60         GSList *tmp;
61
62         g_return_val_if_fail(rec != NULL, NULL);
63         g_return_val_if_fail(module != NULL, NULL);
64
65         for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) {
66                 COMMAND_MODULE_REC *rec = tmp->data;
67
68                 if (g_strcasecmp(rec->name, module) == 0)
69                         return rec;
70         }
71
72         return NULL;
73 }
74
75 static COMMAND_MODULE_REC *
76 command_module_find_and_remove(COMMAND_REC *rec, SIGNAL_FUNC func)
77 {
78         GSList *tmp, *tmp2;
79
80         g_return_val_if_fail(rec != NULL, NULL);
81         g_return_val_if_fail(func != NULL, NULL);
82
83         for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) {
84                 COMMAND_MODULE_REC *rec = tmp->data;
85
86                 for (tmp2 = rec->callbacks; tmp2 != NULL; tmp2 = tmp2->next) {
87                         COMMAND_CALLBACK_REC *cb = tmp2->data;
88
89                         if (cb->func == func) {
90                                 rec->callbacks =
91                                         g_slist_remove(rec->callbacks, cb);
92                                 g_free(cb);
93                                 return rec;
94                         }
95                 }
96         }
97
98         return NULL;
99 }
100
101 int command_have_sub(const char *command)
102 {
103         GSList *tmp;
104         int len;
105
106         g_return_val_if_fail(command != NULL, FALSE);
107
108         /* find "command "s */
109         len = strlen(command);
110         for (tmp = commands; tmp != NULL; tmp = tmp->next) {
111                 COMMAND_REC *rec = tmp->data;
112
113                 if (g_strncasecmp(rec->cmd, command, len) == 0 &&
114                     rec->cmd[len] == ' ')
115                         return TRUE;
116         }
117
118         return FALSE;
119 }
120
121 static COMMAND_MODULE_REC *
122 command_module_get(COMMAND_REC *rec, const char *module, int protocol)
123 {
124         COMMAND_MODULE_REC *modrec;
125
126         g_return_val_if_fail(rec != NULL, NULL);
127
128         modrec = command_module_find(rec, module);
129         if (modrec == NULL) {
130                 modrec = g_new0(COMMAND_MODULE_REC, 1);
131                 modrec->name = g_strdup(module);
132                 modrec->protocol = -1;
133                 rec->modules = g_slist_append(rec->modules, modrec);
134         }
135
136         if (protocol != -1)
137                 modrec->protocol = protocol;
138
139         return modrec;
140 }
141
142 void command_bind_full(const char *module, int priority, const char *cmd,
143                        int protocol, const char *category, SIGNAL_FUNC func,
144                        void *user_data)
145 {
146         COMMAND_REC *rec;
147         COMMAND_MODULE_REC *modrec;
148         COMMAND_CALLBACK_REC *cb;
149         char *str;
150
151         g_return_if_fail(module != NULL);
152         g_return_if_fail(cmd != NULL);
153
154         rec = command_find(cmd);
155         if (rec == NULL) {
156                 rec = g_new0(COMMAND_REC, 1);
157                 rec->cmd = g_strdup(cmd);
158                 rec->category = category == NULL ? NULL : g_strdup(category);
159                 commands = g_slist_append(commands, rec);
160         }
161         modrec = command_module_get(rec, module, protocol);
162
163         cb = g_new0(COMMAND_CALLBACK_REC, 1);
164         cb->func = func;
165         cb->user_data = user_data;
166         modrec->callbacks = g_slist_append(modrec->callbacks, cb);
167
168         if (func != NULL) {
169                 str = g_strconcat("command ", cmd, NULL);
170                 signal_add_full(module, priority, str, func, user_data);
171                 g_free(str);
172         }
173
174         signal_emit("commandlist new", 1, rec);
175 }
176
177 static void command_free(COMMAND_REC *rec)
178 {
179         commands = g_slist_remove(commands, rec);
180         signal_emit("commandlist remove", 1, rec);
181
182         g_free_not_null(rec->category);
183         g_strfreev(rec->options);
184         g_free(rec->cmd);
185         g_free(rec);
186 }
187
188 static void command_module_free(COMMAND_MODULE_REC *modrec, COMMAND_REC *rec)
189 {
190         rec->modules = g_slist_remove(rec->modules, modrec);
191
192         g_slist_foreach(modrec->callbacks, (GFunc) g_free, NULL);
193         g_slist_free(modrec->callbacks);
194         g_free(modrec->name);
195         g_free_not_null(modrec->options);
196         g_free(modrec);
197 }
198
199 static void command_module_destroy(COMMAND_REC *rec,
200                                    COMMAND_MODULE_REC *modrec)
201 {
202         GSList *tmp, *freelist;
203
204         command_module_free(modrec, rec);
205
206         /* command_set_options() might have added module declaration of it's
207            own without any signals .. check if they're the only ones left
208            and if so, destroy them. */
209         freelist = NULL;
210         for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) {
211                 COMMAND_MODULE_REC *rec = tmp->data;
212
213                 if (rec->callbacks == NULL)
214                         freelist = g_slist_append(freelist, rec);
215                 else {
216                         g_slist_free(freelist);
217                         freelist = NULL;
218                         break;
219                 }
220         }
221
222         g_slist_foreach(freelist, (GFunc) command_module_free, rec);
223         g_slist_free(freelist);
224
225         if (rec->modules == NULL)
226                 command_free(rec);
227 }
228
229 void command_unbind_full(const char *cmd, SIGNAL_FUNC func, void *user_data)
230 {
231         COMMAND_REC *rec;
232         COMMAND_MODULE_REC *modrec;
233         char *str;
234
235         g_return_if_fail(cmd != NULL);
236         g_return_if_fail(func != NULL);
237
238         rec = command_find(cmd);
239         if (rec != NULL) {
240                 modrec = command_module_find_and_remove(rec, func);
241                 g_return_if_fail(modrec != NULL);
242
243                 if (modrec->callbacks == NULL)
244                         command_module_destroy(rec, modrec);
245         }
246
247         str = g_strconcat("command ", cmd, NULL);
248         signal_remove_data(str, func, user_data);
249         g_free(str);
250 }
251
252 /* Expand `cmd' - returns `cmd' if not found, NULL if more than one
253    match is found */
254 static const char *command_expand(char *cmd)
255 {
256         GSList *tmp;
257         const char *match;
258         int len, multiple;
259
260         g_return_val_if_fail(cmd != NULL, NULL);
261
262         multiple = FALSE;
263         match = NULL;
264         len = strlen(cmd);
265         for (tmp = commands; tmp != NULL; tmp = tmp->next) {
266                 COMMAND_REC *rec = tmp->data;
267
268                 if (g_strncasecmp(rec->cmd, cmd, len) == 0 &&
269                     strchr(rec->cmd+len, ' ') == NULL) {
270                         if (rec->cmd[len] == '\0') {
271                                 /* full match */
272                                 return rec->cmd;
273                         }
274
275                         if (match != NULL) {
276                                 /* multiple matches, we still need to check
277                                    if there's some command left that is a
278                                    full match.. */
279                                 multiple = TRUE;
280                         }
281
282                         /* check that this is the only match */
283                         match = rec->cmd;
284                 }
285         }
286
287         if (multiple) {
288                 signal_emit("error command", 2,
289                             GINT_TO_POINTER(CMDERR_AMBIGUOUS), cmd);
290                 return NULL;
291         }
292
293         return match != NULL ? match : cmd;
294 }
295
296 void command_runsub(const char *cmd, const char *data,
297                     void *server, void *item)
298 {
299         const char *newcmd;
300         char *orig, *subcmd, *defcmd, *args;
301
302         g_return_if_fail(data != NULL);
303
304         while (*data == ' ') data++;
305
306         if (*data == '\0') {
307                 /* no subcommand given - list the subcommands */
308                 signal_emit("list subcommands", 2, cmd);
309                 return;
310         }
311
312         /* get command.. */
313         orig = subcmd = g_strdup_printf("command %s %s", cmd, data);
314         args = strchr(subcmd+8 + strlen(cmd)+1, ' ');
315         if (args != NULL) *args++ = '\0'; else args = "";
316         while (*args == ' ') args++;
317
318         /* check if this command can be expanded */
319         newcmd = command_expand(subcmd+8);
320         if (newcmd == NULL) {
321                 /* ambiguous command */
322                 g_free(orig);
323                 return;
324         }
325
326         subcmd = g_strconcat("command ", newcmd, NULL);
327
328         g_strdown(subcmd);
329         if (!signal_emit(subcmd, 3, args, server, item)) {
330                 defcmd = g_strdup_printf("default command %s", cmd);
331                 if (!signal_emit(defcmd, 3, data, server, item)) {
332                         signal_emit("error command", 2,
333                                     GINT_TO_POINTER(CMDERR_UNKNOWN), subcmd+8);
334                 }
335                 g_free(defcmd);
336         }
337
338         g_free(subcmd);
339         g_free(orig);
340 }
341
342 static GSList *optlist_find(GSList *optlist, const char *option)
343 {
344         while (optlist != NULL) {
345                 char *name = optlist->data;
346                 if (iscmdtype(*name)) name++;
347
348                 if (g_strcasecmp(name, option) == 0)
349                         return optlist;
350
351                 optlist = optlist->next;
352         }
353
354         return NULL;
355 }
356
357 int command_have_option(const char *cmd, const char *option)
358 {
359         COMMAND_REC *rec;
360         char **tmp;
361
362         g_return_val_if_fail(cmd != NULL, FALSE);
363         g_return_val_if_fail(option != NULL, FALSE);
364
365         rec = command_find(cmd);
366         g_return_val_if_fail(rec != NULL, FALSE);
367
368         if (rec->options == NULL)
369                 return FALSE;
370
371         for (tmp = rec->options; *tmp != NULL; tmp++) {
372                 char *name = iscmdtype(**tmp) ? (*tmp)+1 : *tmp;
373
374                 if (g_strcasecmp(name, option) == 0)
375                         return TRUE;
376         }
377
378         return FALSE;
379 }
380
381 static void command_calc_options(COMMAND_REC *rec, const char *options)
382 {
383         char **optlist, **tmp, *name, *str;
384         GSList *list, *oldopt;
385
386         optlist = g_strsplit(options, " ", -1);
387
388         if (rec->options == NULL) {
389                 /* first call - use specified args directly */
390                 rec->options = optlist;
391                 return;
392         }
393
394         /* save old options to linked list */
395         list = NULL;
396         for (tmp = rec->options; *tmp != NULL; tmp++)
397                 list = g_slist_append(list, g_strdup(*tmp));
398         g_strfreev(rec->options);
399
400         /* merge the options */
401         for (tmp = optlist; *tmp != NULL; tmp++) {
402                 name = iscmdtype(**tmp) ? (*tmp)+1 : *tmp;
403
404                 oldopt = optlist_find(list, name);
405                 if (oldopt != NULL) {
406                         /* already specified - overwrite old defination */
407                         g_free(oldopt->data);
408                         oldopt->data = g_strdup(*tmp);
409                 } else {
410                         /* new option, append to list */
411                         list = g_slist_append(list, g_strdup(*tmp));
412                 }
413         }
414         g_strfreev(optlist);
415
416         /* linked list -> string[] */
417         str = gslist_to_string(list, " ");
418         rec->options = g_strsplit(str, " ", -1);
419         g_free(str);
420
421         g_slist_foreach(list, (GFunc) g_free, NULL);
422         g_slist_free(list);
423 }
424
425 /* recalculate options to command from options in all modules */
426 static void command_update_options(COMMAND_REC *rec)
427 {
428         GSList *tmp;
429
430         g_strfreev(rec->options);
431         rec->options = NULL;
432
433         for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) {
434                 COMMAND_MODULE_REC *modrec = tmp->data;
435
436                 if (modrec->options != NULL)
437                         command_calc_options(rec, modrec->options);
438         }
439 }
440
441 void command_set_options_module(const char *module,
442                                 const char *cmd, const char *options)
443 {
444         COMMAND_REC *rec;
445         COMMAND_MODULE_REC *modrec;
446         int reload;
447
448         g_return_if_fail(module != NULL);
449         g_return_if_fail(cmd != NULL);
450         g_return_if_fail(options != NULL);
451
452         rec = command_find(cmd);
453         g_return_if_fail(rec != NULL);
454         modrec = command_module_get(rec, module, -1);
455
456         reload = modrec->options != NULL;
457         if (reload) {
458                 /* options already set for the module ..
459                    we need to recalculate everything */
460                 g_free(modrec->options);
461         }
462
463         modrec->options = g_strdup(options);
464
465         if (reload)
466                 command_update_options(rec);
467         else
468                 command_calc_options(rec, options);
469 }
470
471 char *cmd_get_param(char **data)
472 {
473         char *pos;
474
475         g_return_val_if_fail(data != NULL, NULL);
476         g_return_val_if_fail(*data != NULL, NULL);
477
478         while (**data == ' ') (*data)++;
479         pos = *data;
480
481         while (**data != '\0' && **data != ' ') (*data)++;
482         if (**data == ' ') *(*data)++ = '\0';
483
484         return pos;
485 }
486
487 char *cmd_get_quoted_param(char **data)
488 {
489         char *pos, quote;
490
491         g_return_val_if_fail(data != NULL, NULL);
492         g_return_val_if_fail(*data != NULL, NULL);
493
494         while (**data == ' ') (*data)++;
495         if (**data != '\'' && **data != '"')
496                 return cmd_get_param(data);
497
498         quote = **data; (*data)++;
499
500         pos = *data;
501         while (**data != '\0' && (**data != quote ||
502                                   ((*data)[1] != ' ' && (*data)[1] != '\0'))) {
503                 if (**data == '\\' && (*data)[1] != '\0')
504                         g_memmove(*data, (*data)+1, strlen(*data));
505                 (*data)++;
506         }
507
508         if (**data == quote) {
509                 *(*data)++ = '\0';
510                 if (**data == ' ')
511                         (*data)++;
512         }
513
514         return pos;
515 }
516
517 /* Find specified option from list of options - the `option' might be
518    shortened version of the full command. Returns index where the
519    option was found, -1 if not found or -2 if there was multiple matches. */
520 static int option_find(char **array, const char *option)
521 {
522         char **tmp;
523         int index, found, len, multiple;
524
525         g_return_val_if_fail(array != NULL, -1);
526         g_return_val_if_fail(option != NULL, -1);
527
528         len = strlen(option);
529
530         found = -1; index = 0; multiple = FALSE;
531         for (tmp = array; *tmp != NULL; tmp++, index++) {
532                 const char *text = *tmp + iscmdtype(**tmp);
533
534                 if (g_strncasecmp(text, option, len) == 0) {
535                         if (text[len] == '\0') {
536                                 /* full match */
537                                 return index;
538                         }
539
540                         if (found != -1) {
541                                 /* multiple matches - we still need to check
542                                    if there's a full match left.. */
543                                 multiple = TRUE;
544                         }
545
546                         /* partial match, check that it's the only one */
547                         found = index;
548                 }
549         }
550
551         if (multiple)
552                 return -2;
553
554         return found;
555 }
556
557 static int get_cmd_options(char **data, int ignore_unknown,
558                            const char *cmd, GHashTable *options)
559 {
560         COMMAND_REC *rec;
561         char *option, *arg, **optlist;
562         int pos;
563
564         /* get option definations */
565         rec = cmd == NULL ? NULL : command_find(cmd);
566         optlist = rec == NULL ? NULL : rec->options;
567
568         option = NULL; pos = -1;
569         for (;;) {
570                 if (**data == '-') {
571                         if (option != NULL && *optlist[pos] == '+') {
572                                 /* required argument missing! */
573                                 *data = optlist[pos] + 1;
574                                 return CMDERR_OPTION_ARG_MISSING;
575                         }
576
577                         (*data)++;
578                         if (**data == '-' && (*data)[1] == ' ') {
579                                 /* -- option means end of options even
580                                    if next word starts with - */
581                                 (*data)++;
582                                 while (**data == ' ') (*data)++;
583                                 break;
584                         }
585
586                         if (**data == '\0')
587                                 option = "-";
588                         else if (**data != ' ')
589                                 option = cmd_get_param(data);
590                         else {
591                                 option = "-";
592                                 (*data)++;
593                         }
594
595                         /* check if this option can have argument */
596                         pos = optlist == NULL ? -1 :
597                                 option_find(optlist, option);
598
599                         if (pos == -1 && optlist != NULL &&
600                             is_numeric(option, '\0')) {
601                                 /* check if we want -<number> option */
602                                 pos = option_find(optlist, "#");
603                                 if (pos != -1) {
604                                         g_hash_table_insert(options, "#",
605                                                             option);
606                                         pos = -3;
607                                 }
608                         }
609
610                         if (pos == -1 && !ignore_unknown) {
611                                 /* unknown option! */
612                                 *data = option;
613                                 return CMDERR_OPTION_UNKNOWN;
614                         }
615                         if (pos == -2 && !ignore_unknown) {
616                                 /* multiple matches */
617                                 *data = option;
618                                 return CMDERR_OPTION_AMBIGUOUS;
619                         }
620                         if (pos >= 0) {
621                                 /* if we used a shortcut of parameter, put
622                                    the whole parameter name in options table */
623                                 option = optlist[pos] +
624                                         iscmdtype(*optlist[pos]);
625                         }
626                         if (options != NULL && pos != -3)
627                                 g_hash_table_insert(options, option, "");
628
629                         if (pos < 0 || !iscmdtype(*optlist[pos]) ||
630                             *optlist[pos] == '!')
631                                 option = NULL;
632
633                         while (**data == ' ') (*data)++;
634                         continue;
635                 }
636
637                 if (option == NULL)
638                         break;
639
640                 if (*optlist[pos] == '@' && !i_isdigit(**data))
641                         break; /* expected a numeric argument */
642
643                 /* save the argument */
644                 arg = cmd_get_quoted_param(data);
645                 if (options != NULL) {
646                         g_hash_table_remove(options, option);
647                         g_hash_table_insert(options, option, arg);
648                 }
649                 option = NULL;
650
651                 while (**data == ' ') (*data)++;
652         }
653
654         return 0;
655 }
656
657 typedef struct {
658         char *data;
659         GHashTable *options;
660 } CMD_TEMP_REC;
661
662 static const char *
663 get_optional_channel(WI_ITEM_REC *active_item, char **data, int require_name)
664 {
665         CHANNEL_REC *chanrec;
666         const char *ret;
667         char *tmp, *origtmp, *channel;
668
669         if (active_item == NULL) {
670                 /* no active channel in window, channel required */
671                 return cmd_get_param(data);
672         }
673
674         origtmp = tmp = g_strdup(*data);
675         channel = cmd_get_param(&tmp);
676
677         if (strcmp(channel, "*") == 0 && !require_name) {
678                 /* "*" means active channel */
679                 cmd_get_param(data);
680                 ret = window_item_get_target(active_item);
681         } else if (!server_ischannel(active_item->server, channel)) {
682                 /* we don't have channel parameter - use active channel */
683                 ret = window_item_get_target(active_item);
684         } else {
685                 /* Find the channel first and use it's name if found.
686                    This allows automatic !channel -> !XXXXXchannel replaces. */
687                 channel = cmd_get_param(data);
688
689                 chanrec = channel_find(active_item->server, channel);
690                 ret = chanrec == NULL ? channel : chanrec->name;
691         }
692
693         g_free(origtmp);
694         return ret;
695 }
696
697 int cmd_get_params(const char *data, gpointer *free_me, int count, ...)
698 {
699         WI_ITEM_REC *item;
700         CMD_TEMP_REC *rec;
701         GHashTable **opthash;
702         char **str, *arg, *datad;
703         va_list args;
704         int cnt, error, ignore_unknown, require_name;
705
706         g_return_val_if_fail(data != NULL, FALSE);
707
708         va_start(args, count);
709
710         rec = g_new0(CMD_TEMP_REC, 1);
711         rec->data = g_strdup(data);
712         *free_me = rec;
713
714         datad = rec->data;
715         error = FALSE;
716
717         item = (count & PARAM_FLAG_OPTCHAN) == 0 ? NULL:
718                 (WI_ITEM_REC *) va_arg(args, WI_ITEM_REC *);
719
720         if (count & PARAM_FLAG_OPTIONS) {
721                 arg = (char *) va_arg(args, char *);
722                 opthash = (GHashTable **) va_arg(args, GHashTable **);
723
724                 rec->options = *opthash =
725                         g_hash_table_new((GHashFunc) g_istr_hash,
726                                          (GCompareFunc) g_istr_equal);
727
728                 ignore_unknown = count & PARAM_FLAG_UNKNOWN_OPTIONS;
729                 error = get_cmd_options(&datad, ignore_unknown,
730                                         arg, *opthash);
731         }
732
733         if (!error) {
734                 /* and now handle the string */
735                 cnt = PARAM_WITHOUT_FLAGS(count);
736                 if (count & PARAM_FLAG_OPTCHAN) {
737                         /* optional channel as first parameter */
738                         require_name = (count & PARAM_FLAG_OPTCHAN_NAME) ==
739                                 PARAM_FLAG_OPTCHAN_NAME;
740                         arg = (char *) get_optional_channel(item, &datad, require_name);
741
742                         str = (char **) va_arg(args, char **);
743                         if (str != NULL) *str = arg;
744                         cnt--;
745                 }
746
747                 while (cnt-- > 0) {
748                         if (cnt == 0 && count & PARAM_FLAG_GETREST) {
749                                 /* get rest */
750                                 arg = datad;
751                         } else {
752                                 arg = (count & PARAM_FLAG_NOQUOTES) ?
753                                         cmd_get_param(&datad) :
754                                         cmd_get_quoted_param(&datad);
755                         }
756
757                         str = (char **) va_arg(args, char **);
758                         if (str != NULL) *str = arg;
759                 }
760         }
761         va_end(args);
762
763         if (error) {
764                 signal_emit("error command", 2, GINT_TO_POINTER(error), datad);
765                 signal_stop();
766
767                 cmd_params_free(rec);
768                 *free_me = NULL;
769         }
770
771         return !error;
772 }
773
774 void cmd_params_free(void *free_me)
775 {
776         CMD_TEMP_REC *rec = free_me;
777
778         if (rec->options != NULL) g_hash_table_destroy(rec->options);
779         g_free(rec->data);
780         g_free(rec);
781 }
782
783 static void command_module_unbind_all(COMMAND_REC *rec,
784                                       COMMAND_MODULE_REC *modrec)
785 {
786         GSList *tmp, *next;
787
788         for (tmp = modrec->callbacks; tmp != NULL; tmp = next) {
789                 COMMAND_CALLBACK_REC *cb = tmp->data;
790                 next = tmp->next;
791
792                 command_unbind_full(rec->cmd, cb->func, cb->user_data);
793         }
794
795         if (g_slist_find(commands, rec) != NULL) {
796                 /* this module might have removed some options
797                    from command, update them. */
798                 command_update_options(rec);
799         }
800 }
801
802 void commands_remove_module(const char *module)
803 {
804         GSList *tmp, *next, *modlist;
805
806         g_return_if_fail(module != NULL);
807
808         for (tmp = commands; tmp != NULL; tmp = next) {
809                 COMMAND_REC *rec = tmp->data;
810
811                 next = tmp->next;
812                 modlist = gslist_find_string(rec->modules, module);
813                 if (modlist != NULL)
814                         command_module_unbind_all(rec, modlist->data);
815         }
816 }
817
818 static int cmd_protocol_match(COMMAND_REC *cmd, SERVER_REC *server)
819 {
820         GSList *tmp;
821
822         for (tmp = cmd->modules; tmp != NULL; tmp = tmp->next) {
823                 COMMAND_MODULE_REC *rec = tmp->data;
824
825                 if (rec->protocol == -1) {
826                         /* at least one module accepts the command
827                            without specific protocol */
828                         return 1;
829                 }
830
831                 if (server != NULL && rec->protocol == server->chat_type) {
832                         /* matching protocol found */
833                         return 1;
834                 }
835         }
836
837         return 0;
838 }
839
840 #define alias_runstack_push(alias) \
841         alias_runstack = g_slist_append(alias_runstack, alias)
842
843 #define alias_runstack_pop(alias) \
844         alias_runstack = g_slist_remove(alias_runstack, alias)
845
846 #define alias_runstack_find(alias) \
847         (gslist_find_icase_string(alias_runstack, alias) != NULL)
848
849 static void parse_command(const char *command, int expand_aliases,
850                           SERVER_REC *server, void *item)
851 {
852         COMMAND_REC *rec;
853         const char *alias, *newcmd;
854         char *cmd, *orig, *args, *oldcmd;
855
856         g_return_if_fail(command != NULL);
857
858         cmd = orig = g_strconcat("command ", command, NULL);
859         args = strchr(cmd+8, ' ');
860         if (args != NULL) *args++ = '\0'; else args = "";
861
862         /* check if there's an alias for command. Don't allow
863            recursive aliases */
864         alias = !expand_aliases || alias_runstack_find(cmd+8) ? NULL :
865                 alias_find(cmd+8);
866         if (alias != NULL) {
867                 alias_runstack_push(cmd+8);
868                 eval_special_string(alias, args, server, item);
869                 alias_runstack_pop(cmd+8);
870                 g_free(orig);
871                 return;
872         }
873
874         /* check if this command can be expanded */
875         newcmd = command_expand(cmd+8);
876         if (newcmd == NULL) {
877                 /* ambiguous command */
878                 g_free(orig);
879                 return;
880         }
881
882         rec = command_find(newcmd);
883         if (rec != NULL && !cmd_protocol_match(rec, server)) {
884                 g_free(orig);
885
886                 signal_emit("error command", 2,
887                             GINT_TO_POINTER(server == NULL ?
888                                             CMDERR_NOT_CONNECTED :
889                                             CMDERR_ILLEGAL_PROTO));
890                 return;
891         }
892
893         cmd = g_strconcat("command ", newcmd, NULL);
894         g_strdown(cmd);
895
896         oldcmd = current_command;
897         current_command = cmd+8;
898         if (server != NULL) server_ref(server);
899         if (!signal_emit(cmd, 3, args, server, item)) {
900                 signal_emit_id(signal_default_command, 3,
901                                command, server, item);
902         }
903         if (server != NULL) {
904                 if (server->connection_lost)
905                         server_disconnect(server);
906                 server_unref(server);
907         }
908         current_command = oldcmd;
909
910         g_free(cmd);
911         g_free(orig);
912 }
913
914 static void event_command(const char *line, SERVER_REC *server, void *item)
915 {
916         char *cmdchar;
917         int expand_aliases = TRUE;
918
919         g_return_if_fail(line != NULL);
920
921         cmdchar = *line == '\0' ? NULL :
922                 strchr(settings_get_str("cmdchars"), *line);
923         if (cmdchar != NULL && line[1] == ' ') {
924                 /* "/ text" = same as sending "text" to active channel. */
925                 line += 2;
926                 cmdchar = NULL;
927         }
928         if (cmdchar == NULL) {
929                 /* non-command - let someone else handle this */
930                 signal_emit("send text", 3, line, server, item);
931                 return;
932         }
933
934         /* same cmdchar twice ignores aliases ignores aliases */
935         line++;
936         if (*line == *cmdchar) {
937                 line++;
938                 expand_aliases = FALSE;
939         }
940
941         /* ^command hides the output - we'll do this at fe-common but
942            we have to skip the ^ char here.. */
943         if (*line == '^') line++;
944
945         parse_command(line, expand_aliases, server, item);
946 }
947
948 static int eval_recursion_depth=0;
949 /* SYNTAX: EVAL <command(s)> */
950 static void cmd_eval(const char *data, SERVER_REC *server, void *item)
951 {
952         g_return_if_fail(data != NULL);
953         if (eval_recursion_depth > 100) 
954                 cmd_return_error(CMDERR_EVAL_MAX_RECURSE);
955
956  
957         eval_recursion_depth++;
958         eval_special_string(data, "", server, item);
959         eval_recursion_depth--;
960 }
961
962 /* SYNTAX: CD <directory> */
963 static void cmd_cd(const char *data)
964 {
965         char *str;
966
967         g_return_if_fail(data != NULL);
968         if (*data == '\0') return;
969
970         str = convert_home(data);
971         chdir(str);
972         g_free(str);
973 }
974
975 void commands_init(void)
976 {
977         commands = NULL;
978         current_command = NULL;
979         alias_runstack = NULL;
980
981         signal_default_command = signal_get_uniq_id("default command");
982
983         settings_add_str("misc", "cmdchars", "/");
984         signal_add("send command", (SIGNAL_FUNC) event_command);
985
986         command_bind("eval", NULL, (SIGNAL_FUNC) cmd_eval);
987         command_bind("cd", NULL, (SIGNAL_FUNC) cmd_cd);
988 }
989
990 void commands_deinit(void)
991 {
992         g_free_not_null(current_command);
993
994         signal_remove("send command", (SIGNAL_FUNC) event_command);
995
996         command_unbind("eval", (SIGNAL_FUNC) cmd_eval);
997         command_unbind("cd", (SIGNAL_FUNC) cmd_cd);
998 }