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