diff options
Diffstat (limited to 'src/core/commands.c')
-rw-r--r-- | src/core/commands.c | 1021 |
1 files changed, 1021 insertions, 0 deletions
diff --git a/src/core/commands.c b/src/core/commands.c new file mode 100644 index 0000000..deb5fe9 --- /dev/null +++ b/src/core/commands.c @@ -0,0 +1,1021 @@ +/* + commands.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/core/signals.h> +#include <irssi/src/core/commands.h> +#include <irssi/src/core/misc.h> +#include <irssi/src/core/special-vars.h> +#include <irssi/src/core/window-item-def.h> + +#include <irssi/src/core/servers.h> +#include <irssi/src/core/channels.h> + +#include <irssi/src/lib-config/iconfig.h> +#include <irssi/src/core/settings.h> + +GSList *commands; +char *current_command; + +static int signal_default_command; + +static GSList *alias_runstack; + +COMMAND_REC *command_find(const char *cmd) +{ + GSList *tmp; + + g_return_val_if_fail(cmd != NULL, NULL); + + for (tmp = commands; tmp != NULL; tmp = tmp->next) { + COMMAND_REC *rec = tmp->data; + + if (g_ascii_strcasecmp(rec->cmd, cmd) == 0) + return rec; + } + + return NULL; +} + +static COMMAND_MODULE_REC *command_module_find(COMMAND_REC *rec, + const char *module) +{ + GSList *tmp; + + g_return_val_if_fail(rec != NULL, NULL); + g_return_val_if_fail(module != NULL, NULL); + + for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) { + COMMAND_MODULE_REC *rec = tmp->data; + + if (g_ascii_strcasecmp(rec->name, module) == 0) + return rec; + } + + return NULL; +} + +static COMMAND_MODULE_REC * +command_module_find_and_remove(COMMAND_REC *rec, SIGNAL_FUNC func) +{ + GSList *tmp, *tmp2; + + g_return_val_if_fail(rec != NULL, NULL); + g_return_val_if_fail(func != NULL, NULL); + + for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) { + COMMAND_MODULE_REC *rec = tmp->data; + + for (tmp2 = rec->callbacks; tmp2 != NULL; tmp2 = tmp2->next) { + COMMAND_CALLBACK_REC *cb = tmp2->data; + + if (cb->func == func) { + rec->callbacks = + g_slist_remove(rec->callbacks, cb); + g_free(cb); + return rec; + } + } + } + + return NULL; +} + +int command_have_sub(const char *command) +{ + GSList *tmp; + int len; + + g_return_val_if_fail(command != NULL, FALSE); + + /* find "command "s */ + len = strlen(command); + for (tmp = commands; tmp != NULL; tmp = tmp->next) { + COMMAND_REC *rec = tmp->data; + + if (g_ascii_strncasecmp(rec->cmd, command, len) == 0 && + rec->cmd[len] == ' ') + return TRUE; + } + + return FALSE; +} + +static COMMAND_MODULE_REC * +command_module_get(COMMAND_REC *rec, const char *module, int protocol) +{ + COMMAND_MODULE_REC *modrec; + + g_return_val_if_fail(rec != NULL, NULL); + + modrec = command_module_find(rec, module); + if (modrec == NULL) { + modrec = g_new0(COMMAND_MODULE_REC, 1); + modrec->name = g_strdup(module); + modrec->protocol = -1; + rec->modules = g_slist_append(rec->modules, modrec); + } + + if (protocol != -1) + modrec->protocol = protocol; + + return modrec; +} + +void command_bind_full(const char *module, int priority, const char *cmd, + int protocol, const char *category, SIGNAL_FUNC func, + void *user_data) +{ + COMMAND_REC *rec; + COMMAND_MODULE_REC *modrec; + COMMAND_CALLBACK_REC *cb; + char *str; + + g_return_if_fail(module != NULL); + g_return_if_fail(cmd != NULL); + + rec = command_find(cmd); + if (rec == NULL) { + rec = g_new0(COMMAND_REC, 1); + rec->cmd = g_strdup(cmd); + rec->category = category == NULL ? NULL : g_strdup(category); + commands = g_slist_append(commands, rec); + } + modrec = command_module_get(rec, module, protocol); + + cb = g_new0(COMMAND_CALLBACK_REC, 1); + cb->func = func; + cb->user_data = user_data; + modrec->callbacks = g_slist_append(modrec->callbacks, cb); + + if (func != NULL) { + str = g_strconcat("command ", cmd, NULL); + signal_add_full(module, priority, str, func, user_data); + g_free(str); + } + + signal_emit("commandlist new", 1, rec); +} + +static void command_free(COMMAND_REC *rec) +{ + commands = g_slist_remove(commands, rec); + signal_emit("commandlist remove", 1, rec); + + g_free_not_null(rec->category); + g_strfreev(rec->options); + g_free(rec->cmd); + g_free(rec); +} + +static void command_module_free(COMMAND_MODULE_REC *modrec, COMMAND_REC *rec) +{ + rec->modules = g_slist_remove(rec->modules, modrec); + + g_slist_foreach(modrec->callbacks, (GFunc) g_free, NULL); + g_slist_free(modrec->callbacks); + g_free(modrec->name); + g_free_not_null(modrec->options); + g_free(modrec); +} + +static void command_module_destroy(COMMAND_REC *rec, + COMMAND_MODULE_REC *modrec) +{ + GSList *tmp, *freelist; + + command_module_free(modrec, rec); + + /* command_set_options() might have added module declaration of it's + own without any signals .. check if they're the only ones left + and if so, destroy them. */ + freelist = NULL; + for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) { + COMMAND_MODULE_REC *rec = tmp->data; + + if (rec->callbacks == NULL) + freelist = g_slist_append(freelist, rec); + else { + g_slist_free(freelist); + freelist = NULL; + break; + } + } + + g_slist_foreach(freelist, (GFunc) command_module_free, rec); + g_slist_free(freelist); + + if (rec->modules == NULL) + command_free(rec); +} + +void command_unbind_full(const char *cmd, SIGNAL_FUNC func, void *user_data) +{ + COMMAND_REC *rec; + COMMAND_MODULE_REC *modrec; + char *str; + + g_return_if_fail(cmd != NULL); + g_return_if_fail(func != NULL); + + rec = command_find(cmd); + if (rec != NULL) { + modrec = command_module_find_and_remove(rec, func); + g_return_if_fail(modrec != NULL); + + if (modrec->callbacks == NULL) + command_module_destroy(rec, modrec); + } + + str = g_strconcat("command ", cmd, NULL); + signal_remove_data(str, func, user_data); + g_free(str); +} + +/* Expand `cmd' - returns `cmd' if not found, NULL if more than one + match is found */ +static const char *command_expand(char *cmd) +{ + GSList *tmp; + const char *match; + int len, multiple; + + g_return_val_if_fail(cmd != NULL, NULL); + + multiple = FALSE; + match = NULL; + len = strlen(cmd); + for (tmp = commands; tmp != NULL; tmp = tmp->next) { + COMMAND_REC *rec = tmp->data; + + if (g_ascii_strncasecmp(rec->cmd, cmd, len) == 0 && + strchr(rec->cmd+len, ' ') == NULL) { + if (rec->cmd[len] == '\0') { + /* full match */ + return rec->cmd; + } + + if (match != NULL) { + /* multiple matches, we still need to check + if there's some command left that is a + full match.. */ + multiple = TRUE; + } + + /* check that this is the only match */ + match = rec->cmd; + } + } + + if (multiple) { + signal_emit("error command", 2, + GINT_TO_POINTER(CMDERR_AMBIGUOUS), cmd); + return NULL; + } + + return match != NULL ? match : cmd; +} + +void command_runsub(const char *cmd, const char *data, + void *server, void *item) +{ + const char *newcmd; + char *orig, *subcmd, *defcmd, *args; + + g_return_if_fail(data != NULL); + + while (*data == ' ') data++; + + if (*data == '\0') { + /* no subcommand given - list the subcommands */ + signal_emit("list subcommands", 1, cmd); + return; + } + + /* get command.. */ + orig = subcmd = g_strdup_printf("command %s %s", cmd, data); + args = strchr(subcmd+8 + strlen(cmd)+1, ' '); + if (args != NULL) *args++ = '\0'; else args = ""; + while (*args == ' ') args++; + + /* check if this command can be expanded */ + newcmd = command_expand(subcmd+8); + if (newcmd == NULL) { + /* ambiguous command */ + g_free(orig); + return; + } + + subcmd = g_strconcat("command ", newcmd, NULL); + + ascii_strdown(subcmd); + if (!signal_emit(subcmd, 3, args, server, item)) { + defcmd = g_strdup_printf("default command %s", cmd); + if (!signal_emit(defcmd, 3, data, server, item)) { + signal_emit("error command", 2, + GINT_TO_POINTER(CMDERR_UNKNOWN), subcmd+8); + } + g_free(defcmd); + } + + g_free(subcmd); + g_free(orig); +} + +static char *optname(char *option) +{ + char *opt = option; + if (*opt == '~') + opt++; + if (iscmdtype(*opt)) + opt++; + return opt; +} + +static gboolean optflag(char *option, char *flag) +{ + if (*option == '~') + return optflag(option + 1, flag); + + return (strchr(flag, *option) != NULL) || (!iscmdtype(*option) && strchr(flag, ' ')); +} + +static GSList *optlist_find(GSList *optlist, const char *option) +{ + while (optlist != NULL) { + char *name = optname(optlist->data); + + if (g_ascii_strcasecmp(name, option) == 0) + return optlist; + + optlist = optlist->next; + } + + return NULL; +} + +int command_have_option(const char *cmd, const char *option) +{ + COMMAND_REC *rec; + char **tmp; + + g_return_val_if_fail(cmd != NULL, FALSE); + g_return_val_if_fail(option != NULL, FALSE); + + rec = command_find(cmd); + g_return_val_if_fail(rec != NULL, FALSE); + + if (rec->options == NULL) + return FALSE; + + for (tmp = rec->options; *tmp != NULL; tmp++) { + char *name = optname(*tmp); + + if (g_ascii_strcasecmp(name, option) == 0) + return TRUE; + } + + return FALSE; +} + +static void command_calc_options(COMMAND_REC *rec, const char *options) +{ + char **optlist, **tmp, *name, *str; + GSList *list, *oldopt; + + optlist = g_strsplit(options, " ", -1); + + if (rec->options == NULL) { + /* first call - use specified args directly */ + rec->options = optlist; + return; + } + + /* save old options to linked list */ + list = NULL; + for (tmp = rec->options; *tmp != NULL; tmp++) + list = g_slist_append(list, g_strdup(*tmp)); + g_strfreev(rec->options); + + /* merge the options */ + for (tmp = optlist; *tmp != NULL; tmp++) { + name = optname(*tmp); + + oldopt = optlist_find(list, name); + if (oldopt != NULL) { + /* already specified - overwrite old definition */ + g_free(oldopt->data); + oldopt->data = g_strdup(*tmp); + } else { + /* new option, append to list */ + list = g_slist_append(list, g_strdup(*tmp)); + } + } + g_strfreev(optlist); + + /* linked list -> string[] */ + str = i_slist_to_string(list, " "); + rec->options = g_strsplit(str, " ", -1); + g_free(str); + + g_slist_foreach(list, (GFunc) g_free, NULL); + g_slist_free(list); +} + +/* recalculate options to command from options in all modules */ +static void command_update_options(COMMAND_REC *rec) +{ + GSList *tmp; + + g_strfreev(rec->options); + rec->options = NULL; + + for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) { + COMMAND_MODULE_REC *modrec = tmp->data; + + if (modrec->options != NULL) + command_calc_options(rec, modrec->options); + } +} + +void command_set_options_module(const char *module, + const char *cmd, const char *options) +{ + COMMAND_REC *rec; + COMMAND_MODULE_REC *modrec; + int reload; + + g_return_if_fail(module != NULL); + g_return_if_fail(cmd != NULL); + g_return_if_fail(options != NULL); + + rec = command_find(cmd); + g_return_if_fail(rec != NULL); + modrec = command_module_get(rec, module, -1); + + reload = modrec->options != NULL; + if (reload) { + /* options already set for the module .. + we need to recalculate everything */ + g_free(modrec->options); + } + + modrec->options = g_strdup(options); + + if (reload) + command_update_options(rec); + else + command_calc_options(rec, options); +} + +char *cmd_get_param(char **data) +{ + char *pos; + + g_return_val_if_fail(data != NULL, NULL); + g_return_val_if_fail(*data != NULL, NULL); + + while (**data == ' ') (*data)++; + pos = *data; + + while (**data != '\0' && **data != ' ') (*data)++; + if (**data == ' ') *(*data)++ = '\0'; + + return pos; +} + +char *cmd_get_quoted_param(char **data) +{ + char *pos, quote; + + g_return_val_if_fail(data != NULL, NULL); + g_return_val_if_fail(*data != NULL, NULL); + + while (**data == ' ') (*data)++; + if (**data != '\'' && **data != '"') + return cmd_get_param(data); + + quote = **data; (*data)++; + + pos = *data; + while (**data != '\0' && (**data != quote || + ((*data)[1] != ' ' && (*data)[1] != '\0'))) { + if (**data == '\\' && (*data)[1] != '\0') + memmove(*data, (*data)+1, strlen(*data)); + (*data)++; + } + + if (**data == quote) { + *(*data)++ = '\0'; + if (**data == ' ') + (*data)++; + } + + return pos; +} + +/* Find specified option from list of options - the `option' might be + shortened version of the full command. Returns index where the + option was found, -1 if not found or -2 if there was multiple matches. */ +static int option_find(char **array, const char *option) +{ + char **tmp; + int index, found, len, multiple; + + g_return_val_if_fail(array != NULL, -1); + g_return_val_if_fail(option != NULL, -1); + + len = strlen(option); + + found = -1; index = 0; multiple = FALSE; + for (tmp = array; *tmp != NULL; tmp++, index++) { + const char *text = optname(*tmp); + + if (g_ascii_strncasecmp(text, option, len) == 0) { + if (text[len] == '\0') { + /* full match */ + return index; + } + + if (found != -1) { + /* multiple matches - we still need to check + if there's a full match left.. */ + multiple = TRUE; + } + + /* partial match, check that it's the only one */ + found = index; + } + } + + if (multiple) + return -2; + + return found; +} + +static int get_cmd_options(char **data, int ignore_unknown, + const char *cmd, GHashTable *options) +{ + COMMAND_REC *rec; + char *option, *arg, **optlist; + int pos; + + /* get option definitions */ + rec = cmd == NULL ? NULL : command_find(cmd); + optlist = rec == NULL ? NULL : rec->options; + + option = NULL; pos = -1; + for (;;) { + if (**data == '\0' || **data == '-') { + if (option != NULL && optflag(optlist[pos], "+")) { + /* required argument missing! */ + *data = optname(optlist[pos]); + return CMDERR_OPTION_ARG_MISSING; + } + } + if (**data == '-') { + (*data)++; + if (**data == '-' && (*data)[1] == ' ') { + /* -- option means end of options even + if next word starts with - */ + (*data)++; + while (**data == ' ') (*data)++; + break; + } + + if (**data == '\0') + option = "-"; + else if (**data != ' ') + option = cmd_get_param(data); + else { + option = "-"; + (*data)++; + } + + /* check if this option can have argument */ + pos = optlist == NULL ? -1 : + option_find(optlist, option); + + if (pos == -1 && optlist != NULL && + is_numeric(option, '\0')) { + /* check if we want -<number> option */ + pos = option_find(optlist, "#"); + if (pos != -1) { + g_hash_table_insert(options, "#", + option); + pos = -3; + } + } + + if (pos == -1 && !ignore_unknown) { + /* unknown option! */ + *data = option; + return CMDERR_OPTION_UNKNOWN; + } + if (pos == -2 && !ignore_unknown) { + /* multiple matches */ + *data = option; + return CMDERR_OPTION_AMBIGUOUS; + } + if (pos >= 0) { + /* if we used a shortcut of parameter, put + the whole parameter name in options table */ + option = optname(optlist[pos]); + } + if (options != NULL && pos != -3) + g_hash_table_insert(options, option, ""); + + if (pos < 0 || optflag(optlist[pos], " !")) + option = NULL; + + while (**data == ' ') (*data)++; + continue; + } + + if (option == NULL) + break; + + if (optflag(optlist[pos], "@") && !is_numeric(*data, ' ')) + break; /* expected a numeric argument */ + + /* save the argument */ + arg = cmd_get_quoted_param(data); + if (options != NULL) { + g_hash_table_remove(options, option); + g_hash_table_insert(options, option, arg); + } + option = NULL; + + while (**data == ' ') (*data)++; + } + + return 0; +} + +typedef struct { + char *data; + GHashTable *options; +} CMD_TEMP_REC; + +static const char * +get_optional_channel(WI_ITEM_REC *active_item, char **data, int require_name) +{ + CHANNEL_REC *chanrec; + const char *ret; + char *tmp, *origtmp, *channel; + + if (active_item == NULL || active_item->server == NULL) { + /* no active channel in window, channel required */ + return cmd_get_param(data); + } + + origtmp = tmp = g_strdup(*data); + channel = cmd_get_param(&tmp); + + if (g_strcmp0(channel, "*") == 0 && IS_CHANNEL(active_item) && + !require_name) { + /* "*" means active channel */ + cmd_get_param(data); + ret = window_item_get_target(active_item); + } else if (IS_CHANNEL(active_item) && + !server_ischannel(active_item->server, channel)) { + /* we don't have channel parameter - use active channel */ + ret = window_item_get_target(active_item); + } else { + /* Find the channel first and use it's name if found. + This allows automatic !channel -> !XXXXXchannel replaces. */ + channel = cmd_get_param(data); + + chanrec = channel_find(active_item->server, channel); + ret = chanrec == NULL ? channel : chanrec->name; + } + + g_free(origtmp); + return ret; +} + +int cmd_get_params(const char *data, gpointer *free_me, int count, ...) +{ + WI_ITEM_REC *item; + CMD_TEMP_REC *rec; + GHashTable **opthash; + char **str, *arg, *datad; + va_list args; + int cnt, error, ignore_unknown, require_name; + + g_return_val_if_fail(data != NULL, FALSE); + + va_start(args, count); + + rec = g_new0(CMD_TEMP_REC, 1); + rec->data = g_strdup(data); + *free_me = rec; + + datad = rec->data; + error = FALSE; + + item = (count & PARAM_FLAG_OPTCHAN) == 0 ? NULL: + (WI_ITEM_REC *) va_arg(args, WI_ITEM_REC *); + + if (count & PARAM_FLAG_OPTIONS) { + arg = (char *) va_arg(args, char *); + opthash = (GHashTable **) va_arg(args, GHashTable **); + + rec->options = *opthash = + g_hash_table_new((GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal); + + ignore_unknown = count & PARAM_FLAG_UNKNOWN_OPTIONS; + error = get_cmd_options(&datad, ignore_unknown, + arg, *opthash); + } + + if (!error) { + /* and now handle the string */ + cnt = PARAM_WITHOUT_FLAGS(count); + if (count & PARAM_FLAG_OPTCHAN) { + /* optional channel as first parameter */ + require_name = (count & PARAM_FLAG_OPTCHAN_NAME) == + PARAM_FLAG_OPTCHAN_NAME; + arg = (char *) get_optional_channel(item, &datad, require_name); + + str = (char **) va_arg(args, char **); + if (str != NULL) *str = arg; + cnt--; + } + + while (cnt-- > 0) { + if (cnt == 0 && count & PARAM_FLAG_GETREST) { + /* get rest */ + arg = datad; + + /* strip the trailing whitespace */ + if (count & PARAM_FLAG_STRIP_TRAILING_WS) { + arg = g_strchomp(arg); + } + } else { + arg = (count & PARAM_FLAG_NOQUOTES) ? + cmd_get_param(&datad) : + cmd_get_quoted_param(&datad); + } + + str = (char **) va_arg(args, char **); + if (str != NULL) *str = arg; + } + } + va_end(args); + + if (error) { + signal_emit("error command", 2, GINT_TO_POINTER(error), datad); + signal_stop(); + + cmd_params_free(rec); + *free_me = NULL; + } + + return !error; +} + +void cmd_params_free(void *free_me) +{ + CMD_TEMP_REC *rec = free_me; + + if (rec->options != NULL) g_hash_table_destroy(rec->options); + g_free(rec->data); + g_free(rec); +} + +static void command_module_unbind_all(COMMAND_REC *rec, + COMMAND_MODULE_REC *modrec) +{ + GSList *tmp, *next; + + for (tmp = modrec->callbacks; tmp != NULL; tmp = next) { + COMMAND_CALLBACK_REC *cb = tmp->data; + next = tmp->next; + + command_unbind_full(rec->cmd, cb->func, cb->user_data); + } + + if (g_slist_find(commands, rec) != NULL) { + /* this module might have removed some options + from command, update them. */ + command_update_options(rec); + } +} + +void commands_remove_module(const char *module) +{ + GSList *tmp, *next, *modlist; + + g_return_if_fail(module != NULL); + + for (tmp = commands; tmp != NULL; tmp = next) { + COMMAND_REC *rec = tmp->data; + + next = tmp->next; + modlist = i_slist_find_string(rec->modules, module); + if (modlist != NULL) + command_module_unbind_all(rec, modlist->data); + } +} + +static int cmd_protocol_match(COMMAND_REC *cmd, SERVER_REC *server) +{ + GSList *tmp; + + for (tmp = cmd->modules; tmp != NULL; tmp = tmp->next) { + COMMAND_MODULE_REC *rec = tmp->data; + + if (rec->protocol == -1) { + /* at least one module accepts the command + without specific protocol */ + return 1; + } + + if (server != NULL && rec->protocol == server->chat_type) { + /* matching protocol found */ + return 1; + } + } + + return 0; +} + +#define alias_runstack_push(alias) \ + alias_runstack = g_slist_append(alias_runstack, alias) + +#define alias_runstack_pop(alias) \ + alias_runstack = g_slist_remove(alias_runstack, alias) + +#define alias_runstack_find(alias) (i_slist_find_icase_string(alias_runstack, alias) != NULL) + +static void parse_command(const char *command, int expand_aliases, + SERVER_REC *server, void *item) +{ + COMMAND_REC *rec; + const char *alias, *newcmd; + char *cmd, *orig, *args, *oldcmd; + + g_return_if_fail(command != NULL); + + cmd = orig = g_strconcat("command ", command, NULL); + args = strchr(cmd+8, ' '); + if (args != NULL) *args++ = '\0'; else args = ""; + + /* check if there's an alias for command. Don't allow + recursive aliases */ + alias = !expand_aliases || alias_runstack_find(cmd+8) ? NULL : + alias_find(cmd+8); + if (alias != NULL) { + alias_runstack_push(cmd+8); + eval_special_string(alias, args, server, item); + alias_runstack_pop(cmd+8); + g_free(orig); + return; + } + + /* check if this command can be expanded */ + newcmd = command_expand(cmd+8); + if (newcmd == NULL) { + /* ambiguous command */ + g_free(orig); + return; + } + + rec = command_find(newcmd); + if (rec != NULL && !cmd_protocol_match(rec, server)) { + g_free(orig); + + signal_emit("error command", 1, + GINT_TO_POINTER(server == NULL ? + CMDERR_NOT_CONNECTED : + CMDERR_ILLEGAL_PROTO)); + return; + } + + cmd = g_strconcat("command ", newcmd, NULL); + ascii_strdown(cmd); + + oldcmd = current_command; + current_command = cmd+8; + if (server != NULL) server_ref(server); + if (!signal_emit(cmd, 3, args, server, item)) { + signal_emit_id(signal_default_command, 3, + command, server, item); + } + if (server != NULL) { + if (server->connection_lost) + server_disconnect(server); + server_unref(server); + } + current_command = oldcmd; + + g_free(cmd); + g_free(orig); +} + +static void event_command(const char *line, SERVER_REC *server, void *item) +{ + char *cmdchar; + int expand_aliases = TRUE; + + g_return_if_fail(line != NULL); + + cmdchar = *line == '\0' ? NULL : + strchr(settings_get_str("cmdchars"), *line); + if (cmdchar != NULL && line[1] == ' ') { + /* "/ text" = same as sending "text" to active channel. */ + line += 2; + cmdchar = NULL; + } + if (cmdchar == NULL) { + /* non-command - let someone else handle this */ + signal_emit("send text", 3, line, server, item); + return; + } + + /* same cmdchar twice ignores aliases */ + line++; + if (*line == *cmdchar) { + line++; + expand_aliases = FALSE; + } + + /* ^command hides the output - we'll do this at fe-common but + we have to skip the ^ char here.. */ + if (*line == '^') line++; + + parse_command(line, expand_aliases, server, item); +} + +static int eval_recursion_depth=0; +/* SYNTAX: EVAL <command(s)> */ +static void cmd_eval(const char *data, SERVER_REC *server, void *item) +{ + g_return_if_fail(data != NULL); + if (eval_recursion_depth > 100) + cmd_return_error(CMDERR_EVAL_MAX_RECURSE); + + + eval_recursion_depth++; + eval_special_string(data, "", server, item); + eval_recursion_depth--; +} + +/* SYNTAX: CD <directory> */ +static void cmd_cd(const char *data) +{ + char *str; + + g_return_if_fail(data != NULL); + if (*data == '\0') return; + + str = convert_home(data); + if (chdir(str) != 0) { + g_warning("Failed to chdir(): %s", strerror(errno)); + } + g_free(str); +} + +void commands_init(void) +{ + commands = NULL; + current_command = NULL; + alias_runstack = NULL; + + signal_default_command = signal_get_uniq_id("default command"); + + settings_add_str("misc", "cmdchars", "/"); + signal_add("send command", (SIGNAL_FUNC) event_command); + + command_bind("eval", NULL, (SIGNAL_FUNC) cmd_eval); + command_bind("cd", NULL, (SIGNAL_FUNC) cmd_cd); +} + +void commands_deinit(void) +{ + g_free_not_null(current_command); + + signal_remove("send command", (SIGNAL_FUNC) event_command); + + command_unbind("eval", (SIGNAL_FUNC) cmd_eval); + command_unbind("cd", (SIGNAL_FUNC) cmd_cd); +} |