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