diff options
author | Sebastien Helleu <flashcode@flashtux.org> | 2007-10-31 17:00:31 +0100 |
---|---|---|
committer | Sebastien Helleu <flashcode@flashtux.org> | 2007-10-31 17:00:31 +0100 |
commit | eb2835fa24d510a46f8616855624f7ac1ddd19b8 (patch) | |
tree | 558e2ea1916ea5873c166167bbdd493f2cdcc40b /src/core | |
parent | 7fd804eab584711da59aec59a84dbb3c5429c652 (diff) | |
download | weechat-eb2835fa24d510a46f8616855624f7ac1ddd19b8.zip |
Moved files from src/common to core, gui and plugins directories
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/CMakeLists.txt | 28 | ||||
-rw-r--r-- | src/core/Makefile.am | 48 | ||||
-rw-r--r-- | src/core/wee-alias.c | 376 | ||||
-rw-r--r-- | src/core/wee-alias.h | 47 | ||||
-rw-r--r-- | src/core/wee-backtrace.c | 176 | ||||
-rw-r--r-- | src/core/wee-backtrace.h | 27 | ||||
-rw-r--r-- | src/core/wee-command.c | 4521 | ||||
-rw-r--r-- | src/core/wee-command.h | 86 | ||||
-rw-r--r-- | src/core/wee-config.c | 2597 | ||||
-rw-r--r-- | src/core/wee-config.h | 291 | ||||
-rw-r--r-- | src/core/wee-list.c | 241 | ||||
-rw-r--r-- | src/core/wee-list.h | 43 | ||||
-rw-r--r-- | src/core/wee-log.c | 233 | ||||
-rw-r--r-- | src/core/wee-log.h | 31 | ||||
-rw-r--r-- | src/core/wee-upgrade.c | 1867 | ||||
-rw-r--r-- | src/core/wee-upgrade.h | 205 | ||||
-rw-r--r-- | src/core/wee-utf8.c | 426 | ||||
-rw-r--r-- | src/core/wee-utf8.h | 50 | ||||
-rw-r--r-- | src/core/wee-util.c | 730 | ||||
-rw-r--r-- | src/core/wee-util.h | 41 | ||||
-rw-r--r-- | src/core/weechat.c | 884 | ||||
-rw-r--r-- | src/core/weechat.h | 121 |
22 files changed, 13069 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt new file mode 100644 index 000000000..8f48d90b2 --- /dev/null +++ b/src/core/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) 2003-2007 FlashCode <flashcode@flashtux.org> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +SET(LIB_MAIN_SRC weechat.c weechat.h alias.c alias.h backtrace.c backtrace.h +command.c command.h completion.c completion.h weelist.c weelist.h weeconfig.c +weeconfig.h history.c history.h hotlist.c hotlist.h log.c log.h fifo.c fifo.h +session.c session.h utf8.c utf8.h util.c util.h) + +# Check for flock support +INCLUDE(CheckSymbolExists) +CHECK_INCLUDE_FILES("sys/file.h" HAVE_SYS_FILE_H) +CHECK_SYMBOL_EXISTS(flock "sys/file.h" HAVE_FLOCK) + +INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}) +ADD_LIBRARY(weechat_main STATIC ${LIB_MAIN_SRC}) diff --git a/src/core/Makefile.am b/src/core/Makefile.am new file mode 100644 index 000000000..ca0701c73 --- /dev/null +++ b/src/core/Makefile.am @@ -0,0 +1,48 @@ +# Copyright (c) 2003-2007 FlashCode <flashcode@flashtux.org> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. +# + +INCLUDES = -DLOCALEDIR=\"$(datadir)/locale\" $(GNUTLS_CFLAGS) + +noinst_LIBRARIES = lib_weechat_main.a + +lib_weechat_main_a_SOURCES = weechat.c \ + weechat.h \ + alias.c \ + alias.h \ + backtrace.c \ + backtrace.h \ + command.c \ + command.h \ + completion.c \ + completion.h \ + weelist.c \ + weelist.h \ + weeconfig.c \ + weeconfig.h \ + history.c \ + history.h \ + hotlist.c \ + hotlist.h \ + log.c \ + log.h \ + fifo.c \ + fifo.h \ + session.c \ + session.h \ + utf8.c \ + utf8.h \ + util.c \ + util.h diff --git a/src/core/wee-alias.c b/src/core/wee-alias.c new file mode 100644 index 000000000..4a8919f35 --- /dev/null +++ b/src/core/wee-alias.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* alias.c: WeeChat alias */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "weechat.h" +#include "alias.h" +#include "util.h" +#include "../protocols/irc/irc.h" + + +t_weechat_alias *weechat_alias = NULL; +t_weechat_alias *weechat_last_alias = NULL; + + +/* + * alias_search: search an alias + */ + +t_weechat_alias * +alias_search (char *alias_name) +{ + t_weechat_alias *ptr_alias; + + for (ptr_alias = weechat_alias; ptr_alias; ptr_alias = ptr_alias->next_alias) + { + if (ascii_strcasecmp (alias_name, ptr_alias->alias_name) == 0) + return ptr_alias; + } + return NULL; +} + +/* + * alias_find_pos: find position for an alias (for sorting aliases) + */ + +t_weechat_alias * +alias_find_pos (char *alias_name) +{ + t_weechat_alias *ptr_alias; + + for (ptr_alias = weechat_alias; ptr_alias; ptr_alias = ptr_alias->next_alias) + { + if (ascii_strcasecmp (alias_name, ptr_alias->alias_name) < 0) + return ptr_alias; + } + return NULL; +} + +/* + * alias_insert_sorted: insert alias into sorted list + */ + +void +alias_insert_sorted (t_weechat_alias *alias) +{ + t_weechat_alias *pos_alias; + + pos_alias = alias_find_pos (alias->alias_name); + + if (weechat_alias) + { + if (pos_alias) + { + /* insert alias into the list (before alias found) */ + alias->prev_alias = pos_alias->prev_alias; + alias->next_alias = pos_alias; + if (pos_alias->prev_alias) + pos_alias->prev_alias->next_alias = alias; + else + weechat_alias = alias; + pos_alias->prev_alias = alias; + } + else + { + /* add alias to the end */ + alias->prev_alias = weechat_last_alias; + alias->next_alias = NULL; + weechat_last_alias->next_alias = alias; + weechat_last_alias = alias; + } + } + else + { + alias->prev_alias = NULL; + alias->next_alias = NULL; + weechat_alias = alias; + weechat_last_alias = alias; + } +} + +/* + * alias_new: create new alias and add it to alias list + */ + +t_weechat_alias * +alias_new (char *alias_name, char *alias_command) +{ + t_weechat_alias *new_alias, *ptr_alias; + + while (alias_name[0] == '/') + { + alias_name++; + } + + if (ascii_strcasecmp (alias_name, "builtin") == 0) + return NULL; + + ptr_alias = alias_search (alias_name); + if (ptr_alias) + { + if (ptr_alias->alias_command) + free (ptr_alias->alias_command); + ptr_alias->alias_command = strdup (alias_command); + return ptr_alias; + } + + if ((new_alias = ((t_weechat_alias *) malloc (sizeof (t_weechat_alias))))) + { + new_alias->alias_name = strdup (alias_name); + new_alias->alias_command = (char *) malloc (strlen (alias_command) + 1); + new_alias->running = 0; + if (new_alias->alias_command) + strcpy (new_alias->alias_command, alias_command); + alias_insert_sorted (new_alias); + return new_alias; + } + else + return NULL; +} + +/* + * alias_get_final_command: get final command pointed by an alias + */ + +char * +alias_get_final_command (t_weechat_alias *alias) +{ + t_weechat_alias *ptr_alias; + char *result; + + if (alias->running) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s circular reference when calling alias \"/%s\"\n"), + WEECHAT_ERROR, alias->alias_name); + return NULL; + } + + ptr_alias = alias_search ((alias->alias_command[0] == '/') ? + alias->alias_command + 1 : alias->alias_command); + if (ptr_alias) + { + alias->running = 1; + result = alias_get_final_command (ptr_alias); + alias->running = 0; + return result; + } + return (alias->alias_command[0] == '/') ? + alias->alias_command + 1 : alias->alias_command; +} + +/* + * alias_add_word: add word to string and increment length + * This function should NOT be called directly. + */ + +void +alias_add_word (char **alias, int *length, char *word) +{ + int length_word; + + if (!word) + return; + + length_word = strlen (word); + if (length_word == 0) + return; + + if (*alias == NULL) + { + *alias = (char *) malloc (length_word + 1); + strcpy (*alias, word); + } + else + { + *alias = realloc (*alias, strlen (*alias) + length_word + 1); + strcat (*alias, word); + } + *length += length_word; +} + +/* + * alias_replace_args: replace arguments ($1, $2, .. or $*) in alias arguments + */ + +char * +alias_replace_args (char *alias_args, char *user_args) +{ + char **argv, *start, *pos, *res; + int argc, length_res, args_count; + + argv = explode_string (user_args, " ", 0, &argc); + + res = NULL; + length_res = 0; + args_count = 0; + start = alias_args; + pos = start; + while (pos && pos[0]) + { + if ((pos[0] == '\\') && (pos[1] == '$')) + { + pos[0] = '\0'; + alias_add_word (&res, &length_res, start); + alias_add_word (&res, &length_res, "$"); + pos[0] = '\\'; + start = pos + 2; + pos = start; + } + else + { + if (pos[0] == '$') + { + if (pos[1] == '*') + { + args_count++; + pos[0] = '\0'; + alias_add_word (&res, &length_res, start); + alias_add_word (&res, &length_res, user_args); + pos[0] = '$'; + start = pos + 2; + pos = start; + } + else + { + if ((pos[1] >= '1') && (pos[1] <= '9')) + { + args_count++; + pos[0] = '\0'; + alias_add_word (&res, &length_res, start); + if (pos[1] - '0' <= argc) + alias_add_word (&res, &length_res, argv[pos[1] - '1']); + pos[0] = '$'; + start = pos + 2; + pos = start; + } + else + pos++; + } + } + else + pos++; + } + } + + if (start < pos) + alias_add_word (&res, &length_res, start); + + if ((args_count == 0) && user_args && user_args[0]) + { + alias_add_word (&res, &length_res, " "); + alias_add_word (&res, &length_res, user_args); + } + + if (argv) + free_exploded_string (argv); + + return res; +} + +/* + * alias_replace_vars: replace special vars ($nick, $channel, $server) in a string + */ + +char * +alias_replace_vars (t_irc_server *server, t_irc_channel *channel, char *string) +{ + char *var_nick, *var_channel, *var_server; + char empty_string[1] = { '\0' }; + char *res, *temp; + + var_nick = (server && server->nick) ? server->nick : empty_string; + var_channel = (channel) ? channel->name : empty_string; + var_server = (server) ? server->name : empty_string; + + /* replace nick */ + temp = weechat_strreplace (string, "$nick", var_nick); + if (!temp) + return NULL; + res = temp; + + /* replace channel */ + temp = weechat_strreplace (res, "$channel", var_channel); + free (res); + if (!temp) + return NULL; + res = temp; + + /* replace server */ + temp = weechat_strreplace (res, "$server", var_server); + free (res); + if (!temp) + return NULL; + res = temp; + + /* return result */ + return res; +} + +/* + * alias_free: free an alias and reomve it from list + */ + +void +alias_free (t_weechat_alias *alias) +{ + t_weechat_alias *new_weechat_alias; + + /* remove alias from list */ + if (weechat_last_alias == alias) + weechat_last_alias = alias->prev_alias; + if (alias->prev_alias) + { + (alias->prev_alias)->next_alias = alias->next_alias; + new_weechat_alias = weechat_alias; + } + else + new_weechat_alias = alias->next_alias; + + if (alias->next_alias) + (alias->next_alias)->prev_alias = alias->prev_alias; + + /* free data */ + if (alias->alias_name) + free (alias->alias_name); + if (alias->alias_command) + free (alias->alias_command); + free (alias); + weechat_alias = new_weechat_alias; +} + +/* + * alias_free_all: free all alias + */ + +void +alias_free_all () +{ + while (weechat_alias) + alias_free (weechat_alias); +} diff --git a/src/core/wee-alias.h b/src/core/wee-alias.h new file mode 100644 index 000000000..759330ddd --- /dev/null +++ b/src/core/wee-alias.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_ALIAS_H +#define __WEECHAT_ALIAS_H 1 + +#include "../protocols/irc/irc.h" + +typedef struct t_weechat_alias t_weechat_alias; + +struct t_weechat_alias +{ + char *alias_name; + char *alias_command; + int running; + t_weechat_alias *prev_alias; + t_weechat_alias *next_alias; +}; + +extern t_weechat_alias *weechat_alias; +extern t_weechat_alias *weechat_last_alias; + +extern t_weechat_alias *alias_search (char *); +extern t_weechat_alias *alias_new (char *, char *); +extern char *alias_get_final_command (t_weechat_alias *); +extern char *alias_replace_args (char *, char *); +extern char *alias_replace_vars (t_irc_server *, t_irc_channel *, char *); +extern void alias_free (t_weechat_alias *); +extern void alias_free_all (); + +#endif /* alias.h */ diff --git a/src/core/wee-backtrace.c b/src/core/wee-backtrace.c new file mode 100644 index 000000000..d6d0b8b08 --- /dev/null +++ b/src/core/wee-backtrace.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* backtrace.c: display backtrace after a segfault */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> + +#ifndef __USE_GNU +#define __USE_GNU +#endif + +#include <dlfcn.h> + +#ifdef HAVE_BACKTRACE +#include <execinfo.h> +#endif + +#include "weechat.h" +#include "backtrace.h" +#include "log.h" +#include "util.h" + + +/* + * weechat_backtrace_printf: display a backtrace line (on stderr and in WeeChat log) + */ + +void +weechat_backtrace_printf (char *message, ...) +{ + static char buffer[4096]; + va_list argptr; + + va_start (argptr, message); + vsnprintf (buffer, sizeof (buffer) - 1, message, argptr); + va_end (argptr); + + weechat_iconv_fprintf (stderr, "%s", buffer); + weechat_log_printf ("%s", buffer); +} + +/* + * weechat_backtrace_addr2line: display function name and line with a backtrace address + */ + +void +weechat_backtrace_addr2line (int number, void *address, char *symbol) +{ +#ifdef HAVE_BACKTRACE + int rc; + Dl_info info; + const void *addr; + FILE *output; + char cmd_line[1024]; + char line[1024], *ptr_line, *pos; + char function_name[1024]; + int file_line; + + rc = dladdr (address, &info); + if ((rc == 0) || !info.dli_fname || !info.dli_fname[0]) + { + weechat_backtrace_printf ("%03d %s\n", number, symbol); + return; + } + + addr = address; + if (info.dli_fbase >= (const void *) 0x40000000) + addr = (void *)((unsigned long)((const char *) addr) - (unsigned long) info.dli_fbase); + + snprintf (cmd_line, sizeof (cmd_line), + "addr2line --functions --demangle -e $(which %s) %p", + info.dli_fname, addr); + output = popen (cmd_line, "r"); + if (!output) + { + weechat_backtrace_printf ("%03d %s\n", number, symbol); + return; + } + function_name[0] = '\0'; + file_line = 0; + while (!feof (output)) + { + ptr_line = fgets (line, sizeof (line) - 1, output); + if (ptr_line && ptr_line[0]) + { + pos = strchr (ptr_line, '\n'); + if (pos) + pos[0] = '\0'; + if (strchr (ptr_line, ':')) + { + file_line = 1; + weechat_backtrace_printf ("%03d %s%s%s%s\n", + number, + ptr_line, + (function_name[0]) ? " [function " : "", + function_name, + (function_name[0]) ? "]" : ""); + function_name[0] = '\0'; + } + else + { + if (function_name[0]) + weechat_backtrace_printf ("%03d %s", + number, function_name); + snprintf (function_name, sizeof (function_name), + "%s", ptr_line); + } + } + } + if (function_name[0]) + weechat_backtrace_printf ("%03d %s\n", + number, function_name); + pclose (output); +#else + /* make C compiler happy */ + (void) number; + (void) address; + (void) symbol; +#endif +} + +/* + * weechat_backtrace: display backtrace (called when a SIGSEGV is received) + */ + +void +weechat_backtrace () +{ +#ifdef HAVE_BACKTRACE + void *trace[BACKTRACE_MAX]; + int trace_size, i; + char **symbols; +#endif + + weechat_backtrace_printf ("======= WeeChat backtrace =======\n"); + weechat_backtrace_printf ("(written by %s, compiled on %s %s)\n", + PACKAGE_STRING, __DATE__, __TIME__); + +#ifdef HAVE_BACKTRACE + trace_size = backtrace (trace, BACKTRACE_MAX); + symbols = backtrace_symbols (trace, trace_size); + + for (i = 0; i < trace_size; i++) + { + weechat_backtrace_addr2line (i + 1, trace[i], symbols[i]); + } +#else + weechat_backtrace_printf (" No backtrace info (no debug info available " + "or no backtrace possible on your system).\n"); +#endif + + weechat_backtrace_printf ("======= End of backtrace =======\n"); +} diff --git a/src/core/wee-backtrace.h b/src/core/wee-backtrace.h new file mode 100644 index 000000000..80a2c8f41 --- /dev/null +++ b/src/core/wee-backtrace.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_BACKTACE_H +#define __WEECHAT_BACKTACE_H 1 + +#define BACKTRACE_MAX 128 + +extern void weechat_backtrace (); + +#endif /* backtrace.h */ diff --git a/src/core/wee-command.c b/src/core/wee-command.c new file mode 100644 index 000000000..337a961a2 --- /dev/null +++ b/src/core/wee-command.c @@ -0,0 +1,4521 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* command.c: WeeChat internal commands */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "weechat.h" +#include "command.h" +#include "alias.h" +#include "fifo.h" +#include "log.h" +#include "session.h" +#include "utf8.h" +#include "util.h" +#include "weelist.h" +#include "weeconfig.h" +#include "../protocols/irc/irc.h" +#include "../gui/gui.h" + +#ifdef PLUGINS +#include "../plugins/plugins.h" +#endif + + +/* WeeChat internal commands */ + +t_weechat_command weechat_commands[] = +{ { "alias", N_("create an alias for a command"), + N_("[alias_name [command [arguments]]]"), + N_("alias_name: name of alias\n" + " command: command name (WeeChat or IRC command, many commands " + "can be separated by semicolons)\n" + " arguments: arguments for command\n\n" + "Note: in command, special variables $1, $2,..,$9 are replaced by " + "arguments given by user, and $* is replaced by all arguments.\n" + "Variables $nick, $channel and $server are replaced by current " + "nick/channel/server."), + "%- %A", 0, MAX_ARGS, 1, NULL, weechat_cmd_alias }, + { "buffer", N_("manage buffers"), + N_("[action [args] | number | [[server] [channel]]]"), + N_(" action: action to do:\n" + " move: move buffer in the list (may be relative, for example -1)\n" + " close: close buffer (optional arg is part message, for a channel)\n" + " list: list open buffers (no parameter implies this list)\n" + " notify: set notify level for buffer (0=never, 1=highlight, 2=1+msg, " + "3=2+join/part)\n" + " (when executed on server buffer, this sets default notify " + "level for whole server)\n" + " scroll: scroll in history (may be relative, and may end by a letter: " + "s=sec, m=min, h=hour, d=day, M=month, y=year); if there is " + "only letter, then scroll to beginning of this item\n\n" + " number: jump to buffer by number\n" + "server,\n" + "channel: jump to buffer by server and/or channel name\n\n" + "Examples:\n" + " move buffer: /buffer move 5\n" + " close buffer: /buffer close this is part msg\n" + " set notify: /buffer notify 2\n" + " scroll 1 day up: /buffer scroll 1d == /buffer scroll -1d == /buffer scroll -24h\n" + "scroll to beginning\n" + " of this day: /buffer scroll d\n" + " scroll 15 min down: /buffer scroll +15m\n" + " scroll 20 msgs up: /buffer scroll -20\n" + " jump to #weechat: /buffer #weechat"), + "move|close|list|notify|scroll|%S|%C %S|%C", 0, MAX_ARGS, 0, NULL, weechat_cmd_buffer }, + { "builtin", N_("launch WeeChat/IRC builtin command (do not look at plugins handlers or aliases)"), + N_("command"), + N_("command: command to execute (a '/' is automatically added if not found at beginning of command)\n"), + "%w|%i", 0, MAX_ARGS, 1, NULL, weechat_cmd_builtin }, + { "clear", N_("clear window(s)"), + N_("[-all | number [number ...]]"), + N_(" -all: clear all buffers\n" + "number: clear buffer by number"), + "-all", 0, MAX_ARGS, 0, weechat_cmd_clear, NULL }, + { "connect", N_("connect to server(s)"), + N_("[-all [-nojoin] | servername [servername ...] [-nojoin] | hostname " + "[-port port] [-ipv6] [-ssl]]"), + N_(" -all: connect to all servers\n" + "servername: internal server name to connect\n" + " -nojoin: do not join any channel (even if autojoin is enabled on server)\n" + " hostname: hostname to connect, creating temporary server\n" + " port: port for server (integer, default is 6667)\n" + " ipv6: use IPv6 protocol\n" + " ssl: use SSL protocol"), + "%S|-all|-nojoin|%*", 0, MAX_ARGS, 0, weechat_cmd_connect, NULL }, + { "disconnect", N_("disconnect from server(s)"), + N_("[-all | servername [servername ...]]"), + N_(" -all: disconnect from all servers\n" + "servername: server name to disconnect"), + "%S|-all", 0, MAX_ARGS, 0, weechat_cmd_disconnect, NULL }, + { "dcc", N_("starts DCC (file or chat) or close chat"), + N_("action [nickname [file]]"), + N_(" action: 'send' (file) or 'chat' or 'close' (chat)\n" + "nickname: nickname to send file or chat\n" + " file: filename (on local host)"), + "chat|send|close %n %f", 1, MAX_ARGS, 0, NULL, weechat_cmd_dcc }, + { "debug", N_("print debug messages"), + N_("dump | buffer | windows"), + N_(" dump: save memory dump in WeeChat log file (same dump is written when WeeChat crashes)\n" + " buffer: dump buffer content with hexadecimal values in log file\n" + "windows: display windows tree"), + "dump|buffer|windows", 1, 1, 0, weechat_cmd_debug, NULL }, + { "help", N_("display help about commands"), + N_("[command]"), + N_("command: name of a WeeChat or IRC command"), + "%w|%i|%h", 0, 1, 0, weechat_cmd_help, NULL }, + { "history", N_("show buffer command history"), + N_("[clear | value]"), + N_("clear: clear history\n" + "value: number of history entries to show"), + "clear", 0, 1, 0, weechat_cmd_history, NULL }, + { "ignore", N_("ignore IRC messages and/or hosts"), + N_("[mask [[type | command] [channel [server]]]]"), + N_(" mask: nick or host mask to ignore\n" + " type: type of message to ignore (action, ctcp, dcc, pv)\n" + "command: IRC command\n" + "channel: name of channel for ignore\n" + " server: name of server for ignore\n\n" + "For each argument, '*' means all.\n" + "Without argument, /ignore command lists all defined ignore."), + "*|%n *|action|ctcp|dcc|pv|%I *|%c *|%s", + 0, 4, 0, weechat_cmd_ignore, NULL }, + { "key", N_("bind/unbind keys"), + N_("[key [function/command]] [unbind key] [functions] [call function [\"args\"]] [reset -yes]"), + N_(" key: display or bind this key to an internal function or a command " + "(beginning by \"/\")\n" + " unbind: unbind a key\n" + "functions: list internal functions for key bindings\n" + " call: call a function by name (with optional arguments)\n" + " reset: restore bindings to the default values and delete ALL " + "personal bindings (use carefully!)"), + "unbind|functions|call|reset %k", 0, MAX_ARGS, 0, NULL, weechat_cmd_key }, +/* { "panel", N_("manage panels"), + N_("[list | add type position size | resize # size | close # | move #1 #2]"), + N_(" list: list open panels (no parameter implies this list)\n" + " add: add a panel, type is global|local, position is top|bottom|left|right\n" + " resize: resize a panel with a new size (may be relative, for example -1)\n" + " close: close a panel by number\n" + " move: move a panel to another number (may be relative, for example -1)"), + "list|add|close|move global|local top|bottom|left|right", + 0, MAX_ARGS, 0, weechat_cmd_panel, NULL },*/ + { "plugin", N_("list/load/unload plugins"), + N_("[list [name]] | [listfull [name]] | [load filename] | [autoload] | [reload [name]] | [unload [name]]"), + N_(" list: list loaded plugins\n" + "listfull: list loaded plugins with detailed info for each plugin\n" + " load: load a plugin\n" + "autoload: autoload plugins in system or user directory\n" + " reload: reload one plugin (if no name given, unload all plugins, then autoload plugins)\n" + " unload: unload one or all plugins\n\n" + "Without argument, /plugin command lists loaded plugins."), + "list|listfull|load|autoload|reload|unload %P", 0, 2, 0, weechat_cmd_plugin, NULL }, + { "reconnect", N_("reconnect to server(s)"), + N_("[-all [-nojoin] | servername [servername ...] [-nojoin]]"), + N_(" -all: reconnect to all servers\n" + "servername: server name to reconnect\n" + " -nojoin: do not join any channel (even if autojoin is enabled on server)"), + "%S|-all|-nojoin|%*", 0, MAX_ARGS, 0, weechat_cmd_reconnect, NULL }, + { "save", N_("save config to disk"), + N_("[file]"), N_("file: filename for writing config"), + NULL, 0, 1, 0, weechat_cmd_save, NULL }, + { "server", N_("list, add or remove servers"), + N_("[list [servername]] | [listfull [servername]] | [add servername " + "hostname [-port port] [-temp] [-auto | -noauto] [-ipv6] [-ssl] " + "[-pwd password] [-nicks nick1 nick2 nick3] [-username username] " + "[-realname realname] [-command command] [-autojoin channel[,channel]] ] " + "| [copy servername newservername] | [rename servername newservername] " + "| [keep servername] | [del servername]"), + N_(" list: list servers (no parameter implies this list)\n" + " listfull: list servers with detailed info for each server\n" + " add: create a new server\n" + "servername: server name, for internal and display use\n" + " hostname: name or IP address of server\n" + " port: port for server (integer, default is 6667)\n" + " temp: create temporary server (not saved in config file)\n" + " auto: automatically connect to server when WeeChat starts\n" + " noauto: do not connect to server when WeeChat starts (default)\n" + " ipv6: use IPv6 protocol\n" + " ssl: use SSL protocol\n" + " password: password for server\n" + " nick1: first nick for server\n" + " nick2: alternate nick for server\n" + " nick3: second alternate nick for server\n" + " username: user name\n" + " realname: real name of user\n" + " copy: duplicate a server\n" + " rename: rename a server\n" + " keep: keep server in config file (for temporary servers only)\n" + " del: delete a server\n" + " deloutq: delete messages out queue for all servers (all messages " + "WeeChat is currently sending)"), + "add|copy|rename|keep|del|deloutq|list|listfull %S %S", + 0, MAX_ARGS, 0, weechat_cmd_server, NULL }, + { "set", N_("set config options"), + N_("[option [ = value]]"), + N_("option: name of an option (if name is full " + "and no value is given, then help is displayed on option)\n" + " value: value for option\n\n" + "Option may be: servername.server_xxx where \"servername\" is an " + "internal server name and \"xxx\" an option for this server."), + "%o = %v", 0, MAX_ARGS, 0, NULL, weechat_cmd_set }, + { "setp", N_("set plugin config options"), + N_("[option [ = value]]"), + N_("option: name of a plugin option\n" + " value: value for option\n\n" + "Option is format: plugin.option, example: perl.myscript.item1"), + "%O = %V", 0, MAX_ARGS, 0, NULL, weechat_cmd_setp }, + { "unalias", N_("remove an alias"), + N_("alias_name"), N_("alias_name: name of alias to remove"), + "%a", 1, 1, 0, NULL, weechat_cmd_unalias }, + { "unignore", N_("unignore IRC messages and/or hosts"), + N_("[number | [mask [[type | command] [channel [server]]]]]"), + N_(" number: # of ignore to unignore (number is displayed by list of ignore)\n" + " mask: nick or host mask to unignore\n" + " type: type of message to unignore (action, ctcp, dcc, pv)\n" + "command: IRC command\n" + "channel: name of channel for unignore\n" + " server: name of server for unignore\n\n" + "For each argument, '*' means all.\n" + "Without argument, /unignore command lists all defined ignore."), + "*|%n *|action|ctcp|dcc|pv|%I *|%c *|%s", + 0, 4, 0, weechat_cmd_unignore, NULL }, + { "upgrade", N_("upgrade WeeChat without disconnecting from servers"), + N_("[path_to_binary]"), + N_("path_to_binary: path to WeeChat binary (default is current binary)\n\n" + "This command run again a WeeChat binary, so it should have been compiled " + "or installed with a package manager before running this command."), + "%f", 0, 1, 0, weechat_cmd_upgrade, NULL }, + { "uptime", N_("show WeeChat uptime"), + N_("[-o]"), + N_("-o: send uptime on current channel as an IRC message"), + "-o", 0, 1, 0, weechat_cmd_uptime, NULL }, + { "window", N_("manage windows"), + N_("[list | -1 | +1 | b# | up | down | left | right | splith [pct] " + "| splitv [pct] | resize pct | merge [all]]"), + N_(" list: list open windows (no parameter implies this list)\n" + " -1: jump to previous window\n" + " +1: jump to next window\n" + " b#: jump to next window displaying buffer number #\n" + " up: switch to window above current one\n" + " down: switch to window below current one\n" + " left: switch to window on the left\n" + " right: switch to window on the right\n" + "splith: split current window horizontally\n" + "splitv: split current window vertically\n" + "resize: resize window size, new size is <pct> pourcentage of parent window\n" + " merge: merge window with another (all = keep only one window)\n\n" + "For splith and splitv, pct is a pourcentage which represents " + "size of new window, computed with current window as size reference. " + "For example 25 means create a new window with size = current_size / 4"), + "list|-1|+1|up|down|left|right|splith|splitv|resize|merge all", + 0, 2, 0, weechat_cmd_window, NULL }, + { NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, NULL } +}; + +t_weelist *index_commands; +t_weelist *last_index_command; + + +/* + * command_index_build: build an index of commands (internal, irc and alias) + * This list will be sorted, and used for completion + */ + +void +command_index_build () +{ + int i; + + index_commands = NULL; + last_index_command = NULL; + i = 0; + while (weechat_commands[i].command_name) + { + (void) weelist_add (&index_commands, &last_index_command, + weechat_commands[i].command_name, + WEELIST_POS_SORT); + i++; + } + i = 0; + while (irc_commands[i].command_name) + { + if (irc_commands[i].cmd_function_args || irc_commands[i].cmd_function_1arg) + (void) weelist_add (&index_commands, &last_index_command, + irc_commands[i].command_name, + WEELIST_POS_SORT); + i++; + } +} + +/* + * command_index_free: remove all commands in index + */ + +void +command_index_free () +{ + while (index_commands) + { + weelist_remove (&index_commands, &last_index_command, index_commands); + } +} + +/* + * command_used_by_weechat: return 1 if command is used by weechat + * (weechat command, IRC command or alias) + */ + +int +command_used_by_weechat (char *command) +{ + t_weechat_alias *ptr_alias; + int i; + + /* look for alias */ + for (ptr_alias = weechat_alias; ptr_alias; + ptr_alias = ptr_alias->next_alias) + { + if (ascii_strcasecmp (ptr_alias->alias_name, command) == 0) + return 1; + } + + /* look for WeeChat command */ + for (i = 0; weechat_commands[i].command_name; i++) + { + if (ascii_strcasecmp (weechat_commands[i].command_name, command) == 0) + return 1; + } + + /* look for IRC command */ + for (i = 0; irc_commands[i].command_name; i++) + { + if ((ascii_strcasecmp (irc_commands[i].command_name, command) == 0) && + ((irc_commands[i].cmd_function_args) || + (irc_commands[i].cmd_function_1arg))) + return 1; + } + + /* no command/alias found */ + return 0; +} + +/* + * exec_weechat_command: executes a command (WeeChat internal or IRC) + * if only_builtin == 1, then try only WeeChat/IRC commands + * (not plugins neither aliases) + * returns: 1 if command was executed succesfully + * 0 if error (command not executed) + */ + +int +exec_weechat_command (t_irc_server *server, t_irc_channel *channel, char *string, + int only_builtin) +{ + int i, rc, argc, argc2, return_code, length1, length2; + char *command, *pos, *ptr_args, *ptr_args2; + char **argv, **argv2, *alias_command; + char **commands, **ptr_cmd, **ptr_next_cmd; + char *args_replaced, *vars_replaced, *new_ptr_cmd; + char *unknown_command; + int some_args_replaced; + t_weechat_alias *ptr_alias; + + if ((!string) || (!string[0]) || (string[0] != '/')) + return 0; + + command = strdup (string); + + /* look for end of command */ + ptr_args = NULL; + + pos = &command[strlen (command) - 1]; + if (pos[0] == ' ') + { + while ((pos > command) && (pos[0] == ' ')) + pos--; + pos[1] = '\0'; + } + + pos = strchr (command, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + ptr_args = pos; + if (!ptr_args[0]) + ptr_args = NULL; + } + +#ifdef PLUGINS + if (only_builtin) + rc = -1; + else + { + vars_replaced = alias_replace_vars (server, channel, ptr_args); + rc = plugin_cmd_handler_exec ((server) ? server->name : "", command + 1, + (vars_replaced) ? vars_replaced : ptr_args); + if (vars_replaced) + free (vars_replaced); + } +#else + rc = -1; +#endif + switch (rc) + { + case 0: /* plugin handler KO */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s command \"%s\" failed\n"), + WEECHAT_ERROR, command + 1); + break; + case 1: /* plugin handler OK, executed */ + break; + default: /* plugin handler not found */ + argv = explode_string (ptr_args, " ", 0, &argc); + + /* look for alias */ + if (!only_builtin) + { + for (ptr_alias = weechat_alias; ptr_alias; + ptr_alias = ptr_alias->next_alias) + { + if (ascii_strcasecmp (ptr_alias->alias_name, command + 1) == 0) + { + if (ptr_alias->running == 1) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s circular reference when calling alias \"/%s\"\n"), + WEECHAT_ERROR, ptr_alias->alias_name); + } + else + { + /* an alias can contain many commands separated by ';' */ + commands = split_multi_command (ptr_alias->alias_command, ';'); + if (commands) + { + some_args_replaced = 0; + ptr_alias->running = 1; + for (ptr_cmd=commands; *ptr_cmd; ptr_cmd++) + { + ptr_next_cmd = ptr_cmd; + ptr_next_cmd++; + + vars_replaced = alias_replace_vars (server, channel, *ptr_cmd); + new_ptr_cmd = (vars_replaced) ? vars_replaced : *ptr_cmd; + args_replaced = alias_replace_args (new_ptr_cmd, ptr_args); + if (args_replaced) + { + some_args_replaced = 1; + if (*ptr_cmd[0] == '/') + (void) exec_weechat_command (server, channel, args_replaced, only_builtin); + else + { + alias_command = (char *) malloc (1 + strlen(args_replaced) + 1); + if (alias_command) + { + strcpy (alias_command, "/"); + strcat (alias_command, args_replaced); + (void) exec_weechat_command (server, channel, alias_command, only_builtin); + free (alias_command); + } + } + free (args_replaced); + } + else + { + /* if alias has arguments, they are now + arguments of the last command in the list (if no $1,$2,..$*) was found */ + if ((*ptr_next_cmd == NULL) && ptr_args && (!some_args_replaced)) + { + length1 = strlen (new_ptr_cmd); + length2 = strlen (ptr_args); + + alias_command = (char *) malloc ( 1 + length1 + 1 + length2 + 1); + if (alias_command) + { + if (*ptr_cmd[0] != '/') + strcpy (alias_command, "/"); + else + strcpy (alias_command, ""); + + strcat (alias_command, new_ptr_cmd); + strcat (alias_command, " "); + strcat (alias_command, ptr_args); + + (void) exec_weechat_command (server, channel, alias_command, only_builtin); + free (alias_command); + } + } + else + { + if (*ptr_cmd[0] == '/') + (void) exec_weechat_command (server, channel, new_ptr_cmd, only_builtin); + else + { + alias_command = (char *) malloc (1 + strlen (new_ptr_cmd) + 1); + if (alias_command) + { + strcpy (alias_command, "/"); + strcat (alias_command, new_ptr_cmd); + (void) exec_weechat_command (server, channel, alias_command, only_builtin); + free (alias_command); + } + } + } + } + if (vars_replaced) + free (vars_replaced); + } + ptr_alias->running = 0; + free_multi_command (commands); + } + } + free_exploded_string (argv); + free (command); + return 1; + } + } + } + + /* look for WeeChat command */ + for (i = 0; weechat_commands[i].command_name; i++) + { + if (ascii_strcasecmp (weechat_commands[i].command_name, command + 1) == 0) + { + if ((argc < weechat_commands[i].min_arg) + || (argc > weechat_commands[i].max_arg)) + { + if (weechat_commands[i].min_arg == + weechat_commands[i].max_arg) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + NG_("%s wrong argument count for %s command \"%s\" " + "(expected: %d arg)\n", + "%s wrong argument count for %s command \"%s\" " + "(expected: %d args)\n", + weechat_commands[i].max_arg), + WEECHAT_ERROR, PACKAGE_NAME, + command + 1, + weechat_commands[i].max_arg); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + NG_("%s wrong argument count for %s command \"%s\" " + "(expected: between %d and %d arg)\n", + "%s wrong argument count for %s command \"%s\" " + "(expected: between %d and %d args)\n", + weechat_commands[i].max_arg), + WEECHAT_ERROR, PACKAGE_NAME, + command + 1, + weechat_commands[i].min_arg, + weechat_commands[i].max_arg); + } + } + else + { + ptr_args2 = (ptr_args) ? (char *)gui_color_encode ((unsigned char *)ptr_args, + (weechat_commands[i].conversion + && cfg_irc_colors_send)) : NULL; + if (weechat_commands[i].cmd_function_args) + { + argv2 = explode_string ((ptr_args2) ? ptr_args2 : ptr_args, + " ", 0, &argc2); + return_code = (int) (weechat_commands[i].cmd_function_args) + (server, channel, argc2, argv2); + free_exploded_string (argv2); + } + else + return_code = (int) (weechat_commands[i].cmd_function_1arg) + (server, channel, (ptr_args2) ? ptr_args2 : ptr_args); + if (return_code < 0) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s command \"%s\" failed\n"), + WEECHAT_ERROR, command + 1); + } + if (ptr_args2) + free (ptr_args2); + } + free_exploded_string (argv); + free (command); + return 1; + } + } + + /* look for IRC command */ + for (i = 0; irc_commands[i].command_name; i++) + { + if ((ascii_strcasecmp (irc_commands[i].command_name, command + 1) == 0) && + ((irc_commands[i].cmd_function_args) || + (irc_commands[i].cmd_function_1arg))) + { + if ((argc < irc_commands[i].min_arg) + || (argc > irc_commands[i].max_arg)) + { + if (irc_commands[i].min_arg == irc_commands[i].max_arg) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf + (NULL, + NG_("%s wrong argument count for IRC command \"%s\" " + "(expected: %d arg)\n", + "%s wrong argument count for IRC command \"%s\" " + "(expected: %d args)\n", + irc_commands[i].max_arg), + WEECHAT_ERROR, + command + 1, + irc_commands[i].max_arg); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf + (NULL, + NG_("%s wrong argument count for IRC command \"%s\" " + "(expected: between %d and %d arg)\n", + "%s wrong argument count for IRC command \"%s\" " + "(expected: between %d and %d args)\n", + irc_commands[i].max_arg), + WEECHAT_ERROR, + command + 1, + irc_commands[i].min_arg, irc_commands[i].max_arg); + } + } + else + { + if ((irc_commands[i].needs_connection) && + ((!server) || (!server->is_connected))) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s command \"%s\" needs a server connection!\n"), + WEECHAT_ERROR, irc_commands[i].command_name); + free (command); + return 0; + } + if (channel && channel->dcc_chat) + { + irc_display_prefix (server, channel->buffer, GUI_PREFIX_ERROR); + gui_printf (channel->buffer, + _("%s command \"%s\" can not be " + "executed on DCC CHAT buffer\n"), + WEECHAT_ERROR, + irc_commands[i].command_name); + free (command); + return 0; + } + ptr_args2 = (ptr_args) ? (char *)gui_color_encode ((unsigned char *)ptr_args, + (irc_commands[i].conversion + && cfg_irc_colors_send)) : NULL; + if (irc_commands[i].cmd_function_args) + { + argv2 = explode_string ((ptr_args2) ? ptr_args2 : ptr_args, + " ", 0, &argc2); + return_code = (int) (irc_commands[i].cmd_function_args) + (server, channel, argc2, argv2); + free_exploded_string (argv2); + } + else + return_code = (int) (irc_commands[i].cmd_function_1arg) + (server, channel, (ptr_args2) ? ptr_args2 : ptr_args); + if (return_code < 0) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s command \"%s\" failed\n"), + WEECHAT_ERROR, command + 1); + } + if (ptr_args2) + free (ptr_args2); + } + free_exploded_string (argv); + free (command); + return 1; + } + } + + /* should we send unknown command to IRC server? */ + if (cfg_irc_send_unknown_commands) + { + if (ptr_args) + unknown_command = (char *)malloc (strlen (command + 1) + 1 + strlen (ptr_args) + 1); + else + unknown_command = (char *)malloc (strlen (command + 1) + 1); + + if (unknown_command) + { + strcpy (unknown_command, command + 1); + if (ptr_args) + { + strcat (unknown_command, " "); + strcat (unknown_command, ptr_args); + } + irc_send_cmd_quote (server, channel, unknown_command); + free (unknown_command); + } + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unknown command \"%s\" (type /help for help). " + "To send unknown commands to IRC server, enable option " + "irc_send_unknown_commands.\n"), + WEECHAT_ERROR, + command + 1); + } + + free_exploded_string (argv); + } + free (command); + return 0; +} + +/* + * user_message_display: display user message + */ + +void +user_message_display (t_irc_server *server, t_gui_buffer *buffer, char *text) +{ + t_irc_nick *ptr_nick; + + if ((GUI_CHANNEL(buffer)->type == IRC_CHANNEL_TYPE_PRIVATE) + || (GUI_CHANNEL(buffer)->type == IRC_CHANNEL_TYPE_DCC_CHAT)) + { + irc_display_nick (buffer, NULL, server->nick, + GUI_MSG_TYPE_NICK, 1, GUI_COLOR_WIN_NICK_SELF, 0); + gui_printf_type (buffer, + GUI_MSG_TYPE_MSG, + "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + text); + } + else + { + ptr_nick = irc_nick_search (GUI_CHANNEL(buffer), server->nick); + if (ptr_nick) + { + irc_display_nick (buffer, ptr_nick, NULL, + GUI_MSG_TYPE_NICK, 1, -1, 0); + gui_printf_type (buffer, + GUI_MSG_TYPE_MSG, + "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + text); + } + else + { + irc_display_prefix (server, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s cannot find nick for sending message\n"), + WEECHAT_ERROR); + } + } +} + +/* + * user_message: send a PRIVMSG message, and split it if > 512 bytes + */ + +void +user_message (t_irc_server *server, t_gui_buffer *buffer, char *text) +{ + int max_length; + char *pos, *pos_next, *pos_max, *next, saved_char, *last_space; + + if (!text || !text[0]) + return; + + if (!server->is_connected) + { + irc_display_prefix (server, buffer, GUI_PREFIX_ERROR); + gui_printf (buffer, _("%s you are not connected to server\n"), + WEECHAT_ERROR); + return; + } + + next = NULL; + last_space = NULL; + saved_char = '\0'; + + max_length = 512 - 16 - 65 - 10 - strlen (server->nick) - + strlen (GUI_CHANNEL(buffer)->name); + + if (max_length > 0) + { + if ((int)strlen (text) > max_length) + { + pos = text; + pos_max = text + max_length; + while (pos && pos[0]) + { + if (pos[0] == ' ') + last_space = pos; + pos_next = utf8_next_char (pos); + if (pos_next > pos_max) + break; + pos = pos_next; + } + if (last_space && (last_space < pos)) + pos = last_space + 1; + saved_char = pos[0]; + pos[0] = '\0'; + next = pos; + } + } + + irc_server_sendf_queued (server, "PRIVMSG %s :%s", + GUI_CHANNEL(buffer)->name, text); + user_message_display (server, buffer, text); + + if (next) + { + next[0] = saved_char; + user_message (server, buffer, next); + } +} + +/* + * is_command: return 1 if line is a command, 0 otherwise + */ + +int +is_command (char *line) +{ + char *pos_slash, *pos_space; + + if (strncmp (line, "/*", 2) == 0) + return 0; + + pos_slash = strchr (line + 1, '/'); + pos_space = strchr (line + 1, ' '); + + return (line[0] == '/') + && (!pos_slash || (pos_space && pos_slash > pos_space)); +} + +/* + * user_command: interprets user command (if beginning with '/') + * any other text is sent to the server, if connected + */ + +void +user_command (t_irc_server *server, t_irc_channel *channel, char *command, int only_builtin) +{ + t_gui_buffer *buffer; + char *new_cmd, *ptr_cmd, *pos; + char *command_with_colors; + + if ((!command) || (!command[0]) || (command[0] == '\r') || (command[0] == '\n')) + return; + +#ifdef PLUGINS + new_cmd = plugin_modifier_exec (PLUGIN_MODIFIER_IRC_USER, + (server) ? server->name : "", + command); +#else + new_cmd = NULL; +#endif + + /* no changes in new command */ + if (new_cmd && (strcmp (command, new_cmd) == 0)) + { + free (new_cmd); + new_cmd = NULL; + } + + /* message not dropped? */ + if (!new_cmd || new_cmd[0]) + { + /* use new command (returned by plugin) */ + ptr_cmd = (new_cmd) ? new_cmd : command; + + while (ptr_cmd && ptr_cmd[0]) + { + pos = strchr (ptr_cmd, '\n'); + if (pos) + pos[0] = '\0'; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (is_command (ptr_cmd)) + { + /* WeeChat internal command (or IRC command) */ + (void) exec_weechat_command (server, channel, ptr_cmd, only_builtin); + } + else + { + if ((ptr_cmd[0] == '/') && (ptr_cmd[1] == '/')) + ptr_cmd++; + + if (server && (!GUI_BUFFER_IS_SERVER(buffer))) + { + command_with_colors = (char *)gui_color_encode ((unsigned char *)ptr_cmd, + cfg_irc_colors_send); + + if (GUI_CHANNEL(buffer)->dcc_chat) + { + if (((t_irc_dcc *)(GUI_CHANNEL(buffer)->dcc_chat))->sock < 0) + { + irc_display_prefix (server, buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (buffer, "%s DCC CHAT is closed\n", + WEECHAT_ERROR); + } + else + { + irc_dcc_chat_sendf ((t_irc_dcc *)(GUI_CHANNEL(buffer)->dcc_chat), + "%s\r\n", + (command_with_colors) ? command_with_colors : ptr_cmd); + user_message_display (server, buffer, + (command_with_colors) ? + command_with_colors : ptr_cmd); + } + } + else + user_message (server, buffer, + (command_with_colors) ? command_with_colors : ptr_cmd); + + if (command_with_colors) + free (command_with_colors); + } + else + { + irc_display_prefix (NULL, (server) ? server->buffer : NULL, GUI_PREFIX_ERROR); + gui_printf_nolog ((server) ? server->buffer : NULL, + _("This window is not a channel!\n")); + } + } + + if (pos) + { + pos[0] = '\n'; + ptr_cmd = pos + 1; + } + else + ptr_cmd = NULL; + } + } +} + +/* + * weechat_cmd_alias: display or create alias + */ + +int +weechat_cmd_alias (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + char *pos; + t_weechat_alias *ptr_alias; + + /* make C compiler happy */ + (void) server; + (void) channel; + + if (arguments && arguments[0]) + { + while (arguments[0] == '/') + { + arguments++; + } + + /* Define new alias */ + pos = strchr (arguments, ' '); + if (pos) + { + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + if (!pos[0]) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s missing arguments for \"%s\" command\n"), + WEECHAT_ERROR, "alias"); + return -1; + } + if (!alias_new (arguments, pos)) + return -1; + if (weelist_add (&index_commands, &last_index_command, arguments, + WEELIST_POS_SORT)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("Alias \"%s\" => \"%s\" created\n"), + arguments, pos); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("Failed to create alias \"%s\" => \"%s\" " + "(not enough memory)\n"), + arguments, pos); + return -1; + } + } + else + { + ptr_alias = alias_search (arguments); + if (ptr_alias) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, _("Alias:\n")); + gui_printf (NULL, " %s %s=>%s %s\n", + ptr_alias->alias_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ptr_alias->alias_command); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("No alias found.\n")); + } + } + } + else + { + /* List all aliases */ + if (weechat_alias) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, _("List of aliases:\n")); + for (ptr_alias = weechat_alias; ptr_alias; + ptr_alias = ptr_alias->next_alias) + { + gui_printf (NULL, " %s %s=>%s %s\n", + ptr_alias->alias_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ptr_alias->alias_command); + } + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("No alias defined.\n")); + } + } + return 0; +} + +/* + * weechat_cmd_buffer_display_info: display info about a buffer + */ + +void +weechat_cmd_buffer_display_info (t_gui_buffer *buffer) +{ + switch (buffer->type) + { + case GUI_BUFFER_TYPE_STANDARD: + if (GUI_BUFFER_IS_SERVER(buffer)) + { + if (GUI_SERVER(buffer)) + gui_printf (NULL, _("%sServer: %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + GUI_SERVER(buffer)->name); + else + gui_printf (NULL, _("%snot connected\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + } + else if (GUI_BUFFER_IS_CHANNEL (buffer)) + gui_printf (NULL, _("%sChannel: %s%s %s(server: %s%s%s)\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + GUI_CHANNEL(buffer)->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + GUI_SERVER(buffer)->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + else if (GUI_BUFFER_IS_PRIVATE (buffer)) + gui_printf (NULL, _("%sPrivate with: %s%s %s(server: %s%s%s)\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + GUI_CHANNEL(buffer)->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + GUI_SERVER(buffer)->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + else + gui_printf (NULL, _("%sunknown\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + break; + case GUI_BUFFER_TYPE_DCC: + gui_printf (NULL, "%sDCC\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL)); + break; + case GUI_BUFFER_TYPE_RAW_DATA: + gui_printf (NULL, _("%sraw IRC data\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL)); + break; + default: + gui_printf (NULL, _("%sunknown\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + break; + } +} + +/* + * weechat_cmd_buffer: manage buffers + */ + +int +weechat_cmd_buffer (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_window *window; + t_gui_buffer *buffer, *ptr_buffer; + t_irc_server *ptr_server; + t_irc_channel *ptr_channel; + long number; + char *error, *pos, **argv; + int argc, target_buffer, count; + + gui_buffer_find_context (server, channel, &window, &buffer); + + argv = explode_string (arguments, " ", 0, &argc); + + if ((argc == 0) || ((argc == 1) && (ascii_strcasecmp (argv[0], "list") == 0))) + { + /* list open buffers */ + + gui_printf (NULL, "\n"); + gui_printf (NULL, _("Open buffers:\n")); + + for (ptr_buffer = gui_buffers; ptr_buffer; ptr_buffer = ptr_buffer->next_buffer) + { + gui_printf (NULL, "%s[%s%d%s] ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ptr_buffer->number, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + weechat_cmd_buffer_display_info (ptr_buffer); + } + } + else + { + if (ascii_strcasecmp (argv[0], "move") == 0) + { + /* move buffer to another number in the list */ + + if (argc < 2) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s missing arguments for \"%s\" command\n"), + WEECHAT_ERROR, "buffer"); + free_exploded_string (argv); + return -1; + } + + error = NULL; + number = strtol (((argv[1][0] == '+') || (argv[1][0] == '-')) ? argv[1] + 1 : argv[1], + &error, 10); + if ((error) && (error[0] == '\0')) + { + if (argv[1][0] == '+') + gui_buffer_move_to_number (buffer, + buffer->number + ((int) number)); + else if (argv[1][0] == '-') + gui_buffer_move_to_number (buffer, + buffer->number - ((int) number)); + else + gui_buffer_move_to_number (buffer, (int) number); + } + else + { + /* invalid number */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s incorrect buffer number\n"), + WEECHAT_ERROR); + free_exploded_string (argv); + return -1; + } + } + else if (ascii_strcasecmp (argv[0], "close") == 0) + { + /* close buffer (server or channel/private) */ + + if ((!buffer->next_buffer) + && (buffer == gui_buffers) + && ((!buffer->all_servers) + || (!GUI_SERVER(buffer)))) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s can not close the single buffer\n"), + WEECHAT_ERROR); + free_exploded_string (argv); + return -1; + } + if (GUI_BUFFER_IS_SERVER(buffer)) + { + if (GUI_SERVER(buffer)) + { + if (GUI_SERVER(buffer)->channels) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s can not close server buffer while channels " + "are open\n"), + WEECHAT_ERROR); + free_exploded_string (argv); + return -1; + } + irc_server_disconnect (GUI_SERVER(buffer), 0); + ptr_server = GUI_SERVER(buffer); + if (!buffer->all_servers) + { + gui_buffer_free (buffer, 1); + ptr_server->buffer = NULL; + } + else + { + ptr_server->buffer = NULL; + buffer->server = NULL; + gui_window_switch_server (window); + } + gui_status_draw (gui_current_window->buffer, 1); + gui_input_draw (gui_current_window->buffer, 1); + } + } + else + { + if (GUI_CHANNEL(buffer) + && (GUI_CHANNEL(buffer)->type == IRC_CHANNEL_TYPE_DCC_CHAT)) + { + ptr_server = GUI_SERVER(buffer); + ptr_channel = GUI_CHANNEL(buffer); + gui_buffer_free (ptr_channel->buffer, 1); + irc_channel_free (ptr_server, ptr_channel); + gui_status_draw (gui_current_window->buffer, 1); + gui_input_draw (gui_current_window->buffer, 1); + } + else + { + if (GUI_SERVER(buffer)) + { + if (GUI_SERVER(buffer)->is_connected + && GUI_CHANNEL(buffer) + && GUI_CHANNEL(buffer)->nicks) + { + pos = strstr (arguments, "close "); + if (pos) + pos += 6; + GUI_CHANNEL(buffer)->close = 1; + irc_send_cmd_part (GUI_SERVER(buffer), + GUI_CHANNEL(buffer), + pos); + } + else + { + ptr_channel = GUI_CHANNEL(buffer); + ptr_server = GUI_SERVER(buffer); + gui_buffer_free (buffer, 1); + if (ptr_channel) + irc_channel_free (ptr_server, ptr_channel); + } + } + else + gui_buffer_free (buffer, 1); + gui_status_draw (gui_current_window->buffer, 1); + } + } + } + else if (ascii_strcasecmp (argv[0], "notify") == 0) + { + if (argc < 2) + { + gui_printf (NULL, "\n"); + + /* display default notify level for all connected servers */ + gui_printf (NULL, _("Default notify levels for servers:")); + count = 0; + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (ptr_server->buffer) + { + gui_printf (NULL, " %s:%d", + ptr_server->name, + irc_server_get_default_notify_level (ptr_server)); + count++; + } + } + if (count == 0) + gui_printf (NULL, " -"); + gui_printf (NULL, "\n"); + + /* display notify level for all buffers */ + gui_printf (NULL, _("Notify levels:")); + for (ptr_buffer = gui_buffers; ptr_buffer; + ptr_buffer = ptr_buffer->next_buffer) + { + gui_printf (NULL, " %d.%s:", + ptr_buffer->number, + (ptr_buffer->type == GUI_BUFFER_TYPE_DCC) ? "DCC" : + ((ptr_buffer->type == GUI_BUFFER_TYPE_RAW_DATA) ? _("Raw IRC data") : + ((GUI_BUFFER_IS_SERVER(ptr_buffer) && GUI_SERVER(ptr_buffer)) ? GUI_SERVER(ptr_buffer)->name : + ((GUI_CHANNEL(ptr_buffer)) ? (GUI_CHANNEL(ptr_buffer)->name) : "-")))); + if ((!GUI_BUFFER_IS_CHANNEL(ptr_buffer)) + && (!GUI_BUFFER_IS_PRIVATE(ptr_buffer))) + gui_printf (NULL, "-"); + else + gui_printf (NULL, "%d", ptr_buffer->notify_level); + } + gui_printf (NULL, "\n"); + } + else + { + /* set notify level for buffer */ + error = NULL; + number = strtol (argv[1], &error, 10); + if ((error) && (error[0] == '\0')) + { + if ((number < GUI_NOTIFY_LEVEL_MIN) || (number > GUI_NOTIFY_LEVEL_MAX)) + { + /* invalid highlight level */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s incorrect notify level (must be between %d and %d)\n"), + WEECHAT_ERROR, GUI_NOTIFY_LEVEL_MIN, GUI_NOTIFY_LEVEL_MAX); + free_exploded_string (argv); + return -1; + } + if (!GUI_SERVER(buffer) + || ((!GUI_BUFFER_IS_SERVER(buffer)) + && (!GUI_BUFFER_IS_CHANNEL(buffer)) + && (!GUI_BUFFER_IS_PRIVATE(buffer)))) + { + /* invalid buffer type (only ok on server, channel or private) */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s incorrect buffer for notify " + "(must be server, channel or private)\n"), + WEECHAT_ERROR); + free_exploded_string (argv); + return -1; + } + if (GUI_BUFFER_IS_SERVER(buffer)) + { + irc_server_set_default_notify_level (GUI_SERVER(buffer), + number); + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("New default notify level for server %s%s%s: %s%d %s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + GUI_SERVER(buffer)->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + number, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + } + else + { + irc_channel_set_notify_level (GUI_SERVER(buffer), + GUI_CHANNEL(buffer), + number); + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("New notify level for %s%s%s: %s%d %s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + GUI_CHANNEL(buffer)->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + number, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + } + switch (number) + { + case 0: + gui_printf (NULL, _("(hotlist: never)\n")); + break; + case 1: + gui_printf (NULL, _("(hotlist: highlights)\n")); + break; + case 2: + gui_printf (NULL, _("(hotlist: highlights + messages)\n")); + break; + case 3: + gui_printf (NULL, _("(hotlist: highlights + messages + join/part (all))\n")); + break; + default: + gui_printf (NULL, "\n"); + break; + } + config_change_notify_levels (); + } + else + { + /* invalid number */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s incorrect notify level (must be between %d and %d)\n"), + WEECHAT_ERROR, GUI_NOTIFY_LEVEL_MIN, GUI_NOTIFY_LEVEL_MAX); + free_exploded_string (argv); + return -1; + } + } + } + else if (ascii_strcasecmp (argv[0], "scroll") == 0) + { + if (argc >= 2) + gui_buffer_scroll (window, argv[1]); + } + else + { + /* jump to buffer by number or server/channel name */ + + if (argv[0][0] == '-') + { + /* relative jump '-' */ + error = NULL; + number = strtol (argv[0] + 1, &error, 10); + if ((error) && (error[0] == '\0')) + { + target_buffer = buffer->number - (int) number; + if (target_buffer < 1) + target_buffer = (last_gui_buffer) ? + last_gui_buffer->number + target_buffer : 1; + gui_buffer_switch_by_number (window, + target_buffer); + } + } + else if (argv[0][0] == '+') + { + /* relative jump '+' */ + error = NULL; + number = strtol (argv[0] + 1, &error, 10); + if ((error) && (error[0] == '\0')) + { + target_buffer = buffer->number + (int) number; + if (last_gui_buffer && target_buffer > last_gui_buffer->number) + target_buffer -= last_gui_buffer->number; + gui_buffer_switch_by_number (window, + target_buffer); + } + } + else + { + /* absolute jump by number, or by server/channel name */ + error = NULL; + number = strtol (argv[0], &error, 10); + if ((error) && (error[0] == '\0')) + gui_buffer_switch_by_number (window, (int) number); + else + { + ptr_buffer = NULL; + if (argc > 1) + ptr_buffer = gui_buffer_search (argv[0], argv[1]); + else + { + ptr_server = irc_server_search (argv[0]); + if (ptr_server) + ptr_buffer = gui_buffer_search (argv[0], NULL); + else + ptr_buffer = gui_buffer_search (NULL, argv[0]); + } + if (ptr_buffer) + { + gui_window_switch_to_buffer (window, ptr_buffer); + gui_window_redraw_buffer (ptr_buffer); + } + } + } + + } + } + free_exploded_string (argv); + return 0; +} + +/* + * weechat_cmd_builtin: launch WeeChat/IRC builtin command + */ + +int +weechat_cmd_builtin (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + char *command; + int length; + + if (arguments && arguments[0]) + { + if (arguments[0] == '/') + user_command (server, channel, arguments, 1); + else + { + length = strlen (arguments) + 2; + command = (char *)malloc (length); + if (command) + { + snprintf (command, length, "/%s", arguments); + user_command (server, channel, command, 1); + free (command); + } + } + } + return 0; +} + +/* + * weechat_cmd_clear: display or create alias + */ + +int +weechat_cmd_clear (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + char *error; + long number; + int i; + + if (argc > 0) + { + if (ascii_strcasecmp (argv[0], "-all") == 0) + gui_buffer_clear_all (); + else + { + for (i = 0; i < argc; i++) + { + error = NULL; + number = strtol (argv[i], &error, 10); + if ((error) && (error[0] == '\0')) + { + buffer = gui_buffer_search_by_number (number); + if (!buffer) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s buffer number \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, argv[i], "clear"); + return -1; + } + gui_buffer_clear (buffer); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unknown option for \"%s\" command\n"), + WEECHAT_ERROR, "clear"); + return -1; + } + } + } + } + else + { + gui_buffer_find_context (server, channel, NULL, &buffer); + gui_buffer_clear (buffer); + } + + return 0; +} + +/* + * weechat_cmd_connect_one_server: connect to one server + * return 0 if error, 1 if ok + */ + +int +weechat_cmd_connect_one_server (t_gui_window *window, t_irc_server *server, + int no_join) +{ + if (server->is_connected) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s already connected to server \"%s\"!\n"), + WEECHAT_ERROR, server->name); + return 0; + } + if (server->child_pid > 0) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s currently connecting to server \"%s\"!\n"), + WEECHAT_ERROR, server->name); + return 0; + } + if (!server->buffer) + { + if (!gui_buffer_new (window, server, NULL, + GUI_BUFFER_TYPE_STANDARD, 1)) + return 0; + } + if (irc_server_connect (server, no_join)) + { + server->reconnect_start = 0; + server->reconnect_join = (server->channels) ? 1 : 0; + gui_status_draw (server->buffer, 1); + } + + /* connect ok */ + return 1; +} + +/* + * weechat_cmd_connect: connect to server(s) + */ + +int +weechat_cmd_connect (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_window *window; + t_gui_buffer *buffer; + t_irc_server *ptr_server, server_tmp; + int i, nb_connect, connect_ok, all_servers, no_join, port, ipv6, ssl; + char *error; + long number; + + gui_buffer_find_context (server, channel, &window, &buffer); + + nb_connect = 0; + connect_ok = 1; + port = IRC_DEFAULT_PORT; + ipv6 = 0; + ssl = 0; + + all_servers = 0; + no_join = 0; + for (i = 0; i < argc; i++) + { + if (ascii_strcasecmp (argv[i], "-all") == 0) + all_servers = 1; + if (ascii_strcasecmp (argv[i], "-nojoin") == 0) + no_join = 1; + if (ascii_strcasecmp (argv[i], "-ipv6") == 0) + ipv6 = 1; + if (ascii_strcasecmp (argv[i], "-ssl") == 0) + ssl = 1; + if (ascii_strcasecmp (argv[i], "-port") == 0) + { + if (i == (argc - 1)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing argument for \"%s\" option\n"), + WEECHAT_ERROR, "-port"); + return -1; + } + error = NULL; + number = strtol (argv[++i], &error, 10); + if ((error) && (error[0] == '\0')) + port = number; + } + } + + if (all_servers) + { + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + nb_connect++; + if (!ptr_server->is_connected && (ptr_server->child_pid == 0)) + { + if (!weechat_cmd_connect_one_server (window, ptr_server, + no_join)) + connect_ok = 0; + } + } + } + else + { + for (i = 0; i < argc; i++) + { + if (argv[i][0] != '-') + { + nb_connect++; + ptr_server = irc_server_search (argv[i]); + if (ptr_server) + { + if (!weechat_cmd_connect_one_server (window, ptr_server, + no_join)) + connect_ok = 0; + } + else + { + irc_server_init (&server_tmp); + server_tmp.name = strdup (argv[i]); + server_tmp.address = strdup (argv[i]); + server_tmp.port = port; + server_tmp.ipv6 = ipv6; + server_tmp.ssl = ssl; + ptr_server = irc_server_new (server_tmp.name, + server_tmp.autoconnect, + server_tmp.autoreconnect, + server_tmp.autoreconnect_delay, + 1, /* temp server */ + server_tmp.address, + server_tmp.port, + server_tmp.ipv6, + server_tmp.ssl, + server_tmp.password, + server_tmp.nick1, + server_tmp.nick2, + server_tmp.nick3, + server_tmp.username, + server_tmp.realname, + server_tmp.hostname, + server_tmp.command, + 1, /* command_delay */ + server_tmp.autojoin, + 1, /* autorejoin */ + NULL); + if (ptr_server) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("Server %s%s%s created (temporary server, NOT SAVED!)\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + server_tmp.name, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + if (!weechat_cmd_connect_one_server (window, ptr_server, 0)) + connect_ok = 0; + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unable to create server \"%s\"\n"), + WEECHAT_ERROR, argv[i]); + } + } + } + else + { + if (ascii_strcasecmp (argv[i], "-port") == 0) + i++; + } + } + } + + if (nb_connect == 0) + connect_ok = weechat_cmd_connect_one_server (window, server, no_join); + + if (!connect_ok) + return -1; + + return 0; +} + +/* + * weechat_cmd_dcc: DCC control (file or chat) + */ + +int +weechat_cmd_dcc (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_buffer *buffer; + char *pos_nick, *pos_file; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + /* DCC SEND file */ + if (strncasecmp (arguments, "send", 4) == 0) + { + pos_nick = strchr (arguments, ' '); + if (!pos_nick) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s wrong argument count for \"%s\" command\n"), + WEECHAT_ERROR, "dcc send"); + return -1; + } + while (pos_nick[0] == ' ') + pos_nick++; + + pos_file = strchr (pos_nick, ' '); + if (!pos_file) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s wrong argument count for \"%s\" command\n"), + WEECHAT_ERROR, "dcc send"); + return -1; + } + pos_file[0] = '\0'; + pos_file++; + while (pos_file[0] == ' ') + pos_file++; + + irc_dcc_send_request (server, IRC_DCC_FILE_SEND, pos_nick, pos_file); + } + /* DCC CHAT */ + else if (strncasecmp (arguments, "chat", 4) == 0) + { + pos_nick = strchr (arguments, ' '); + if (!pos_nick) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s wrong argument count for \"%s\" command\n"), + WEECHAT_ERROR, "dcc chat"); + return -1; + } + while (pos_nick[0] == ' ') + pos_nick++; + + irc_dcc_send_request (server, IRC_DCC_CHAT_SEND, pos_nick, NULL); + } + /* close DCC CHAT */ + else if (ascii_strcasecmp (arguments, "close") == 0) + { + if (GUI_BUFFER_IS_PRIVATE(buffer) && + GUI_CHANNEL(buffer)->dcc_chat) + { + irc_dcc_close ((t_irc_dcc *)(GUI_CHANNEL(buffer)->dcc_chat), + IRC_DCC_ABORTED); + irc_dcc_redraw (1); + } + } + /* unknown DCC action */ + else + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (server->buffer, + _("%s wrong arguments for \"%s\" command\n"), + WEECHAT_ERROR, "dcc"); + return -1; + } + + return 0; +} + +/* + * weechat_cmd_debug_display_windows: display tree of windows + */ + +void +weechat_cmd_debug_display_windows (t_gui_window_tree *tree, int indent) +{ + int i; + + if (tree) + { + for (i = 0; i < indent; i++) + gui_printf_nolog (NULL, " "); + + if (tree->window) + { + /* leaf */ + gui_printf_nolog (NULL, "leaf: %X (parent:%X), win=%X, child1=%X, child2=%X, %d,%d %dx%d, %d%%x%d%%\n", + tree, tree->parent_node, tree->window, + tree->child1, tree->child2, + tree->window->win_x, tree->window->win_y, + tree->window->win_width, tree->window->win_height, + tree->window->win_width_pct, tree->window->win_height_pct); + } + else + { + /* node */ + gui_printf_nolog (NULL, "node: %X (parent:%X), win=%X, child1=%X, child2=%X)\n", + tree, tree->parent_node, tree->window, + tree->child1, tree->child2); + } + + if (tree->child1) + weechat_cmd_debug_display_windows (tree->child1, indent + 1); + if (tree->child2) + weechat_cmd_debug_display_windows (tree->child2, indent + 1); + } +} + +/* + * weechat_cmd_debug: print debug messages + */ + +int +weechat_cmd_debug (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + + /* make C compiler happy */ + (void) server; + (void) channel; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (argc != 1) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s wrong argument count for \"%s\" command\n"), + WEECHAT_ERROR, "debug"); + return -1; + } + + if (ascii_strcasecmp (argv[0], "dump") == 0) + { + weechat_dump (0); + } + else if (ascii_strcasecmp (argv[0], "buffer") == 0) + { + gui_buffer_dump_hexa (buffer); + } + else if (ascii_strcasecmp (argv[0], "windows") == 0) + { + gui_printf_nolog (NULL, "\n"); + gui_printf_nolog (NULL, "DEBUG: windows tree:\n"); + weechat_cmd_debug_display_windows (gui_windows_tree, 1); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unknown option for \"%s\" command\n"), + WEECHAT_ERROR, "debug"); + return -1; + } + + return 0; +} + +/* + * weechat_cmd_disconnect_one_server: disconnect from a server + * return 0 if error, 1 if ok + */ + +int +weechat_cmd_disconnect_one_server (t_irc_server *server) +{ + if ((!server->is_connected) && (server->child_pid == 0) + && (server->reconnect_start == 0)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s not connected to server \"%s\"!\n"), + WEECHAT_ERROR, server->name); + return 0; + } + if (server->reconnect_start > 0) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_INFO); + gui_printf (server->buffer, + _("Auto-reconnection is cancelled\n")); + } + irc_send_quit_server (server, NULL); + irc_server_disconnect (server, 0); + gui_status_draw (server->buffer, 1); + + /* disconnect ok */ + return 1; +} + +/* + * weechat_cmd_disconnect: disconnect from server(s) + */ + +int +weechat_cmd_disconnect (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + t_irc_server *ptr_server; + int i, disconnect_ok; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + if (argc == 0) + disconnect_ok = weechat_cmd_disconnect_one_server (server); + else + { + disconnect_ok = 1; + + if (ascii_strcasecmp (argv[0], "-all") == 0) + { + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if ((ptr_server->is_connected) || (ptr_server->child_pid != 0) + || (ptr_server->reconnect_start != 0)) + { + if (!weechat_cmd_disconnect_one_server (ptr_server)) + disconnect_ok = 0; + } + } + } + else + { + for (i = 0; i < argc; i++) + { + ptr_server = irc_server_search (argv[i]); + if (ptr_server) + { + if (!weechat_cmd_disconnect_one_server (ptr_server)) + disconnect_ok = 0; + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s server \"%s\" not found\n"), + WEECHAT_ERROR, argv[i]); + disconnect_ok = 0; + } + } + } + } + + if (!disconnect_ok) + return -1; + + return 0; +} + +/* + * weechat_cmd_help: display help + */ + +int +weechat_cmd_help (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + int i; +#ifdef PLUGINS + t_weechat_plugin *ptr_plugin; + t_plugin_handler *ptr_handler; +#endif + + /* make C compiler happy */ + (void) server; + (void) channel; + + switch (argc) + { + case 0: + gui_printf (NULL, "\n"); + gui_printf (NULL, _("%s internal commands:\n"), PACKAGE_NAME); + for (i = 0; weechat_commands[i].command_name; i++) + { + gui_printf (NULL, " %s%s %s- %s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + weechat_commands[i].command_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + _(weechat_commands[i].command_description)); + } + gui_printf (NULL, "\n"); + gui_printf (NULL, _("IRC commands:\n")); + for (i = 0; irc_commands[i].command_name; i++) + { + if (irc_commands[i].cmd_function_args || irc_commands[i].cmd_function_1arg) + { + gui_printf (NULL, " %s%s %s- %s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + irc_commands[i].command_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + _(irc_commands[i].command_description)); + } + } +#ifdef PLUGINS + gui_printf (NULL, "\n"); + gui_printf (NULL, _("Plugin commands:\n")); + for (ptr_plugin = weechat_plugins; ptr_plugin; + ptr_plugin = ptr_plugin->next_plugin) + { + for (ptr_handler = ptr_plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if (ptr_handler->type == PLUGIN_HANDLER_COMMAND) + { + gui_printf (NULL, " %s%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_handler->command); + if (ptr_handler->description + && ptr_handler->description[0]) + gui_printf (NULL, " %s- %s", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ptr_handler->description); + gui_printf (NULL, "\n"); + } + } + } +#endif + break; + case 1: +#ifdef PLUGINS + for (ptr_plugin = weechat_plugins; ptr_plugin; + ptr_plugin = ptr_plugin->next_plugin) + { + for (ptr_handler = ptr_plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if ((ptr_handler->type == PLUGIN_HANDLER_COMMAND) + && (ascii_strcasecmp (ptr_handler->command, argv[0]) == 0)) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, "[p]"); + gui_printf (NULL, " %s/%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_handler->command); + if (ptr_handler->arguments && + ptr_handler->arguments[0]) + gui_printf (NULL, " %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ptr_handler->arguments); + else + gui_printf (NULL, "\n"); + if (ptr_handler->description && + ptr_handler->description[0]) + gui_printf (NULL, "\n%s\n", + ptr_handler->description); + if (ptr_handler->arguments_description && + ptr_handler->arguments_description[0]) + gui_printf (NULL, "\n%s\n", + ptr_handler->arguments_description); + return 0; + } + } + } +#endif + for (i = 0; weechat_commands[i].command_name; i++) + { + if (ascii_strcasecmp (weechat_commands[i].command_name, argv[0]) == 0) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, "[w]"); + gui_printf (NULL, " %s/%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + weechat_commands[i].command_name); + if (weechat_commands[i].arguments && + weechat_commands[i].arguments[0]) + gui_printf (NULL, " %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + _(weechat_commands[i].arguments)); + else + gui_printf (NULL, "\n"); + if (weechat_commands[i].command_description && + weechat_commands[i].command_description[0]) + gui_printf (NULL, "\n%s\n", + _(weechat_commands[i].command_description)); + if (weechat_commands[i].arguments_description && + weechat_commands[i].arguments_description[0]) + gui_printf (NULL, "\n%s\n", + _(weechat_commands[i].arguments_description)); + return 0; + } + } + for (i = 0; irc_commands[i].command_name; i++) + { + if ((ascii_strcasecmp (irc_commands[i].command_name, argv[0]) == 0) + && (irc_commands[i].cmd_function_args || irc_commands[i].cmd_function_1arg)) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, "[i]"); + gui_printf (NULL, " %s/%s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + irc_commands[i].command_name); + if (irc_commands[i].arguments && + irc_commands[i].arguments[0]) + gui_printf (NULL, " %s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + _(irc_commands[i].arguments)); + else + gui_printf (NULL, "\n"); + if (irc_commands[i].command_description && + irc_commands[i].command_description[0]) + gui_printf (NULL, "\n%s\n", + _(irc_commands[i].command_description)); + if (irc_commands[i].arguments_description && + irc_commands[i].arguments_description[0]) + gui_printf (NULL, "\n%s\n", + _(irc_commands[i].arguments_description)); + return 0; + } + } + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("No help available, \"%s\" is an unknown command\n"), + argv[0]); + break; + } + return 0; +} + +/* + * weechat_cmd_history: display current buffer history + */ + +int +weechat_cmd_history (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + t_history *ptr_history; + int n; + int n_total; + int n_user; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + n_user = cfg_history_display_default; + + if (argc == 1) + { + if (ascii_strcasecmp (argv[0], "clear") == 0) + { + history_buffer_free (buffer); + return 0; + } + else + n_user = atoi (argv[0]); + } + + if (buffer->history) + { + n_total = 1; + for (ptr_history = buffer->history; + ptr_history->next_history; + ptr_history = ptr_history->next_history) + { + n_total++; + } + for (n = 0; ptr_history; ptr_history = ptr_history->prev_history, n++) + { + if ((n_user > 0) && ((n_total - n_user) > n)) + continue; + irc_display_prefix (NULL, buffer, GUI_PREFIX_INFO); + gui_printf_nolog (buffer, "%s\n", ptr_history->text); + } + } + + return 0; +} + +/* + * weechat_cmd_ignore_display: display an ignore entry + */ + +void +weechat_cmd_ignore_display (char *text, t_irc_ignore *ptr_ignore) +{ + if (text) + gui_printf (NULL, "%s%s ", + GUI_COLOR(GUI_COLOR_WIN_CHAT), + text); + + gui_printf (NULL, _("%son %s%s%s/%s%s%s:%s ignoring %s%s%s from %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_ignore->server_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_ignore->channel_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_ignore->type, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + ptr_ignore->mask); +} + +/* + * weechat_cmd_ignore: ignore IRC commands and/or hosts + */ + +int +weechat_cmd_ignore (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + t_irc_ignore *ptr_ignore; + int i; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + ptr_ignore = NULL; + switch (argc) + { + case 0: + /* List all ignore */ + if (irc_ignore) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, _("List of ignore:\n")); + i = 0; + for (ptr_ignore = irc_ignore; ptr_ignore; + ptr_ignore = ptr_ignore->next_ignore) + { + i++; + gui_printf (NULL, "%s[%s%d%s] ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + i, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + weechat_cmd_ignore_display (NULL, ptr_ignore); + } + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("No ignore defined.\n")); + } + return 0; + break; + case 1: + ptr_ignore = irc_ignore_add (argv[0], "*", "*", + (GUI_SERVER(buffer)) ? + GUI_SERVER(buffer)->name : "*"); + break; + case 2: + ptr_ignore = irc_ignore_add (argv[0], argv[1], "*", + (GUI_SERVER(buffer)) ? + GUI_SERVER(buffer)->name : "*"); + break; + case 3: + ptr_ignore = irc_ignore_add (argv[0], argv[1], argv[2], + (GUI_SERVER(buffer)) ? + GUI_SERVER(buffer)->name : "*"); + break; + case 4: + ptr_ignore = irc_ignore_add (argv[0], argv[1], argv[2], argv[3]); + break; + } + if (ptr_ignore) + { + gui_printf (NULL, "\n"); + weechat_cmd_ignore_display (_("New ignore:"), ptr_ignore); + return 0; + } + else + return -1; +} + +/* + * weechat_cmd_key_display: display a key binding + */ + +void +weechat_cmd_key_display (t_gui_key *key, int new_key) +{ + char *expanded_name; + + expanded_name = gui_keyboard_get_expanded_name (key->key); + if (new_key) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("New key binding: %s"), + (expanded_name) ? expanded_name : key->key); + } + else + gui_printf (NULL, " %20s", (expanded_name) ? expanded_name : key->key); + gui_printf (NULL, "%s => %s%s%s%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + (key->function) ? + gui_keyboard_function_search_by_ptr (key->function) : key->command, + (key->args) ? " \"" : "", + (key->args) ? key->args : "", + (key->args) ? "\"" : ""); + if (expanded_name) + free (expanded_name); +} + +/* + * weechat_cmd_key: bind/unbind keys + */ + +int +weechat_cmd_key (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_gui_window *window; + t_gui_buffer *buffer; + char *pos, *pos_args, *args_tmp, *internal_code; + int i, length; + t_gui_key *ptr_key; + void (*ptr_function)(t_gui_window *, char *); + + gui_buffer_find_context (server, channel, &window, &buffer); + + if (arguments) + { + while (arguments[0] == ' ') + arguments++; + } + + if (!arguments || (arguments[0] == '\0')) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, _("Key bindings:\n")); + for (ptr_key = gui_keys; ptr_key; ptr_key = ptr_key->next_key) + { + weechat_cmd_key_display (ptr_key, 0); + } + } + else if (ascii_strncasecmp (arguments, "unbind ", 7) == 0) + { + arguments += 7; + while (arguments[0] == ' ') + arguments++; + if (gui_keyboard_unbind (arguments)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("Key \"%s\" unbound\n"), arguments); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unable to unbind key \"%s\"\n"), + WEECHAT_ERROR, arguments); + return -1; + } + } + else if (ascii_strcasecmp (arguments, "functions") == 0) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, _("Internal key functions:\n")); + i = 0; + while (gui_key_functions[i].function_name) + { + gui_printf (NULL, "%25s %s\n", + gui_key_functions[i].function_name, + _(gui_key_functions[i].description)); + i++; + } + } + else if (ascii_strncasecmp (arguments, "call ", 5) == 0) + { + arguments += 5; + while (arguments[0] == ' ') + arguments++; + pos = strchr (arguments, ' '); + if (pos) + pos[0] = '\0'; + ptr_function = gui_keyboard_function_search_by_name (arguments); + if (pos) + pos[0] = ' '; + if (ptr_function) + { + pos_args = pos; + args_tmp = NULL; + if (pos_args) + { + pos_args++; + while (pos_args[0] == ' ') + pos_args++; + if (pos_args[0] == '"') + { + length = strlen (pos_args); + if ((length > 1) && (pos_args[length - 1] == '"')) + args_tmp = strndup (pos_args + 1, length - 2); + else + args_tmp = strdup (pos_args); + } + else + args_tmp = strdup (pos_args); + } + (void)(*ptr_function)(window, args_tmp); + if (args_tmp) + free (args_tmp); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unknown key function \"%s\"\n"), + WEECHAT_ERROR, arguments); + return -1; + } + } + else if (ascii_strncasecmp (arguments, "reset", 5) == 0) + { + arguments += 5; + while (arguments[0] == ' ') + arguments++; + if (ascii_strcasecmp (arguments, "-yes") == 0) + { + gui_keyboard_free_all (); + gui_keyboard_init (); + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("Default key bindings restored\n")); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s \"-yes\" argument is required for keys reset (security reason)\n"), + WEECHAT_ERROR); + return -1; + } + } + else + { + while (arguments[0] == ' ') + arguments++; + pos = strchr (arguments, ' '); + if (!pos) + { + ptr_key = NULL; + internal_code = gui_keyboard_get_internal_code (arguments); + if (internal_code) + ptr_key = gui_keyboard_search (internal_code); + if (ptr_key) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, _("Key:\n")); + weechat_cmd_key_display (ptr_key, 0); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("No key found.\n")); + } + if (internal_code) + free (internal_code); + return 0; + } + pos[0] = '\0'; + pos++; + while (pos[0] == ' ') + pos++; + ptr_key = gui_keyboard_bind (arguments, pos); + if (ptr_key) + weechat_cmd_key_display (ptr_key, 1); + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unable to bind key \"%s\"\n"), + WEECHAT_ERROR, arguments); + return -1; + } + } + + return 0; +} + +/* + * weechat_cmd_panel_display_info: display infos about a panel + */ + +void +weechat_cmd_panel_display_info (t_gui_panel *panel) +{ + gui_printf (NULL, " %s%2d%s. ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + panel->number, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + gui_printf (NULL, "%s%s%s ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + panel->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + gui_printf (NULL, "(%s%s/%s", + (panel->panel_window) ? _("global") : _("local"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + switch (panel->position) + { + case GUI_PANEL_TOP: + gui_printf (NULL, "%s", _("top")); + break; + case GUI_PANEL_BOTTOM: + gui_printf (NULL, "%s", _("bottom")); + break; + case GUI_PANEL_LEFT: + gui_printf (NULL, "%s", _("left")); + break; + case GUI_PANEL_RIGHT: + gui_printf (NULL, "%s", _("right")); + break; + } + gui_printf (NULL, "%s/%s%d)\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + panel->size); +} + +/* + * weechat_cmd_panel: manage panels + */ + +int +weechat_cmd_panel (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_panel *ptr_panel; + + /* make C compiler happy */ + (void) server; + (void) channel; + + gui_printf (NULL, "\n/panel command is under development!\n"); + + if ((argc == 0) || ((argc == 1) && (ascii_strcasecmp (argv[0], "list") == 0))) + { + /* list open panels */ + + gui_printf (NULL, "\n"); + gui_printf (NULL, _("Open panels:\n")); + + for (ptr_panel = gui_panels; ptr_panel; ptr_panel = ptr_panel->next_panel) + { + weechat_cmd_panel_display_info (ptr_panel); + } + } + else + { + } + return 0; +} + +/* + * weechat_cmd_plugin_list: list loaded plugins + */ + +void +weechat_cmd_plugin_list (char *name, int full) +{ +#ifdef PLUGINS + t_weechat_plugin *ptr_plugin; + int plugins_found; + t_plugin_handler *ptr_handler; + int handler_found; + t_plugin_modifier *ptr_modifier; + int modifier_found; + + gui_printf (NULL, "\n"); + if (!name) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _("Plugins loaded:\n")); + } + + plugins_found = 0; + + for (ptr_plugin = weechat_plugins; ptr_plugin; + ptr_plugin = ptr_plugin->next_plugin) + { + if (!name || (ascii_strcasestr (ptr_plugin->name, name))) + { + plugins_found++; + + /* plugin info */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, " %s%s%s v%s - %s (%s)\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ptr_plugin->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ptr_plugin->version, + ptr_plugin->description, + ptr_plugin->filename); + + if (full) + { + /* message handlers */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _(" message handlers:\n")); + handler_found = 0; + for (ptr_handler = ptr_plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if (ptr_handler->type == PLUGIN_HANDLER_MESSAGE) + { + handler_found = 1; + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _(" IRC(%s)\n"), + ptr_handler->irc_command); + } + } + if (!handler_found) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _(" (no message handler)\n")); + } + + /* command handlers */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _(" command handlers:\n")); + handler_found = 0; + for (ptr_handler = ptr_plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if (ptr_handler->type == PLUGIN_HANDLER_COMMAND) + { + handler_found = 1; + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, " /%s", + ptr_handler->command); + if (ptr_handler->description + && ptr_handler->description[0]) + gui_printf (NULL, " (%s)", + ptr_handler->description); + gui_printf (NULL, "\n"); + } + } + if (!handler_found) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _(" (no command handler)\n")); + } + + /* timer handlers */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _(" timer handlers:\n")); + handler_found = 0; + for (ptr_handler = ptr_plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if (ptr_handler->type == PLUGIN_HANDLER_TIMER) + { + handler_found = 1; + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _(" %d seconds\n"), + ptr_handler->interval); + } + } + if (!handler_found) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _(" (no timer handler)\n")); + } + + /* keyboard handlers */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _(" keyboard handlers:\n")); + handler_found = 0; + for (ptr_handler = ptr_plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if (ptr_handler->type == PLUGIN_HANDLER_KEYBOARD) + handler_found++; + } + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + if (!handler_found) + gui_printf (NULL, _(" (no keyboard handler)\n")); + else + gui_printf (NULL, _(" %d defined\n"), + handler_found); + + /* event handlers */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _(" event handlers:\n")); + handler_found = 0; + for (ptr_handler = ptr_plugin->handlers; + ptr_handler; ptr_handler = ptr_handler->next_handler) + { + if (ptr_handler->type == PLUGIN_HANDLER_EVENT) + handler_found++; + } + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + if (!handler_found) + gui_printf (NULL, _(" (no event handler)\n")); + else + gui_printf (NULL, _(" %d defined\n"), + handler_found); + + /* modifiers */ + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + gui_printf (NULL, _(" modifiers:\n")); + modifier_found = 0; + for (ptr_modifier = ptr_plugin->modifiers; + ptr_modifier; ptr_modifier = ptr_modifier->next_modifier) + { + modifier_found++; + } + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + if (!modifier_found) + gui_printf (NULL, _(" (no modifier)\n")); + else + gui_printf (NULL, _(" %d defined\n"), + modifier_found); + } + } + } + if (plugins_found == 0) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_PLUGIN); + if (name) + gui_printf (NULL, _("No plugin found.\n")); + else + gui_printf (NULL, _(" (no plugin)\n")); + } +#else + /* make C compiler happy */ + (void) name; + (void) full; +#endif +} + +/* + * weechat_cmd_plugin: list/load/unload WeeChat plugins + */ + +int +weechat_cmd_plugin (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ +#ifdef PLUGINS + /* make C compiler happy */ + (void) server; + (void) channel; + + switch (argc) + { + case 0: + weechat_cmd_plugin_list (NULL, 0); + break; + case 1: + if (ascii_strcasecmp (argv[0], "list") == 0) + weechat_cmd_plugin_list (NULL, 0); + else if (ascii_strcasecmp (argv[0], "listfull") == 0) + weechat_cmd_plugin_list (NULL, 1); + else if (ascii_strcasecmp (argv[0], "autoload") == 0) + plugin_auto_load (); + else if (ascii_strcasecmp (argv[0], "reload") == 0) + { + plugin_unload_all (); + plugin_auto_load (); + } + else if (ascii_strcasecmp (argv[0], "unload") == 0) + plugin_unload_all (); + break; + case 2: + if (ascii_strcasecmp (argv[0], "list") == 0) + weechat_cmd_plugin_list (argv[1], 0); + else if (ascii_strcasecmp (argv[0], "listfull") == 0) + weechat_cmd_plugin_list (argv[1], 1); + else if (ascii_strcasecmp (argv[0], "load") == 0) + plugin_load (argv[1]); + else if (ascii_strcasecmp (argv[0], "reload") == 0) + plugin_reload_name (argv[1]); + else if (ascii_strcasecmp (argv[0], "unload") == 0) + plugin_unload_name (argv[1]); + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unknown option for \"%s\" command\n"), + WEECHAT_ERROR, "plugin"); + } + break; + default: + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s wrong argument count for \"%s\" command\n"), + WEECHAT_ERROR, "plugin"); + } +#else + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("Command \"%s\" is not available, WeeChat was built " + "without plugins support.\n"), + "plugin"); + /* make C compiler happy */ + (void) server; + (void) channel; + (void) argc; + (void) argv; +#endif /* PLUGINS */ + + return 0; +} + +/* + * weechat_cmd_reconnect_one_server: reconnect to a server + * return 0 if error, 1 if ok + */ + +int +weechat_cmd_reconnect_one_server (t_irc_server *server, int no_join) +{ + if ((!server->is_connected) && (server->child_pid == 0)) + { + irc_display_prefix (NULL, server->buffer, GUI_PREFIX_ERROR); + gui_printf (server->buffer, + _("%s not connected to server \"%s\"!\n"), + WEECHAT_ERROR, server->name); + return 0; + } + irc_send_quit_server (server, NULL); + irc_server_disconnect (server, 0); + if (irc_server_connect (server, no_join)) + { + server->reconnect_start = 0; + server->reconnect_join = (server->channels) ? 1 : 0; + } + gui_status_draw (server->buffer, 1); + + /* reconnect ok */ + return 1; +} + +/* + * weechat_cmd_reconnect: reconnect to server(s) + */ + +int +weechat_cmd_reconnect (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + t_irc_server *ptr_server; + int i, nb_reconnect, reconnect_ok, all_servers, no_join; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + nb_reconnect = 0; + reconnect_ok = 1; + + all_servers = 0; + no_join = 0; + for (i = 0; i < argc; i++) + { + if (ascii_strcasecmp (argv[i], "-all") == 0) + all_servers = 1; + if (ascii_strcasecmp (argv[i], "-nojoin") == 0) + no_join = 1; + } + + if (all_servers) + { + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + nb_reconnect++; + if ((ptr_server->is_connected) || (ptr_server->child_pid != 0)) + { + if (!weechat_cmd_reconnect_one_server (ptr_server, no_join)) + reconnect_ok = 0; + } + } + } + else + { + for (i = 0; i < argc; i++) + { + if (argv[i][0] != '-') + { + nb_reconnect++; + ptr_server = irc_server_search (argv[i]); + if (ptr_server) + { + if (!weechat_cmd_reconnect_one_server (ptr_server, no_join)) + reconnect_ok = 0; + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s server \"%s\" not found\n"), + WEECHAT_ERROR, argv[i]); + reconnect_ok = 0; + } + } + } + } + + if (nb_reconnect == 0) + reconnect_ok = weechat_cmd_reconnect_one_server (server, no_join); + + if (!reconnect_ok) + return -1; + + return 0; +} + +/* + * weechat_cmd_save: save WeeChat and plugins options to disk + */ + +int +weechat_cmd_save (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + /* make C compiler happy */ + (void) server; + (void) channel; + + if (config_write ((argc == 1) ? argv[0] : NULL) == 0) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf_nolog (NULL, _("Configuration file saved\n")); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf_nolog (NULL, _("%s failed to save configuration file\n"), + WEECHAT_ERROR); + } + +#ifdef PLUGINS + if (plugin_config_write () == 0) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf_nolog (NULL, _("Plugins options saved\n")); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf_nolog (NULL, _("%s failed to save plugins options\n"), + WEECHAT_ERROR); + } +#endif + + return 0; +} + +/* + * weechat_cmd_server: list, add or remove server(s) + */ + +int +weechat_cmd_server (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_window *window; + t_gui_buffer *buffer; + int i, detailed_list, one_server_found; + t_irc_server server_tmp, *ptr_server, *server_found, *new_server; + t_gui_buffer *ptr_buffer; + char *server_name, *error; + long number; + + gui_buffer_find_context (server, channel, &window, &buffer); + + if ((argc == 0) + || (ascii_strcasecmp (argv[0], "list") == 0) + || (ascii_strcasecmp (argv[0], "listfull") == 0)) + { + /* list servers */ + server_name = NULL; + detailed_list = 0; + for (i = 0; i < argc; i++) + { + if (ascii_strcasecmp (argv[i], "list") == 0) + continue; + if (ascii_strcasecmp (argv[i], "listfull") == 0) + { + detailed_list = 1; + continue; + } + if (!server_name) + server_name = argv[i]; + } + if (!server_name) + { + if (irc_servers) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, _("All servers:\n")); + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + irc_display_server (ptr_server, detailed_list); + } + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("No server.\n")); + } + } + else + { + one_server_found = 0; + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (ascii_strcasestr (ptr_server->name, server_name)) + { + if (!one_server_found) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, _("Servers with '%s':\n"), + server_name); + } + one_server_found = 1; + irc_display_server (ptr_server, detailed_list); + } + } + if (!one_server_found) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("No server with '%s' found.\n"), + server_name); + } + } + } + else + { + if (ascii_strcasecmp (argv[0], "add") == 0) + { + if (argc < 3) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing parameters for \"%s\" command\n"), + WEECHAT_ERROR, "server"); + return -1; + } + + if (irc_server_name_already_exists (argv[1])) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s server \"%s\" already exists, can't create it!\n"), + WEECHAT_ERROR, argv[1]); + return -1; + } + + /* init server struct */ + irc_server_init (&server_tmp); + + server_tmp.name = strdup (argv[1]); + server_tmp.address = strdup (argv[2]); + server_tmp.port = IRC_DEFAULT_PORT; + + /* parse arguments */ + for (i = 3; i < argc; i++) + { + if (argv[i][0] == '-') + { + if (ascii_strcasecmp (argv[i], "-temp") == 0) + server_tmp.temp_server = 1; + if (ascii_strcasecmp (argv[i], "-auto") == 0) + server_tmp.autoconnect = 1; + if (ascii_strcasecmp (argv[i], "-noauto") == 0) + server_tmp.autoconnect = 0; + if (ascii_strcasecmp (argv[i], "-ipv6") == 0) + server_tmp.ipv6 = 1; + if (ascii_strcasecmp (argv[i], "-ssl") == 0) + server_tmp.ssl = 1; + if (ascii_strcasecmp (argv[i], "-port") == 0) + { + if (i == (argc - 1)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing argument for \"%s\" option\n"), + WEECHAT_ERROR, "-port"); + irc_server_destroy (&server_tmp); + return -1; + } + error = NULL; + number = strtol (argv[++i], &error, 10); + if ((error) && (error[0] == '\0')) + server_tmp.port = number; + } + if (ascii_strcasecmp (argv[i], "-pwd") == 0) + { + if (i == (argc - 1)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing argument for \"%s\" option\n"), + WEECHAT_ERROR, "-pwd"); + irc_server_destroy (&server_tmp); + return -1; + } + server_tmp.password = strdup (argv[++i]); + } + if (ascii_strcasecmp (argv[i], "-nicks") == 0) + { + if (i >= (argc - 3)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing argument for \"%s\" option\n"), + WEECHAT_ERROR, "-nicks"); + irc_server_destroy (&server_tmp); + return -1; + } + server_tmp.nick1 = strdup (argv[++i]); + server_tmp.nick2 = strdup (argv[++i]); + server_tmp.nick3 = strdup (argv[++i]); + } + if (ascii_strcasecmp (argv[i], "-username") == 0) + { + if (i == (argc - 1)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing argument for \"%s\" option\n"), + WEECHAT_ERROR, "-username"); + irc_server_destroy (&server_tmp); + return -1; + } + server_tmp.username = strdup (argv[++i]); + } + if (ascii_strcasecmp (argv[i], "-realname") == 0) + { + if (i == (argc - 1)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing argument for \"%s\" option\n"), + WEECHAT_ERROR, "-realname"); + irc_server_destroy (&server_tmp); + return -1; + } + server_tmp.realname = strdup (argv[++i]); + } + if (ascii_strcasecmp (argv[i], "-command") == 0) + { + if (i == (argc - 1)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing argument for \"%s\" option\n"), + WEECHAT_ERROR, "-command"); + irc_server_destroy (&server_tmp); + return -1; + } + server_tmp.command = strdup (argv[++i]); + } + if (ascii_strcasecmp (argv[i], "-autojoin") == 0) + { + if (i == (argc - 1)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing argument for \"%s\" option\n"), + WEECHAT_ERROR, "-autojoin"); + irc_server_destroy (&server_tmp); + return -1; + } + server_tmp.autojoin = strdup (argv[++i]); + } + } + } + + /* create new server */ + new_server = irc_server_new (server_tmp.name, + server_tmp.autoconnect, + server_tmp.autoreconnect, + server_tmp.autoreconnect_delay, + server_tmp.temp_server, + server_tmp.address, + server_tmp.port, + server_tmp.ipv6, + server_tmp.ssl, + server_tmp.password, + server_tmp.nick1, + server_tmp.nick2, + server_tmp.nick3, + server_tmp.username, + server_tmp.realname, + server_tmp.hostname, + server_tmp.command, + 1, /* command_delay */ + server_tmp.autojoin, + 1, /* autorejoin */ + NULL); + if (new_server) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("Server %s%s%s created\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + server_tmp.name, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unable to create server\n"), + WEECHAT_ERROR); + irc_server_destroy (&server_tmp); + return -1; + } + + if (new_server->autoconnect) + { + (void) gui_buffer_new (window, new_server, NULL, + GUI_BUFFER_TYPE_STANDARD, 1); + irc_server_connect (new_server, 0); + } + + irc_server_destroy (&server_tmp); + } + else if (ascii_strcasecmp (argv[0], "copy") == 0) + { + if (argc < 3) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing server name for \"%s\" command\n"), + WEECHAT_ERROR, "server copy"); + return -1; + } + + /* look for server by name */ + server_found = irc_server_search (argv[1]); + if (!server_found) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s server \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, argv[1], "server copy"); + return -1; + } + + /* check if target name already exists */ + if (irc_server_search (argv[2])) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s server \"%s\" already exists for \"%s\" command\n"), + WEECHAT_ERROR, argv[2], "server copy"); + return -1; + } + + /* duplicate server */ + new_server = irc_server_duplicate (server_found, argv[2]); + if (new_server) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("Server %s%s%s has been copied to %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + argv[1], + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + argv[2]); + gui_window_redraw_all_buffers (); + return 0; + } + + return -1; + } + else if (ascii_strcasecmp (argv[0], "rename") == 0) + { + if (argc < 3) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing server name for \"%s\" command\n"), + WEECHAT_ERROR, "server rename"); + return -1; + } + + /* look for server by name */ + server_found = irc_server_search (argv[1]); + if (!server_found) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s server \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, argv[1], "server rename"); + return -1; + } + + /* check if target name already exists */ + if (irc_server_search (argv[2])) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s server \"%s\" already exists for \"%s\" command\n"), + WEECHAT_ERROR, argv[2], "server rename"); + return -1; + } + + /* rename server */ + if (irc_server_rename (server_found, argv[2])) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("Server %s%s%s has been renamed to %s%s\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + argv[1], + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + argv[2]); + gui_window_redraw_all_buffers (); + return 0; + } + + return -1; + } + else if (ascii_strcasecmp (argv[0], "keep") == 0) + { + if (argc < 2) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing server name for \"%s\" command\n"), + WEECHAT_ERROR, "server keep"); + return -1; + } + + /* look for server by name */ + server_found = irc_server_search (argv[1]); + if (!server_found) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s server \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, argv[1], "server keep"); + return -1; + } + + /* check that it is temporary server */ + if (!server_found->temp_server) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s server \"%s\" is not a temporary server\n"), + WEECHAT_ERROR, argv[1]); + return -1; + } + + /* remove temporary flag on server */ + server_found->temp_server = 0; + + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("Server %s%s%s is not temporary any more\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + argv[1], + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + + return 0; + } + else if (ascii_strcasecmp (argv[0], "del") == 0) + { + if (argc < 2) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s missing server name for \"%s\" command\n"), + WEECHAT_ERROR, "server del"); + return -1; + } + + /* look for server by name */ + server_found = irc_server_search (argv[1]); + if (!server_found) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s server \"%s\" not found for \"%s\" command\n"), + WEECHAT_ERROR, argv[1], "server del"); + return -1; + } + if (server_found->is_connected) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s you can not delete server \"%s\" because you are connected to. " + "Try /disconnect %s before.\n"), + WEECHAT_ERROR, argv[1], argv[1]); + return -1; + } + + for (ptr_buffer = gui_buffers; ptr_buffer; ptr_buffer = ptr_buffer->next_buffer) + { + if (GUI_SERVER(ptr_buffer) == server_found) + { + ptr_buffer->server = NULL; + ptr_buffer->channel = NULL; + } + } + + server_name = strdup (server_found->name); + + irc_server_free (server_found); + + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("Server %s%s%s has been deleted\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + server_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + if (server_name) + free (server_name); + + gui_window_redraw_buffer (buffer); + + return 0; + } + else if (ascii_strcasecmp (argv[0], "deloutq") == 0) + { + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + irc_server_outqueue_free_all (ptr_server); + } + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf_nolog (NULL, _("Messages outqueue DELETED for all servers. " + "Some messages from you or WeeChat may " + "have been lost!\n")); + return 0; + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unknown option for \"%s\" command\n"), + WEECHAT_ERROR, "server"); + return -1; + } + } + return 0; +} + +/* + * weechat_cmd_set_display_option: display config option + */ + +void +weechat_cmd_set_display_option (t_config_option *option, char *prefix, void *value) +{ + char *color_name, *value2; + + gui_printf (NULL, " %s%s%s%s = ", + (prefix) ? prefix : "", + (prefix) ? "." : "", + option->option_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + if (!value) + { + if (option->option_type == OPTION_TYPE_STRING) + value = option->ptr_string; + else + value = option->ptr_int; + } + switch (option->option_type) + { + case OPTION_TYPE_BOOLEAN: + gui_printf (NULL, "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + (*((int *)value)) ? "ON" : "OFF"); + break; + case OPTION_TYPE_INT: + gui_printf (NULL, "%s%d\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + *((int *)value)); + break; + case OPTION_TYPE_INT_WITH_STRING: + gui_printf (NULL, "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + option->array_values[*((int *)value)]); + break; + case OPTION_TYPE_COLOR: + color_name = gui_color_get_name (*((int *)value)); + gui_printf (NULL, "%s%s\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + (color_name) ? color_name : _("(unknown)")); + break; + case OPTION_TYPE_STRING: + if (*((char **)value)) + { + value2 = strdup (*((char **)value)); + if (value2) + { + if (cfg_log_hide_nickserv_pwd) + { + irc_display_hide_password (value2, 1); + if (strcmp (*((char **)value), value2) != 0) + gui_printf (NULL, _("%s(password hidden) "), + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + } + gui_printf (NULL, "%s\"%s%s%s\"", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + value2, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + free (value2); + } + } + else + gui_printf (NULL, "%s\"\"", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + gui_printf (NULL, "\n"); + break; + } +} + +/* + * weechat_cmd_set: set config options + */ + +int +weechat_cmd_set (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + char *option, *value, *pos; + int i, j, section_displayed; + t_config_option *ptr_option; + t_irc_server *ptr_server; + char option_name[256]; + void *ptr_option_value; + int last_section, last_option, number_found; + + /* make C compiler happy */ + (void) server; + (void) channel; + + option = NULL; + value = NULL; + if (arguments && arguments[0]) + { + option = arguments; + value = strchr (option, '='); + if (value) + { + value[0] = '\0'; + + /* remove spaces before '=' */ + pos = value - 1; + while ((pos > option) && (pos[0] == ' ')) + { + pos[0] = '\0'; + pos--; + } + + /* skip spaces after '=' */ + value++; + while (value[0] && (value[0] == ' ')) + { + value++; + } + + /* remove simple or double quotes + and spaces at the end */ + if (strlen(value) > 1) + { + pos = value + strlen (value) - 1; + while ((pos > value) && (pos[0] == ' ')) + { + pos[0] = '\0'; + pos--; + } + pos = value + strlen (value) - 1; + if (((value[0] == '\'') && + (pos[0] == '\'')) || + ((value[0] == '"') && + (pos[0] == '"'))) + { + pos[0] = '\0'; + value++; + } + } + } + } + + if (value) + { + pos = strrchr (option, '.'); + if (pos) + { + /* server config option modification */ + pos[0] = '\0'; + ptr_server = irc_server_search (option); + if (!ptr_server) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s server \"%s\" not found\n"), + WEECHAT_ERROR, option); + } + else + { + switch (config_set_server_value (ptr_server, pos + 1, value)) + { + case 0: + gui_printf (NULL, "\n"); + gui_printf (NULL, "%s[%s%s %s%s%s]\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + config_sections[CONFIG_SECTION_SERVER].section_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + ptr_server->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + for (i = 0; weechat_options[CONFIG_SECTION_SERVER][i].option_name; i++) + { + if (strcmp (weechat_options[CONFIG_SECTION_SERVER][i].option_name, pos + 1) == 0) + break; + } + if (weechat_options[CONFIG_SECTION_SERVER][i].option_name) + { + ptr_option_value = config_get_server_option_ptr (ptr_server, + weechat_options[CONFIG_SECTION_SERVER][i].option_name); + weechat_cmd_set_display_option (&weechat_options[CONFIG_SECTION_SERVER][i], + ptr_server->name, + ptr_option_value); + } + config_change_buffer_content (); + break; + case -1: + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s config option \"%s\" not found\n"), + WEECHAT_ERROR, pos + 1); + break; + case -2: + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s incorrect value for option \"%s\"\n"), + WEECHAT_ERROR, pos + 1); + break; + } + } + pos[0] = '.'; + } + else + { + ptr_option = config_option_search (option); + if (ptr_option) + { + if (ptr_option->handler_change == NULL) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s option \"%s\" can not be changed while WeeChat is running\n"), + WEECHAT_ERROR, option); + } + else + { + if (config_option_set_value (ptr_option, value) == 0) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, "%s[%s%s%s]\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + config_get_section (ptr_option), + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + weechat_cmd_set_display_option (ptr_option, NULL, NULL); + (void) (ptr_option->handler_change()); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s incorrect value for option \"%s\"\n"), + WEECHAT_ERROR, option); + } + } + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s config option \"%s\" not found\n"), + WEECHAT_ERROR, option); + } + } + } + else + { + last_section = -1; + last_option = -1; + number_found = 0; + for (i = 0; i < CONFIG_NUMBER_SECTIONS; i++) + { + section_displayed = 0; + if ((i != CONFIG_SECTION_KEYS) && (i != CONFIG_SECTION_ALIAS) + && (i != CONFIG_SECTION_IGNORE) && (i != CONFIG_SECTION_SERVER)) + { + for (j = 0; weechat_options[i][j].option_name; j++) + { + if ((!option) || + ((option) && (option[0]) + && (strstr (weechat_options[i][j].option_name, option) + != NULL))) + { + if (!section_displayed) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, "%s[%s%s%s]\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + config_sections[i].section_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + section_displayed = 1; + } + weechat_cmd_set_display_option (&weechat_options[i][j], NULL, NULL); + last_section = i; + last_option = j; + number_found++; + } + } + } + } + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + section_displayed = 0; + for (i = 0; weechat_options[CONFIG_SECTION_SERVER][i].option_name; i++) + { + snprintf (option_name, sizeof (option_name), "%s.%s", + ptr_server->name, + weechat_options[CONFIG_SECTION_SERVER][i].option_name); + if ((!option) || + ((option) && (option[0]) + && (strstr (option_name, option) != NULL))) + { + if (!section_displayed) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, "%s[%s%s %s%s%s]\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + config_sections[CONFIG_SECTION_SERVER].section_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_SERVER), + ptr_server->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + section_displayed = 1; + } + ptr_option_value = config_get_server_option_ptr (ptr_server, + weechat_options[CONFIG_SECTION_SERVER][i].option_name); + if (ptr_option_value) + { + weechat_cmd_set_display_option (&weechat_options[CONFIG_SECTION_SERVER][i], + ptr_server->name, + ptr_option_value); + last_section = CONFIG_SECTION_SERVER; + last_option = i; + number_found++; + } + } + } + } + if (number_found == 0) + { + if (option) + gui_printf (NULL, _("No config option found with \"%s\"\n"), + option); + else + gui_printf (NULL, _("No config option found\n")); + } + else + { + if ((number_found == 1) && (last_section >= 0) && (last_option >= 0)) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, _("%sDetail:\n"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL)); + switch (weechat_options[last_section][last_option].option_type) + { + case OPTION_TYPE_BOOLEAN: + gui_printf (NULL, _(" . type boolean (values: 'on' or 'off')\n")); + gui_printf (NULL, _(" . default value: '%s'\n"), + (weechat_options[last_section][last_option].default_int == BOOL_TRUE) ? + "on" : "off"); + break; + case OPTION_TYPE_INT: + gui_printf (NULL, _(" . type integer (values: between %d and %d)\n"), + weechat_options[last_section][last_option].min, + weechat_options[last_section][last_option].max); + gui_printf (NULL, _(" . default value: %d\n"), + weechat_options[last_section][last_option].default_int); + break; + case OPTION_TYPE_INT_WITH_STRING: + gui_printf (NULL, _(" . type string (values: ")); + i = 0; + while (weechat_options[last_section][last_option].array_values[i]) + { + gui_printf (NULL, "'%s'", + weechat_options[last_section][last_option].array_values[i]); + if (weechat_options[last_section][last_option].array_values[i + 1]) + gui_printf (NULL, ", "); + i++; + } + gui_printf (NULL, ")\n"); + gui_printf (NULL, _(" . default value: '%s'\n"), + (weechat_options[last_section][last_option].default_string) ? + weechat_options[last_section][last_option].default_string : _("empty")); + break; + case OPTION_TYPE_COLOR: + gui_printf (NULL, _(" . type color (Curses or Gtk color, look at WeeChat doc)\n")); + gui_printf (NULL, _(" . default value: '%s'\n"), + (weechat_options[last_section][last_option].default_string) ? + weechat_options[last_section][last_option].default_string : _("empty")); + break; + case OPTION_TYPE_STRING: + switch (weechat_options[last_section][last_option].max) + { + case 0: + gui_printf (NULL, _(" . type string (any string)\n")); + break; + case 1: + gui_printf (NULL, _(" . type: char (any char)\n")); + break; + default: + gui_printf (NULL, _(" . type string (any string, limit: %d chars)\n"), + weechat_options[last_section][last_option].max); + break; + } + gui_printf (NULL, _(" . default value: '%s'\n"), + (weechat_options[last_section][last_option].default_string) ? + weechat_options[last_section][last_option].default_string : _("empty")); + break; + } + gui_printf (NULL, _(" . description: %s\n"), + _(weechat_options[last_section][last_option].long_description)); + } + else + { + gui_printf (NULL, "\n"); + gui_printf (NULL, "%s%d %s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + number_found, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + if (option) + gui_printf (NULL, _("config option(s) found with \"%s\"\n"), + option); + else + gui_printf (NULL, _("config option(s) found\n")); + } + } + } + return 0; +} + +/* + * weechat_cmd_setp: set plugin options + */ + +int +weechat_cmd_setp (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ +#ifdef PLUGINS + char *option, *value, *pos, *ptr_name; + t_plugin_option *ptr_option; + int number_found; + + /* make C compiler happy */ + (void) server; + (void) channel; + + option = NULL; + value = NULL; + if (arguments && arguments[0]) + { + option = arguments; + value = strchr (option, '='); + if (value) + { + value[0] = '\0'; + + /* remove spaces before '=' */ + pos = value - 1; + while ((pos > option) && (pos[0] == ' ')) + { + pos[0] = '\0'; + pos--; + } + + /* skip spaces after '=' */ + value++; + while (value[0] && (value[0] == ' ')) + { + value++; + } + + /* remove simple or double quotes + and spaces at the end */ + if (strlen(value) > 1) + { + pos = value + strlen (value) - 1; + while ((pos > value) && (pos[0] == ' ')) + { + pos[0] = '\0'; + pos--; + } + pos = value + strlen (value) - 1; + if (((value[0] == '\'') && + (pos[0] == '\'')) || + ((value[0] == '"') && + (pos[0] == '"'))) + { + pos[0] = '\0'; + value++; + } + } + } + } + + if (value) + { + ptr_name = NULL; + ptr_option = plugin_config_search_internal (option); + if (ptr_option) + ptr_name = ptr_option->name; + else + { + pos = strchr (option, '.'); + if (pos) + pos[0] = '\0'; + if (!pos || !pos[1] || (!plugin_search (option))) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s plugin \"%s\" not found\n"), + WEECHAT_ERROR, option); + } + else + ptr_name = option; + if (pos) + pos[0] = '.'; + } + if (ptr_name) + { + if (plugin_config_set_internal (ptr_name, value)) + { + gui_printf (NULL, "\n %s%s = \"%s%s%s\"\n", + ptr_name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + value, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s incorrect value for plugin option \"%s\"\n"), + WEECHAT_ERROR, ptr_name); + } + } + } + else + { + number_found = 0; + for (ptr_option = plugin_options; ptr_option; + ptr_option = ptr_option->next_option) + { + if ((!option) || + ((option) && (option[0]) + && (strstr (ptr_option->name, option) != NULL))) + { + if (number_found == 0) + gui_printf (NULL, "\n"); + gui_printf (NULL, " %s%s = \"%s%s%s\"\n", + ptr_option->name, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_HOST), + ptr_option->value, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + number_found++; + } + } + if (number_found == 0) + { + if (option) + gui_printf (NULL, _("No plugin option found with \"%s\"\n"), + option); + else + gui_printf (NULL, _("No plugin option found\n")); + } + else + { + gui_printf (NULL, "\n"); + gui_printf (NULL, "%s%d %s", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + number_found, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + if (option) + gui_printf (NULL, _("plugin option(s) found with \"%s\"\n"), + option); + else + gui_printf (NULL, _("plugin option(s) found\n")); + } + } +#else + /* make C compiler happy */ + (void) server; + (void) channel; + (void) arguments; + + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("Command \"%s\" is not available, WeeChat was built " + "without plugins support.\n"), + "setp"); +#endif + + return 0; +} + +/* + * cmd_unalias: remove an alias + */ + +int +weechat_cmd_unalias (t_irc_server *server, t_irc_channel *channel, + char *arguments) +{ + t_weelist *ptr_weelist; + t_weechat_alias *ptr_alias; + + /* make C compiler happy */ + (void) server; + (void) channel; + + if (arguments[0] == '/') + arguments++; + + ptr_weelist = weelist_search (index_commands, arguments); + if (!ptr_weelist) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s alias or command \"%s\" not found\n"), + WEECHAT_ERROR, arguments); + return -1; + } + + weelist_remove (&index_commands, &last_index_command, ptr_weelist); + ptr_alias = alias_search (arguments); + if (ptr_alias) + alias_free (ptr_alias); + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, _("Alias \"%s\" removed\n"), + arguments); + return 0; +} + +/* + * weechat_cmd_unignore: unignore IRC commands and/or hosts + */ + +int +weechat_cmd_unignore (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + char *error; + int number, ret; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + ret = 0; + switch (argc) + { + case 0: + /* List all ignore */ + weechat_cmd_ignore (server, channel, argc, argv); + return 0; + break; + case 1: + error = NULL; + number = strtol (argv[0], &error, 10); + if ((error) && (error[0] == '\0')) + ret = irc_ignore_search_free_by_number (number); + else + ret = irc_ignore_search_free (argv[0], "*", "*", + (GUI_SERVER(buffer)) ? + GUI_SERVER(buffer)->name : "*"); + break; + case 2: + ret = irc_ignore_search_free (argv[0], argv[1], "*", + (GUI_SERVER(buffer)) ? + GUI_SERVER(buffer)->name : "*"); + break; + case 3: + ret = irc_ignore_search_free (argv[0], argv[1], argv[2], + (GUI_SERVER(buffer)) ? + GUI_SERVER(buffer)->name : "*"); + break; + case 4: + ret = irc_ignore_search_free (argv[0], argv[1], argv[2], argv[3]); + break; + } + + if (ret) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf (NULL, + NG_("%s%d%s ignore was removed.\n", + "%s%d%s ignore were removed.\n", + ret), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ret, + GUI_COLOR(GUI_COLOR_WIN_CHAT)); + } + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, _("%s no ignore found\n"), + WEECHAT_ERROR); + return -1; + } + + return 0; +} + +/* + * weechat_cmd_upgrade: upgrade WeeChat + */ + +int +weechat_cmd_upgrade (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_irc_server *ptr_server; + int filename_length; + char *filename, *ptr_binary; + char *exec_args[7] = { NULL, "-a", "--dir", NULL, "--session", NULL, NULL }; + + /* make C compiler happy */ + (void) server; + (void) channel; + + ptr_binary = (argc > 0) ? argv[0] : weechat_argv0; + + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (ptr_server->child_pid != 0) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf_nolog (NULL, + _("%s can't upgrade: connection to at least " + "one server is pending\n"), + WEECHAT_ERROR); + return -1; + } + /* TODO: remove this test, and fix gnutls save/load in session */ + if (ptr_server->is_connected && ptr_server->ssl_connected) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf_nolog (NULL, + _("%s can't upgrade: connection to at least " + "one SSL server is active " + "(should be fixed in a future version)\n"), + WEECHAT_ERROR); + return -1; + } + if (ptr_server->outqueue) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf_nolog (NULL, + _("%s can't upgrade: anti-flood is active on " + "at least one server (sending many lines)\n"), + WEECHAT_ERROR); + return -1; + } + } + + filename_length = strlen (weechat_home) + strlen (WEECHAT_SESSION_NAME) + 2; + filename = (char *) malloc (filename_length * sizeof (char)); + if (!filename) + return -2; + snprintf (filename, filename_length, "%s%s" WEECHAT_SESSION_NAME, + weechat_home, DIR_SEPARATOR); + + irc_display_prefix (NULL, NULL, GUI_PREFIX_INFO); + gui_printf_nolog (NULL, _("Upgrading WeeChat...\n")); + + if (!session_save (filename)) + { + free (filename); + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf_nolog (NULL, + _("%s unable to save session in file\n"), + WEECHAT_ERROR); + return -1; + } + + exec_args[0] = strdup (ptr_binary); + exec_args[3] = strdup (weechat_home); + exec_args[5] = strdup (filename); + + /* unload plugins, save config, then upgrade */ +#ifdef PLUGINS + plugin_end (); +#endif + if (cfg_look_save_on_exit) + (void) config_write (NULL); + gui_main_end (); + fifo_remove (); + weechat_log_close (); + + execvp (exec_args[0], exec_args); + + /* this code should not be reached if execvp is ok */ +#ifdef PLUGINS + plugin_init (1); +#endif + + weechat_iconv_fprintf (stderr, + _("%s exec failed (program: \"%s\"), exiting WeeChat\n"), + WEECHAT_ERROR, + exec_args[0]); + + free (exec_args[0]); + free (exec_args[3]); + free (filename); + + exit (EXIT_FAILURE); + + /* never executed */ + return -1; +} + +/* + * weechat_cmd_uptime: display WeeChat uptime + */ + +int +weechat_cmd_uptime (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_buffer *buffer; + time_t running_time; + int day, hour, min, sec; + char string[256]; + + gui_buffer_find_context (server, channel, NULL, &buffer); + + running_time = time (NULL) - weechat_start_time; + day = running_time / (60 * 60 * 24); + hour = (running_time % (60 * 60 * 24)) / (60 * 60); + min = ((running_time % (60 * 60 * 24)) % (60 * 60)) / 60; + sec = ((running_time % (60 * 60 * 24)) % (60 * 60)) % 60; + + if ((argc == 1) && (strcmp (argv[0], "-o") == 0) + && ((GUI_BUFFER_IS_CHANNEL(buffer)) + || (GUI_BUFFER_IS_PRIVATE(buffer)))) + { + snprintf (string, sizeof (string), + _("WeeChat uptime: %d %s %02d:%02d:%02d, started on %s"), + day, + NG_("day", "days", day), + hour, + min, + sec, + ctime (&weechat_start_time)); + string[strlen (string) - 1] = '\0'; + user_command (server, channel, string, 0); + } + else + { + irc_display_prefix (NULL, buffer, GUI_PREFIX_INFO); + gui_printf_nolog (buffer, + _("WeeChat uptime: %s%d %s%s " + "%s%02d%s:%s%02d%s:%s%02d%s, " + "started on %s%s"), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + day, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + NG_("day", "days", day), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + hour, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + min, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + sec, + GUI_COLOR(GUI_COLOR_WIN_CHAT), + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + ctime (&weechat_start_time)); + } + + return 0; +} + +/* + * weechat_cmd_window: manage windows + */ + +int +weechat_cmd_window (t_irc_server *server, t_irc_channel *channel, + int argc, char **argv) +{ + t_gui_window *window, *ptr_win; + t_gui_buffer *buffer; + int i; + char *error; + long number; + + gui_buffer_find_context (server, channel, &window, &buffer); + + if ((argc == 0) || ((argc == 1) && (ascii_strcasecmp (argv[0], "list") == 0))) + { + /* list open windows */ + + gui_printf (NULL, "\n"); + gui_printf (NULL, _("Open windows:\n")); + + i = 1; + for (ptr_win = gui_windows; ptr_win; ptr_win = ptr_win->next_window) + { + gui_printf (NULL, "%s[%s%d%s] (%s%d:%d%s;%s%dx%d%s) ", + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + i, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ptr_win->win_x, + ptr_win->win_y, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK), + GUI_COLOR(GUI_COLOR_WIN_CHAT), + ptr_win->win_width, + ptr_win->win_height, + GUI_COLOR(GUI_COLOR_WIN_CHAT_DARK)); + + weechat_cmd_buffer_display_info (ptr_win->buffer); + + i++; + } + } + else + { + if (ascii_strcasecmp (argv[0], "splith") == 0) + { + /* split window horizontally */ + if (argc > 1) + { + error = NULL; + number = strtol (argv[1], &error, 10); + if ((error) && (error[0] == '\0') + && (number > 0) && (number < 100)) + gui_window_split_horiz (window, number); + } + else + gui_window_split_horiz (window, 50); + } + else if (ascii_strcasecmp (argv[0], "splitv") == 0) + { + /* split window vertically */ + if (argc > 1) + { + error = NULL; + number = strtol (argv[1], &error, 10); + if ((error) && (error[0] == '\0') + && (number > 0) && (number < 100)) + gui_window_split_vertic (window, number); + } + else + gui_window_split_vertic (window, 50); + } + else if (ascii_strcasecmp (argv[0], "resize") == 0) + { + /* resize window */ + if (argc > 1) + { + error = NULL; + number = strtol (argv[1], &error, 10); + if ((error) && (error[0] == '\0') + && (number > 0) && (number < 100)) + gui_window_resize (window, number); + } + } + else if (ascii_strcasecmp (argv[0], "merge") == 0) + { + if (argc >= 2) + { + if (ascii_strcasecmp (argv[1], "all") == 0) + gui_window_merge_all (window); + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unknown option for \"%s\" command\n"), + WEECHAT_ERROR, "window merge"); + return -1; + } + } + else + { + if (!gui_window_merge (window)) + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s can not merge windows, " + "there's no other window with same size " + "near current one.\n"), + WEECHAT_ERROR); + return -1; + } + } + } + else if (ascii_strncasecmp (argv[0], "b", 1) == 0) + { + /* jump to window by buffer number */ + error = NULL; + number = strtol (argv[0] + 1, &error, 10); + if ((error) && (error[0] == '\0')) + gui_window_switch_by_buffer (window, number); + } + else if (ascii_strcasecmp (argv[0], "-1") == 0) + gui_window_switch_previous (window); + else if (ascii_strcasecmp (argv[0], "+1") == 0) + gui_window_switch_next (window); + else if (ascii_strcasecmp (argv[0], "up") == 0) + gui_window_switch_up (window); + else if (ascii_strcasecmp (argv[0], "down") == 0) + gui_window_switch_down (window); + else if (ascii_strcasecmp (argv[0], "left") == 0) + gui_window_switch_left (window); + else if (ascii_strcasecmp (argv[0], "right") == 0) + gui_window_switch_right (window); + else + { + irc_display_prefix (NULL, NULL, GUI_PREFIX_ERROR); + gui_printf (NULL, + _("%s unknown option for \"%s\" command\n"), + WEECHAT_ERROR, "window"); + return -1; + } + } + return 0; +} diff --git a/src/core/wee-command.h b/src/core/wee-command.h new file mode 100644 index 000000000..644491dd0 --- /dev/null +++ b/src/core/wee-command.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_COMMAND_H +#define __WEECHAT_COMMAND_H 1 + +#include "weelist.h" +#include "../protocols/irc/irc.h" +#include "../gui/gui.h" + +#define MAX_ARGS 8192 + +typedef struct t_weechat_command t_weechat_command; + +struct t_weechat_command +{ + char *command_name; /* WeeChat (internal) command name */ + char *command_description; /* command description (for /help) */ + char *arguments; /* command arguments (for /help) */ + char *arguments_description; /* arguments description (for /help) */ + char *completion_template; /* template for completion */ + /* NULL=no completion, ""=default (nick) */ + int min_arg, max_arg; /* min & max number of arguments */ + int conversion; /* = 1 if cmd args are converted (charset */ + /* and color) before execution */ + int (*cmd_function_args)(t_irc_server *, t_irc_channel *, int, char **); + /* function called when user enters cmd */ + int (*cmd_function_1arg)(t_irc_server *, t_irc_channel *, char *); + /* function called when user enters cmd */ +}; + +extern t_weechat_command weechat_commands[]; + +extern t_weelist *index_commands; +extern t_weelist *last_index_command; + +extern void command_index_build (); +extern void command_index_free (); +extern int command_used_by_weechat (char *); +extern char **split_multi_command (char *, char); +extern void free_multi_command (char **); +extern int exec_weechat_command (t_irc_server *, t_irc_channel *, char *, int); +extern void user_command (t_irc_server *, t_irc_channel *, char *, int); +extern int weechat_cmd_alias (t_irc_server *, t_irc_channel *, char *); +extern int weechat_cmd_buffer (t_irc_server *, t_irc_channel *, char *); +extern int weechat_cmd_builtin (t_irc_server *, t_irc_channel *, char *); +extern int weechat_cmd_clear (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_connect (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_dcc (t_irc_server *, t_irc_channel *, char *); +extern int weechat_cmd_debug (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_disconnect (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_help (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_history (t_irc_server *, t_irc_channel *, int, char **); +extern void weechat_cmd_ignore_display (char *, t_irc_ignore *); +extern int weechat_cmd_ignore (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_key (t_irc_server *, t_irc_channel *, char *); +extern int weechat_cmd_panel (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_plugin (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_reconnect (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_save (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_server (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_set (t_irc_server *, t_irc_channel *, char *); +extern int weechat_cmd_setp (t_irc_server *, t_irc_channel *, char *); +extern int weechat_cmd_unalias (t_irc_server *, t_irc_channel *, char *); +extern int weechat_cmd_unignore (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_upgrade (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_uptime (t_irc_server *, t_irc_channel *, int, char **); +extern int weechat_cmd_window (t_irc_server *, t_irc_channel *, int, char **); + +#endif /* command.h */ diff --git a/src/core/wee-config.c b/src/core/wee-config.c new file mode 100644 index 000000000..b4f1c8f46 --- /dev/null +++ b/src/core/wee-config.c @@ -0,0 +1,2597 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* weeconfig.c: WeeChat configuration */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <time.h> +#include <pwd.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "weechat.h" +#include "weeconfig.h" +#include "alias.h" +#include "command.h" +#include "fifo.h" +#include "hotlist.h" +#include "log.h" +#include "utf8.h" +#include "util.h" +#include "../protocols/irc/irc.h" +#include "../gui/gui.h" + + +/* config sections */ + +t_config_section config_sections[CONFIG_NUMBER_SECTIONS] = +{ { CONFIG_SECTION_LOOK, "look" }, + { CONFIG_SECTION_COLORS, "colors" }, + { CONFIG_SECTION_HISTORY, "history" }, + { CONFIG_SECTION_LOG, "log" }, + { CONFIG_SECTION_IRC, "irc" }, + { CONFIG_SECTION_DCC, "dcc" }, + { CONFIG_SECTION_PROXY, "proxy" }, + { CONFIG_SECTION_PLUGINS, "plugins" }, + { CONFIG_SECTION_KEYS, "keys" }, + { CONFIG_SECTION_ALIAS, "alias" }, + { CONFIG_SECTION_IGNORE, "ignore" }, + { CONFIG_SECTION_SERVER, "server" } +}; + +/* config, look & feel section */ + +int cfg_look_save_on_exit; +int cfg_look_set_title; +int cfg_look_startup_logo; +int cfg_look_startup_version; +char *cfg_look_weechat_slogan; +int cfg_look_one_server_buffer; +int cfg_look_scroll_amount; +int cfg_look_open_near_server; +char *cfg_look_buffer_timestamp; +int cfg_look_color_nicks_number; +int cfg_look_color_actions; +int cfg_look_nicklist; +int cfg_look_nicklist_position; +char *cfg_look_nicklist_position_values[] = +{ "left", "right", "top", "bottom", NULL }; +int cfg_look_nicklist_min_size; +int cfg_look_nicklist_max_size; +int cfg_look_nicklist_separator; +int cfg_look_nickmode; +int cfg_look_nickmode_empty; +char *cfg_look_no_nickname; +char *cfg_look_nick_prefix; +char *cfg_look_nick_suffix; +int cfg_look_align_nick; +char *cfg_look_align_nick_values[] = +{ "none", "left", "right", NULL }; +int cfg_look_align_other; +int cfg_look_align_size; +int cfg_look_align_size_max; +int cfg_look_align_text_offset; +char *cfg_look_nick_completor; +char *cfg_look_nick_completion_ignore; +int cfg_look_nick_completion_smart; +int cfg_look_nick_complete_first; +int cfg_look_infobar; +char *cfg_look_infobar_timestamp; +int cfg_look_infobar_seconds; +int cfg_look_infobar_delay_highlight; +int cfg_look_hotlist_names_count; +int cfg_look_hotlist_names_level; +int cfg_look_hotlist_names_length; +int cfg_look_hotlist_sort; +char *cfg_look_hotlist_sort_values[] = +{ "group_time_asc", "group_time_desc", + "group_number_asc", "group_number_desc", + "number_asc", "number_desc" }; +int cfg_look_day_change; +char *cfg_look_day_change_timestamp; +char *cfg_look_read_marker; +char *cfg_look_input_format; +int cfg_look_paste_max_lines; + +t_config_option weechat_options_look[] = +{ { "look_save_on_exit", N_("save config file on exit"), + N_("save config file on exit"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_save_on_exit, NULL, config_change_save_on_exit }, + { "look_set_title", N_("set title for window (terminal for Curses GUI) with name and version"), + N_("set title for window (terminal for Curses GUI) with name and version"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_set_title, NULL, config_change_title }, + { "look_startup_logo", N_("display WeeChat logo at startup"), + N_("display WeeChat logo at startup"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_startup_logo, NULL, config_change_noop }, + { "look_startup_version", N_("display WeeChat version at startup"), + N_("display WeeChat version at startup"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_startup_version, NULL, config_change_noop }, + { "look_weechat_slogan", N_("WeeChat slogan"), + N_("WeeChat slogan (if empty, slogan is not used)"), + OPTION_TYPE_STRING, 0, 0, 0, + "the geekest IRC client!", NULL, NULL, &cfg_look_weechat_slogan, config_change_noop }, + { "look_one_server_buffer", N_("use same buffer for all servers"), + N_("use same buffer for all servers"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_look_one_server_buffer, NULL, config_change_one_server_buffer }, + { "look_open_near_server", N_("open new channels/privates near server"), + N_("open new channels/privates near server"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_look_open_near_server, NULL, config_change_noop }, + { "look_scroll_amount", N_("how many lines to scroll by with scroll_up and scroll_down"), + N_("how many lines to scroll by with scroll_up and scroll_down"), + OPTION_TYPE_INT, 1, INT_MAX, 3, + NULL, NULL, &cfg_look_scroll_amount, NULL, config_change_buffer_content }, + { "look_buffer_timestamp", N_("timestamp for buffers"), + N_("timestamp for buffers"), + OPTION_TYPE_STRING, 0, 0, 0, + "[%H:%M:%S]", NULL, NULL, &cfg_look_buffer_timestamp, config_change_buffer_content }, + { "look_color_nicks_number", N_("number of colors to use for nicks colors"), + N_("number of colors to use for nicks colors"), + OPTION_TYPE_INT, 1, 10, 10, + NULL, NULL, &cfg_look_color_nicks_number, NULL, config_change_nicks_colors }, + { "look_color_actions", N_("display actions with different colors"), + N_("display actions with different colors"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_color_actions, NULL, config_change_noop }, + { "look_nicklist", N_("display nicklist window"), + N_("display nicklist window (for channel windows)"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_nicklist, NULL, config_change_buffers }, + { "look_nicklist_position", N_("nicklist position"), + N_("nicklist position (top, left, right (default), bottom)"), + OPTION_TYPE_INT_WITH_STRING, 0, 0, 0, + "right", cfg_look_nicklist_position_values, &cfg_look_nicklist_position, NULL, config_change_buffers }, + { "look_nicklist_min_size", N_("min size for nicklist"), + N_("min size for nicklist (width or height, depending on look_nicklist_position " + "(0 = no min size))"), + OPTION_TYPE_INT, 0, 100, 0, + NULL, NULL, &cfg_look_nicklist_min_size, NULL, config_change_buffers }, + { "look_nicklist_max_size", N_("max size for nicklist"), + N_("max size for nicklist (width or height, depending on look_nicklist_position " + "(0 = no max size; if min = max and > 0, then size is fixed))"), + OPTION_TYPE_INT, 0, 100, 0, + NULL, NULL, &cfg_look_nicklist_max_size, NULL, config_change_buffers }, + { "look_nicklist_separator", N_("separator between chat and nicklist"), + N_("separator between chat and nicklist"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_nicklist_separator, NULL, config_change_buffers }, + { "look_no_nickname", N_("text to display instead of nick when not connected"), + N_("text to display instead of nick when not connected"), + OPTION_TYPE_STRING, 0, 0, 0, + "-cmd-", NULL, NULL, &cfg_look_no_nickname, config_change_buffer_content }, + { "look_nickmode", N_("display nick mode ((half)op/voice) before each nick"), + N_("display nick mode ((half)op/voice) before each nick"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_nickmode, NULL, config_change_buffers }, + { "look_nickmode_empty", N_("display space if nick mode is not (half)op/voice"), + N_("display space if nick mode is not (half)op/voice"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_look_nickmode_empty, NULL, config_change_buffers }, + { "look_nick_prefix", N_("text to display before nick in chat window"), + N_("text to display before nick in chat window"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &cfg_look_nick_prefix, config_change_noop }, + { "look_nick_suffix", N_("text to display after nick in chat window"), + N_("text to display after nick in chat window"), + OPTION_TYPE_STRING, 0, 0, 0, + " |", NULL, NULL, &cfg_look_nick_suffix, config_change_noop }, + { "look_align_nick", N_("nick alignment (fixed size for nicks in chat window)"), + N_("nick alignment (fixed size for nicks in chat window (none, left, right))"), + OPTION_TYPE_INT_WITH_STRING, 0, 0, 0, + "right", cfg_look_align_nick_values, &cfg_look_align_nick, NULL, config_change_noop }, + { "look_align_other", N_("alignment for other messages (not beginning with a nick)"), + N_("alignment for other messages (not beginning with a nick)"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_align_other, NULL, config_change_noop }, + { "look_align_size", N_("size for aligning nick and other messages"), + N_("size for aligning nick and other messages"), + OPTION_TYPE_INT, 8, 64, 14, + NULL, NULL, &cfg_look_align_size, NULL, config_change_noop }, + { "look_align_size_max", N_("max size for aligning nick and other messages"), + N_("max size for aligning nick and other messages (should be >= to " + "look_align_size)"), + OPTION_TYPE_INT, 8, 64, 20, + NULL, NULL, &cfg_look_align_size_max, NULL, config_change_noop }, + { "look_align_text_offset", N_("offset for aligning lines of messages " + "(except first lines)"), + N_("offset for aligning lines of messages (except first lines), default is " + "-1 (align after nick), a null or positive value is offset after " + "beginning of line"), + OPTION_TYPE_INT, -1, 64, -1, + NULL, NULL, &cfg_look_align_text_offset, NULL, config_change_buffers }, + { "look_nick_completor", N_("the string inserted after nick completion"), + N_("the string inserted after nick completion"), + OPTION_TYPE_STRING, 0, 0, 0, + ":", NULL, NULL, &cfg_look_nick_completor, config_change_noop }, + { "look_nick_completion_ignore", N_("chars ignored for nick completion"), + N_("chars ignored for nick completion"), + OPTION_TYPE_STRING, 0, 0, 0, + "[]-^", NULL, NULL, &cfg_look_nick_completion_ignore, config_change_noop }, + { "look_nick_completion_smart", N_("smart completion for nicks"), + N_("smart completion for nicks (completes with last speakers first)"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_nick_completion_smart, NULL, config_change_noop }, + { "look_nick_complete_first", N_("complete only with first nick found"), + N_("complete only with first nick found"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_look_nick_complete_first, NULL, config_change_noop }, + { "look_infobar", N_("enable info bar"), + N_("enable info bar"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_infobar, NULL, config_change_buffers }, + { "look_infobar_timestamp", N_("timestamp for time in infobar"), + N_("timestamp for time in infobar"), + OPTION_TYPE_STRING, 0, 0, 0, + "%B, %A %d %Y", NULL, NULL, &cfg_look_infobar_timestamp, config_change_buffer_content }, + { "look_infobar_seconds", N_("display seconds in infobar time"), + N_("display seconds in infobar time"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_infobar_seconds, NULL, config_change_buffer_content }, + { "look_infobar_delay_highlight", N_("delay (in seconds) for highlight messages in infobar"), + N_("delay (in seconds) for highlight messages in infobar " + "(0 = disable highlight notifications in infobar)"), + OPTION_TYPE_INT, 0, INT_MAX, 7, + NULL, NULL, &cfg_look_infobar_delay_highlight, NULL, config_change_noop }, + { "look_hotlist_names_count", N_("max number of names in hotlist"), + N_("max number of names in hotlist (0 = no name displayed, only buffer numbers)"), + OPTION_TYPE_INT, 0, 32, 3, + NULL, NULL, &cfg_look_hotlist_names_count, NULL, config_change_buffer_content }, + { "look_hotlist_names_level", N_("level for displaying names in hotlist"), + N_("level for displaying names in hotlist (combination of: 1=join/part, 2=message, " + "4=private, 8=highlight, for example: 12=private+highlight)"), + OPTION_TYPE_INT, 1, 15, 12, + NULL, NULL, &cfg_look_hotlist_names_level, NULL, config_change_buffer_content }, + { "look_hotlist_names_length", N_("max length of names in hotlist"), + N_("max length of names in hotlist (0 = no limit)"), + OPTION_TYPE_INT, 0, 32, 0, + NULL, NULL, &cfg_look_hotlist_names_length, NULL, config_change_buffer_content }, + { "look_hotlist_sort", N_("hotlist sort type"), + N_("hotlist sort type (group_time_asc (default), group_time_desc, " + "group_number_asc, group_number_desc, number_asc, number_desc)"), + OPTION_TYPE_INT_WITH_STRING, 0, 0, 0, + "group_time_asc", cfg_look_hotlist_sort_values, &cfg_look_hotlist_sort, NULL, config_change_hotlist }, + { "look_day_change", N_("display special message when day changes"), + N_("display special message when day changes"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_look_day_change, NULL, config_change_noop }, + { "look_day_change_timestamp", N_("timestamp for date displayed when day changed"), + N_("timestamp for date displayed when day changed"), + OPTION_TYPE_STRING, 0, 0, 0, + "%a, %d %b %Y", NULL, NULL, &cfg_look_day_change_timestamp, config_change_noop }, + { "look_read_marker", N_("use a marker on servers/channels to show first unread line"), + N_("use a marker on servers/channels to show first unread line"), + OPTION_TYPE_STRING, 0, 1, 0, + " ", NULL, NULL, &cfg_look_read_marker, config_change_read_marker }, + { "look_input_format", N_("format for input prompt"), + N_("format for input prompt ('%c' is replaced by channel or server, " + "'%n' by nick and '%m' by nick modes)"), + OPTION_TYPE_STRING, 0, 0, 0, + "[%n(%m)] ", NULL, NULL, &cfg_look_input_format, config_change_buffer_content }, + { "look_paste_max_lines", N_("max number of lines for paste without asking user"), + N_("max number of lines for paste without asking user (0 = disable this feature)"), + OPTION_TYPE_INT, 0, INT_MAX, 3, + NULL, NULL, &cfg_look_paste_max_lines, NULL, &config_change_noop }, + { NULL, NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } +}; + +/* config, colors section */ + +int cfg_col_real_white; +int cfg_col_separator; +int cfg_col_title; +int cfg_col_title_more; +int cfg_col_title_bg; +int cfg_col_chat; +int cfg_col_chat_time; +int cfg_col_chat_time_sep; +int cfg_col_chat_prefix1; +int cfg_col_chat_prefix2; +int cfg_col_chat_server; +int cfg_col_chat_join; +int cfg_col_chat_part; +int cfg_col_chat_nick; +int cfg_col_chat_host; +int cfg_col_chat_channel; +int cfg_col_chat_dark; +int cfg_col_chat_highlight; +int cfg_col_chat_bg; +int cfg_col_chat_read_marker; +int cfg_col_chat_read_marker_bg; +int cfg_col_status; +int cfg_col_status_delimiters; +int cfg_col_status_channel; +int cfg_col_status_data_msg; +int cfg_col_status_data_private; +int cfg_col_status_data_highlight; +int cfg_col_status_data_other; +int cfg_col_status_more; +int cfg_col_status_bg; +int cfg_col_infobar; +int cfg_col_infobar_delimiters; +int cfg_col_infobar_highlight; +int cfg_col_infobar_bg; +int cfg_col_input; +int cfg_col_input_server; +int cfg_col_input_channel; +int cfg_col_input_nick; +int cfg_col_input_delimiters; +int cfg_col_input_text_not_found; +int cfg_col_input_actions; +int cfg_col_input_bg; +int cfg_col_nick; +int cfg_col_nick_away; +int cfg_col_nick_chanowner; +int cfg_col_nick_chanadmin; +int cfg_col_nick_op; +int cfg_col_nick_halfop; +int cfg_col_nick_voice; +int cfg_col_nick_user; +int cfg_col_nick_more; +int cfg_col_nick_sep; +int cfg_col_nick_self; +int cfg_col_nick_colors[GUI_COLOR_WIN_NICK_NUMBER]; +int cfg_col_nick_private; +int cfg_col_nick_bg; +int cfg_col_nicklist_bg; +int cfg_col_dcc_selected; +int cfg_col_dcc_waiting; +int cfg_col_dcc_connecting; +int cfg_col_dcc_active; +int cfg_col_dcc_done; +int cfg_col_dcc_failed; +int cfg_col_dcc_aborted; + +t_config_option weechat_options_colors[] = +{ /* general color settings */ + { "col_real_white", N_("if set, uses real white color"), + N_("if set, uses real white color, disabled by default for terms with white " + "background (if you never use white background, you should turn on " + "this option to see real white instead of default term foreground color)"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_col_real_white, NULL, config_change_color }, + { "col_separator", N_("color for window separators (when splited)"), + N_("color for window separators (when splited)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "blue", NULL, &cfg_col_separator, NULL, &config_change_color }, + /* title window */ + { "col_title", N_("color for title bar"), + N_("color for title bar"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_title, NULL, &config_change_color }, + { "col_title_more", N_("color for '+' when scrolling topic"), + N_("color for '+' when scrolling topic"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightmagenta", NULL, &cfg_col_title_more, NULL, &config_change_color }, + { "col_title_bg", N_("background for title bar"), + N_("background for title bar"), + OPTION_TYPE_COLOR, 0, 0, 0, + "blue", NULL, &cfg_col_title_bg, NULL, &config_change_color }, + + /* chat window */ + { "col_chat", N_("color for chat text"), + N_("color for chat text"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_chat, NULL, &config_change_color }, + { "col_chat_time", N_("color for time"), + N_("color for time in chat window"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_chat_time, NULL, &config_change_color }, + { "col_chat_time_sep", N_("color for time separator"), + N_("color for time separator (chat window)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "brown", NULL, &cfg_col_chat_time_sep, NULL, &config_change_color }, + { "col_chat_prefix1", N_("color for 1st and 3rd char of prefix"), + N_("color for 1st and 3rd char of prefix"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightcyan", NULL, &cfg_col_chat_prefix1, NULL, &config_change_color }, + { "col_chat_prefix2", N_("color for middle char of prefix"), + N_("color for middle char of prefix"), + OPTION_TYPE_COLOR, 0, 0, 0, + "white", NULL, &cfg_col_chat_prefix2, NULL, &config_change_color }, + { "col_chat_server", N_("color for server name"), + N_("color for server name"), + OPTION_TYPE_COLOR, 0, 0, 0, + "brown", NULL, &cfg_col_chat_server, NULL, &config_change_color }, + { "col_chat_join", N_("color for join arrow (prefix)"), + N_("color for join arrow (prefix)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightgreen", NULL, &cfg_col_chat_join, NULL, &config_change_color }, + { "col_chat_part", N_("color for part/quit arrow (prefix)"), + N_("color for part/quit arrow (prefix)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightred", NULL, &cfg_col_chat_part, NULL, &config_change_color }, + { "col_chat_nick", N_("color for nicks in actions"), + N_("color for nicks in actions (chat window)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightcyan", NULL, &cfg_col_chat_nick, NULL, &config_change_color }, + { "col_chat_host", N_("color for hostnames"), + N_("color for hostnames (chat window)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "cyan", NULL, &cfg_col_chat_host, NULL, &config_change_color }, + { "col_chat_channel", N_("color for channel names in actions"), + N_("color for channel names in actions (chat window)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "white", NULL, &cfg_col_chat_channel, NULL, &config_change_color }, + { "col_chat_dark", N_("color for dark separators"), + N_("color for dark separators (chat window)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "green", NULL, &cfg_col_chat_dark, NULL, &config_change_color }, + { "col_chat_highlight", N_("color for highlighted nick"), + N_("color for highlighted nick (chat window)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "yellow", NULL, &cfg_col_chat_highlight, NULL, &config_change_color }, + { "col_chat_bg", N_("background for chat"), + N_("background for chat window"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_chat_bg, NULL, &config_change_color }, + { "col_chat_read_marker", N_("color for unread data marker"), + N_("color for unread data marker"), + OPTION_TYPE_COLOR, 0, 0, 0, + "yellow", NULL, &cfg_col_chat_read_marker, NULL, &config_change_color }, + { "col_chat_read_marker_bg", N_("background for unread data marker"), + N_("background for unread data marker"), + OPTION_TYPE_COLOR, 0, 0, 0, + "magenta", NULL, &cfg_col_chat_read_marker_bg, NULL, &config_change_color }, + + /* status window */ + { "col_status", N_("color for status bar"), + N_("color for status bar"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_status, NULL, &config_change_color }, + { "col_status_delimiters", N_("color for status bar delimiters"), + N_("color for status bar delimiters"), + OPTION_TYPE_COLOR, 0, 0, 0, + "cyan", NULL, &cfg_col_status_delimiters, NULL, &config_change_color }, + { "col_status_channel", N_("color for current channel in status bar"), + N_("color for current channel in status bar"), + OPTION_TYPE_COLOR, 0, 0, 0, + "white", NULL, &cfg_col_status_channel, NULL, &config_change_color }, + { "col_status_data_msg", N_("color for window with new messages"), + N_("color for window with new messages (status bar)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "yellow", NULL, &cfg_col_status_data_msg, NULL, &config_change_color }, + { "col_status_private", N_("color for window with private message"), + N_("color for window with private message (status bar)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightmagenta", NULL, &cfg_col_status_data_private, NULL, &config_change_color }, + { "col_status_highlight", N_("color for window with highlight"), + N_("color for window with highlight (status bar)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightred", NULL, &cfg_col_status_data_highlight, NULL, &config_change_color }, + { "col_status_data_other", N_("color for window with new data (not messages)"), + N_("color for window with new data (not messages) (status bar)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_status_data_other, NULL, &config_change_color }, + { "col_status_more", N_("color for \"-MORE-\" text"), + N_("color for window with new data (status bar)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "white", NULL, &cfg_col_status_more, NULL, &config_change_color }, + { "col_status_bg", N_("background for status window"), + N_("background for status window"), + OPTION_TYPE_COLOR, 0, 0, 0, + "blue", NULL, &cfg_col_status_bg, NULL, &config_change_color }, + + /* infobar window */ + { "col_infobar", N_("color for info bar text"), + N_("color for info bar text"), + OPTION_TYPE_COLOR, 0, 0, 0, + "black", NULL, &cfg_col_infobar, NULL, &config_change_color }, + { "col_infobar_delimiters", N_("color for infobar delimiters"), + N_("color for infobar delimiters"), + OPTION_TYPE_COLOR, 0, 0, 0, + "blue", NULL, &cfg_col_infobar_delimiters, NULL, &config_change_color }, + { "col_infobar_highlight", N_("color for info bar highlight notification"), + N_("color for info bar highlight notification"), + OPTION_TYPE_COLOR, 0, 0, 0, + "white", NULL, &cfg_col_infobar_highlight, NULL, &config_change_color }, + { "col_infobar_bg", N_("background for info bar window"), + N_("background for info bar window"), + OPTION_TYPE_COLOR, 0, 0, 0, + "cyan", NULL, &cfg_col_infobar_bg, NULL, &config_change_color }, + + /* input window */ + { "col_input", N_("color for input text"), + N_("color for input text"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_input, NULL, &config_change_color }, + { "col_input_server", N_("color for input text (server name)"), + N_("color for input text (server name)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "brown", NULL, &cfg_col_input_server, NULL, &config_change_color }, + { "col_input_channel", N_("color for input text (channel name)"), + N_("color for input text (channel name)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "white", NULL, &cfg_col_input_channel, NULL, &config_change_color }, + { "col_input_nick", N_("color for input text (nick name)"), + N_("color for input text (nick name)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightcyan", NULL, &cfg_col_input_nick, NULL, &config_change_color }, + { "col_input_delimiters", N_("color for input text (delimiters)"), + N_("color for input text (delimiters)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "cyan", NULL, &cfg_col_input_delimiters, NULL, &config_change_color }, + { "col_input_text_not_found", N_("color for text not found"), + N_("color for text not found"), + OPTION_TYPE_COLOR, 0, 0, 0, + "red", NULL, &cfg_col_input_text_not_found, NULL, &config_change_color }, + { "col_input_actions", N_("color for actions in input window"), + N_("color for actions in input window"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightgreen", NULL, &cfg_col_input_actions, NULL, &config_change_color }, + { "col_input_bg", N_("background for input window"), + N_("background for input window"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_input_bg, NULL, &config_change_color }, + + /* nick window */ + { "col_nick", N_("color for nicknames"), + N_("color for nicknames"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_nick, NULL, &config_change_color }, + { "col_nick_away", N_("color for away nicknames"), + N_("color for away nicknames"), + OPTION_TYPE_COLOR, 0, 0, 0, + "cyan", NULL, &cfg_col_nick_away, NULL, &config_change_color }, + { "col_nick_chanowner", N_("color for chan owner symbol"), + N_("color for chan owner symbol (specific to unrealircd)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightgreen", NULL, &cfg_col_nick_chanowner, NULL, &config_change_color }, + { "col_nick_chanadmin", N_("color for chan admin symbol"), + N_("color for chan admin symbol (specific to unrealircd)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightgreen", NULL, &cfg_col_nick_chanadmin, NULL, &config_change_color }, + { "col_nick_op", N_("color for operator symbol"), + N_("color for operator symbol"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightgreen", NULL, &cfg_col_nick_op, NULL, &config_change_color }, + { "col_nick_halfop", N_("color for half-operator symbol"), + N_("color for half-operator symbol"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightmagenta", NULL, &cfg_col_nick_halfop, NULL, &config_change_color }, + { "col_nick_voice", N_("color for voice symbol"), + N_("color for voice symbol"), + OPTION_TYPE_COLOR, 0, 0, 0, + "yellow", NULL, &cfg_col_nick_voice, NULL, &config_change_color }, + { "col_nick_user", N_("color for user symbol"), + N_("color for user symbol"), + OPTION_TYPE_COLOR, 0, 0, 0, + "blue", NULL, &cfg_col_nick_user, NULL, &config_change_color }, + { "col_nick_more", N_("color for '+' when scrolling nicks"), + N_("color for '+' when scrolling nicks"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightmagenta", NULL, &cfg_col_nick_more, NULL, &config_change_color }, + { "col_nick_sep", N_("color for nick separator"), + N_("color for nick separator"), + OPTION_TYPE_COLOR, 0, 0, 0, + "blue", NULL, &cfg_col_nick_sep, NULL, &config_change_color }, + { "col_nick_self", N_("color for local nick"), + N_("color for local nick"), + OPTION_TYPE_COLOR, 0, 0, 0, + "white", NULL, &cfg_col_nick_self, NULL, &config_change_color }, + { "col_nick_color1", N_("color for nick"), + N_("color for nick"), + OPTION_TYPE_COLOR, 0, 0, 0, + "cyan", NULL, &cfg_col_nick_colors[0], NULL, &config_change_color }, + { "col_nick_color2", N_("color for nick"), + N_("color for nick"), + OPTION_TYPE_COLOR, 0, 0, 0, + "magenta", NULL, &cfg_col_nick_colors[1], NULL, &config_change_color }, + { "col_nick_color3", N_("color for nick"), + N_("color for nick"), + OPTION_TYPE_COLOR, 0, 0, 0, + "green", NULL, &cfg_col_nick_colors[2], NULL, &config_change_color }, + { "col_nick_color4", N_("color for nick"), + N_("color for nick"), + OPTION_TYPE_COLOR, 0, 0, 0, + "brown", NULL, &cfg_col_nick_colors[3], NULL, &config_change_color }, + { "col_nick_color5", N_("color for nick"), + N_("color for nick"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightblue", NULL, &cfg_col_nick_colors[4], NULL, &config_change_color }, + { "col_nick_color6", N_("color for nick"), + N_("color for nick"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_nick_colors[5], NULL, &config_change_color }, + { "col_nick_color7", N_("color for nick"), + N_("color for nick"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightcyan", NULL, &cfg_col_nick_colors[6], NULL, &config_change_color }, + { "col_nick_color8", N_("color for nick"), + N_("color for nick"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightmagenta", NULL, &cfg_col_nick_colors[7], NULL, &config_change_color }, + { "col_nick_color9", N_("color for nick"), + N_("color for nick"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightgreen", NULL, &cfg_col_nick_colors[8], NULL, &config_change_color }, + { "col_nick_color10", N_("color for nick"), + N_("color for nick"), + OPTION_TYPE_COLOR, 0, 0, 0, + "blue", NULL, &cfg_col_nick_colors[9], NULL, &config_change_color }, + { "col_nick_private", N_("color for other nick in private window"), + N_("color for other nick in private window"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_nick_private, NULL, &config_change_color }, + { "col_nick_bg", N_("background for nicknames"), + N_("background for nicknames"), + OPTION_TYPE_COLOR, 0, 0, 0, + "default", NULL, &cfg_col_nick_bg, NULL, &config_change_color }, + + /* DCC */ + { "col_chat_dcc_selected", N_("color for selected DCC"), + N_("color for selected DCC (chat window)"), + OPTION_TYPE_COLOR, 0, 0, 0, + "white", NULL, &cfg_col_dcc_selected, NULL, &config_change_color }, + { "col_dcc_waiting", N_("color for \"waiting\" dcc status"), + N_("color for \"waiting\" dcc status"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightcyan", NULL, &cfg_col_dcc_waiting, NULL, &config_change_color }, + { "col_dcc_connecting", N_("color for \"connecting\" dcc status"), + N_("color for \"connecting\" dcc status"), + OPTION_TYPE_COLOR, 0, 0, 0, + "yellow", NULL, &cfg_col_dcc_connecting, NULL, &config_change_color }, + { "col_dcc_active", N_("color for \"active\" dcc status"), + N_("color for \"active\" dcc status"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightblue", NULL, &cfg_col_dcc_active, NULL, &config_change_color }, + { "col_dcc_done", N_("color for \"done\" dcc status"), + N_("color for \"done\" dcc status"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightgreen", NULL, &cfg_col_dcc_done, NULL, &config_change_color }, + { "col_dcc_failed", N_("color for \"failed\" dcc status"), + N_("color for \"failed\" dcc status"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightred", NULL, &cfg_col_dcc_failed, NULL, &config_change_color }, + { "col_dcc_aborted", N_("color for \"aborted\" dcc status"), + N_("color for \"aborted\" dcc status"), + OPTION_TYPE_COLOR, 0, 0, 0, + "lightred", NULL, &cfg_col_dcc_aborted, NULL, &config_change_color }, + + { NULL, NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } +}; + +/* config, history section */ + +int cfg_history_max_lines; +int cfg_history_max_commands; +int cfg_history_display_default; + +t_config_option weechat_options_history[] = +{ { "history_max_lines", N_("max lines in history (per window)"), + N_("maximum number of lines in history " + "for one server/channel/private window (0 = unlimited)"), + OPTION_TYPE_INT, 0, INT_MAX, 4096, + NULL, NULL, &cfg_history_max_lines, NULL, &config_change_noop }, + { "history_max_commands", N_("max user commands in history"), + N_("maximum number of user commands in history (0 = unlimited)"), + OPTION_TYPE_INT, 0, INT_MAX, 100, + NULL, NULL, &cfg_history_max_commands, NULL, &config_change_noop }, + { "history_display_default", N_("max commands to display"), + N_("maximum number of commands to display by default in history listing (0 = unlimited)"), + OPTION_TYPE_INT, 0, INT_MAX, 5, + NULL, NULL, &cfg_history_display_default, NULL, &config_change_noop }, + { NULL, NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } +}; + +/* config, log section */ + +int cfg_log_auto_server; +int cfg_log_auto_channel; +int cfg_log_auto_private; +int cfg_log_plugin_msg; +char *cfg_log_path; +char *cfg_log_timestamp; +int cfg_log_hide_nickserv_pwd; + +t_config_option weechat_options_log[] = +{ { "log_auto_server", N_("automatically log server messages"), + N_("automatically log server messages"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_log_auto_server, NULL, &config_change_log }, + { "log_auto_channel", N_("automatically log channel chats"), + N_("automatically log channel chats"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_log_auto_channel, NULL, &config_change_log }, + { "log_auto_private", N_("automatically log private chats"), + N_("automatically log private chats"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_log_auto_private, NULL, &config_change_log }, + { "log_plugin_msg", N_("log messages from plugins (scripts)"), + N_("log messages from plugins (scripts)"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_log_plugin_msg, NULL, &config_change_noop }, + { "log_path", N_("path for log files"), + N_("path for WeeChat log files ('%h' will be replaced by WeeChat home, " + "~/.weechat by default)"), + OPTION_TYPE_STRING, 0, 0, 0, + "%h/logs/", NULL, NULL, &cfg_log_path, &config_change_noop }, + { "log_timestamp", N_("timestamp for log"), + N_("timestamp for log (see man strftime for date/time specifiers)"), + OPTION_TYPE_STRING, 0, 0, 0, + "%Y %b %d %H:%M:%S", NULL, NULL, &cfg_log_timestamp, &config_change_noop }, + { "log_hide_nickserv_pwd", N_("hide password displayed by nickserv"), + N_("hide password displayed by nickserv"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_log_hide_nickserv_pwd, NULL, &config_change_noop }, + { NULL, NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } +}; + +/* config, irc section */ + +int cfg_irc_display_away; +int cfg_irc_show_away_once; +char *cfg_irc_display_away_values[] = +{ "off", "local", "channel", NULL }; +char *cfg_irc_default_msg_part; +char *cfg_irc_default_msg_quit; +int cfg_irc_notice_as_pv; +int cfg_irc_away_check; +int cfg_irc_away_check_max_nicks; +int cfg_irc_lag_check; +int cfg_irc_lag_min_show; +int cfg_irc_lag_disconnect; +int cfg_irc_anti_flood; +int cfg_irc_fifo_pipe; +char *cfg_irc_highlight; +int cfg_irc_colors_receive; +int cfg_irc_colors_send; +int cfg_irc_send_unknown_commands; + +t_config_option weechat_options_irc[] = +{ { "irc_display_away", N_("display message for away"), + N_("display message when (un)marking as away"), + OPTION_TYPE_INT_WITH_STRING, 0, 0, 0, + "off", cfg_irc_display_away_values, &cfg_irc_display_away, NULL, &config_change_noop }, + { "irc_show_away_once", N_("show remote away message only once in private"), + N_("show remote away message only once in private"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_irc_show_away_once, NULL, &config_change_noop }, + { "irc_default_msg_part", N_("default part message (leaving channel)"), + N_("default part message (leaving channel) ('%v' will be replaced by " + "WeeChat version in string)"), + OPTION_TYPE_STRING, 0, 0, 0, + "WeeChat %v", NULL, NULL, &cfg_irc_default_msg_part, &config_change_noop }, + { "irc_default_msg_quit", N_("default quit message"), + N_("default quit message ('%v' will be replaced by WeeChat version in string)"), + OPTION_TYPE_STRING, 0, 0, 0, + "WeeChat %v", NULL, NULL, &cfg_irc_default_msg_quit, &config_change_noop }, + { "irc_notice_as_pv", N_("display notices as private messages"), + N_("display notices as private messages"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_irc_notice_as_pv, NULL, &config_change_noop }, + { "irc_away_check", N_("interval between two checks for away"), + N_("interval between two checks for away (in minutes, 0 = never check)"), + OPTION_TYPE_INT, 0, INT_MAX, 0, + NULL, NULL, &cfg_irc_away_check, NULL, &config_change_away_check }, + { "irc_away_check_max_nicks", N_("max number of nicks for away check"), + N_("do not check away nicks on channels with high number of nicks (0 = unlimited)"), + OPTION_TYPE_INT, 0, INT_MAX, 0, + NULL, NULL, &cfg_irc_away_check_max_nicks, NULL, &config_change_away_check }, + { "irc_lag_check", N_("interval between two checks for lag"), + N_("interval between two checks for lag (in seconds)"), + OPTION_TYPE_INT, 30, INT_MAX, 60, + NULL, NULL, &cfg_irc_lag_check, NULL, &config_change_noop }, + { "irc_lag_min_show", N_("minimum lag to show"), + N_("minimum lag to show (in seconds)"), + OPTION_TYPE_INT, 0, INT_MAX, 1, + NULL, NULL, &cfg_irc_lag_min_show, NULL, &config_change_noop }, + { "irc_lag_disconnect", N_("disconnect after important lag"), + N_("disconnect after important lag (in minutes, 0 = never disconnect)"), + OPTION_TYPE_INT, 0, INT_MAX, 5, + NULL, NULL, &cfg_irc_lag_disconnect, NULL, &config_change_noop }, + { "irc_anti_flood", N_("anti-flood"), + N_("anti-flood: # seconds between two user messages (0 = no anti-flood)"), + OPTION_TYPE_INT, 0, 5, 2, + NULL, NULL, &cfg_irc_anti_flood, NULL, &config_change_noop }, + { "irc_fifo_pipe", N_("create a FIFO pipe for remote control"), + N_("create a FIFO pipe for remote control"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_irc_fifo_pipe, NULL, &config_change_fifo_pipe }, + { "irc_highlight", N_("list of words to highlight"), + N_("comma separated list of words to highlight (case insensitive comparison, " + "words may begin or end with \"*\" for partial match)"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &cfg_irc_highlight, &config_change_noop }, + { "irc_colors_receive", N_("when off, colors codes are ignored in " + "incoming messages"), + N_("when off, colors codes are ignored in incoming messages"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_irc_colors_receive, NULL, config_change_noop }, + { "irc_colors_send", N_("allow user to send colors"), + N_("allow user to send colors with special codes (^Cb=bold, ^Ccxx=color, " + "^Ccxx,yy=color+background, ^Cu=underline, ^Cr=reverse)"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_irc_colors_send, NULL, config_change_noop }, + { "irc_send_unknown_commands", N_("send unknown commands to IRC server"), + N_("send unknown commands to IRC server"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_irc_send_unknown_commands, NULL, &config_change_noop }, + { NULL, NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } +}; + +/* config, dcc section */ + +int cfg_dcc_auto_accept_files; +int cfg_dcc_auto_accept_chats; +int cfg_dcc_timeout; +int cfg_dcc_blocksize; +int cfg_dcc_fast_send; +char *cfg_dcc_port_range; +char *cfg_dcc_own_ip; +char *cfg_dcc_download_path; +char *cfg_dcc_upload_path; +int cfg_dcc_convert_spaces; +int cfg_dcc_auto_rename; +int cfg_dcc_auto_resume; + +t_config_option weechat_options_dcc[] = +{ { "dcc_auto_accept_files", N_("automatically accept dcc files"), + N_("automatically accept incoming dcc files"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_dcc_auto_accept_files, NULL, &config_change_noop }, + { "dcc_auto_accept_chats", N_("automatically accept dcc chats"), + N_("automatically accept dcc chats (use carefully!)"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_dcc_auto_accept_chats, NULL, &config_change_noop }, + { "dcc_timeout", N_("timeout for dcc request"), + N_("timeout for dcc request (in seconds)"), + OPTION_TYPE_INT, 5, INT_MAX, 300, + NULL, NULL, &cfg_dcc_timeout, NULL, &config_change_noop }, + { "dcc_blocksize", N_("block size for dcc packets"), + N_("block size for dcc packets in bytes (default: 65536)"), + OPTION_TYPE_INT, IRC_DCC_MIN_BLOCKSIZE, IRC_DCC_MAX_BLOCKSIZE, 65536, + NULL, NULL, &cfg_dcc_blocksize, NULL, &config_change_noop }, + { "dcc_fast_send", N_("does not wait for ACK when sending file"), + N_("does not wait for ACK when sending file"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_dcc_fast_send, NULL, &config_change_noop }, + { "dcc_port_range", N_("allowed ports for outgoing dcc"), + N_("restricts outgoing dcc to use only ports in the given range " + "(useful for NAT) (syntax: a single port, ie. 5000 or a port " + "range, ie. 5000-5015, empty value means any port)"), + OPTION_TYPE_STRING, 0, 0, 0, "", + NULL, NULL, &cfg_dcc_port_range, &config_change_noop }, + { "dcc_own_ip", N_("IP address for outgoing dcc"), + N_("IP or DNS address used for outgoing dcc " + "(if empty, local interface IP is used)"), + OPTION_TYPE_STRING, 0, 0, 0, "", + NULL, NULL, &cfg_dcc_own_ip, &config_change_noop }, + { "dcc_download_path", N_("path for incoming files with dcc"), + N_("path for writing incoming files with dcc (default: user home)"), + OPTION_TYPE_STRING, 0, 0, 0, + "%h/dcc", NULL, NULL, &cfg_dcc_download_path, &config_change_noop }, + { "dcc_upload_path", N_("default path for sending files with dcc"), + N_("path for reading files when sending thru dcc (when no path is specified)"), + OPTION_TYPE_STRING, 0, 0, 0, "~", + NULL, NULL, &cfg_dcc_upload_path, &config_change_noop }, + { "dcc_convert_spaces", N_("convert spaces to underscores when sending files"), + N_("convert spaces to underscores when sending files"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_dcc_convert_spaces, NULL, &config_change_noop }, + { "dcc_auto_rename", N_("automatically rename dcc files if already exists"), + N_("rename incoming files if already exists (add '.1', '.2', ...)"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_dcc_auto_rename, NULL, &config_change_noop }, + { "dcc_auto_resume", N_("automatically resume aborted transfers"), + N_("automatically resume dcc transfer if connection with remote host is loosed"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &cfg_dcc_auto_resume, NULL, &config_change_noop }, + { NULL, NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } +}; + +/* config, proxy section */ + +int cfg_proxy_use; +int cfg_proxy_type; +int cfg_proxy_ipv6; +char *cfg_proxy_type_values[] = +{ "http", "socks4", "socks5", NULL }; +char *cfg_proxy_address; +int cfg_proxy_port; +char *cfg_proxy_username; +char *cfg_proxy_password; + +t_config_option weechat_options_proxy[] = +{ { "proxy_use", N_("use proxy"), + N_("use a proxy server to connect to irc server"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_proxy_use, NULL, &config_change_noop }, + { "proxy_type", N_("proxy type"), + N_("proxy type (http (default), socks4, socks5)"), + OPTION_TYPE_INT_WITH_STRING, 0, 0, 0, + "http", cfg_proxy_type_values, &cfg_proxy_type, NULL, &config_change_noop }, + { "proxy_ipv6", N_("use ipv6 proxy"), + N_("connect to proxy in ipv6"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &cfg_proxy_ipv6, NULL, &config_change_noop }, + { "proxy_address", N_("proxy address"), + N_("proxy server address (IP or hostname)"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &cfg_proxy_address, &config_change_noop }, + { "proxy_port", N_("port for proxy"), + N_("port for connecting to proxy server"), + OPTION_TYPE_INT, 0, 65535, 3128, + NULL, NULL, &cfg_proxy_port, NULL, &config_change_noop }, + { "proxy_username", N_("proxy username"), + N_("username for proxy server"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &cfg_proxy_username, &config_change_noop }, + { "proxy_password", N_("proxy password"), + N_("password for proxy server"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &cfg_proxy_password, &config_change_noop }, + { NULL, NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } +}; + +/* config, plugins section */ + +char *cfg_plugins_path; +char *cfg_plugins_autoload; +char *cfg_plugins_extension; + +t_config_option weechat_options_plugins[] = +{ { "plugins_path", N_("path for searching plugins"), + N_("path for searching plugins ('%h' will be replaced by WeeChat home, " + "~/.weechat by default)"), + OPTION_TYPE_STRING, 0, 0, 0, + "%h/plugins", NULL, NULL, &cfg_plugins_path, &config_change_noop }, + { "plugins_autoload", N_("list of plugins to load automatically"), + N_("comma separated list of plugins to load automatically at startup, " + "\"*\" means all plugins found " + "(names may be partial, for example \"perl\" is ok for \"libperl.so\")"), + OPTION_TYPE_STRING, 0, 0, 0, + "*", NULL, NULL, &cfg_plugins_autoload, &config_change_noop }, + { "plugins_extension", N_("standard plugins extension in filename"), + N_("standard plugins extension in filename, used for autoload " + "(if empty, then all files are loaded when autoload is \"*\")"), + OPTION_TYPE_STRING, 0, 0, 0, +#ifdef WIN32 + ".dll", +#else + ".so", +#endif + NULL, NULL, &cfg_plugins_extension, &config_change_noop }, + { NULL, NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } +}; + +/* config, server section */ + +static t_irc_server cfg_server; + +t_config_option weechat_options_server[] = +{ { "server_name", N_("server name"), + N_("name associated to IRC server (for display only)"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.name), NULL }, + { "server_autoconnect", N_("automatically connect to server"), + N_("automatically connect to server when WeeChat is starting"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &(cfg_server.autoconnect), NULL, NULL }, + { "server_autoreconnect", N_("automatically reconnect to server"), + N_("automatically reconnect to server when disconnected"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &(cfg_server.autoreconnect), NULL, NULL }, + { "server_autoreconnect_delay", N_("delay before trying again to reconnect"), + N_("delay (in seconds) before trying again to reconnect to server"), + OPTION_TYPE_INT, 0, 65535, 30, + NULL, NULL, &(cfg_server.autoreconnect_delay), NULL, NULL }, + { "server_address", N_("server address or hostname"), + N_("IP address or hostname of IRC server"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.address), NULL }, + { "server_port", N_("port for IRC server"), + N_("port for connecting to server"), + OPTION_TYPE_INT, 0, 65535, 6667, + NULL, NULL, &(cfg_server.port), NULL, NULL }, + { "server_ipv6", N_("use IPv6 protocol for server communication"), + N_("use IPv6 protocol for server communication"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &(cfg_server.ipv6), NULL, NULL }, + { "server_ssl", N_("use SSL for server communication"), + N_("use SSL for server communication"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_FALSE, + NULL, NULL, &(cfg_server.ssl), NULL, NULL }, + { "server_password", N_("server password"), + N_("password for IRC server"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.password), NULL }, + { "server_nick1", N_("nickname for server"), + N_("nickname to use on IRC server"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.nick1), NULL }, + { "server_nick2", N_("alternate nickname for server"), + N_("alternate nickname to use on IRC server (if nickname is already used)"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.nick2), NULL }, + { "server_nick3", N_("2nd alternate nickname for server"), + N_("2nd alternate nickname to use on IRC server (if alternate nickname is already used)"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.nick3), NULL }, + { "server_username", N_("user name for server"), + N_("user name to use on IRC server"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.username), NULL }, + { "server_realname", N_("real name for server"), + N_("real name to use on IRC server"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.realname), NULL }, + { "server_hostname", N_("custom hostname/IP for server"), + N_("custom hostname/IP for server (optional, if empty local hostname is used)"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.hostname), NULL }, + { "server_command", N_("command(s) to run when connected to server"), + N_("command(s) to run when connected to server (many commands should be " + "separated by ';', use '\\;' for a semicolon, special variables $nick, " + "$channel and $server are replaced by their value)"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.command), NULL }, + { "server_command_delay", N_("delay (in seconds) after command was executed"), + N_("delay (in seconds) after command was executed (example: give some time for authentication)"), + OPTION_TYPE_INT, 0, 3600, 0, + NULL, NULL, &(cfg_server.command_delay), NULL, NULL }, + { "server_autojoin", N_("list of channels to join when connected to server"), + N_("comma separated list of channels to join when connected to server (example: \"#chan1,#chan2,#chan3 key1,key2\")"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.autojoin), NULL }, + { "server_autorejoin", N_("automatically rejoin channels when kicked"), + N_("automatically rejoin channels when kicked"), + OPTION_TYPE_BOOLEAN, BOOL_FALSE, BOOL_TRUE, BOOL_TRUE, + NULL, NULL, &(cfg_server.autorejoin), NULL, NULL }, + { "server_notify_levels", N_("notify levels for channels of this server"), + N_("comma separated list of notify levels for channels of this server " + "(format: #channel:1,..), a channel name '*' is reserved for server " + "default notify level"), + OPTION_TYPE_STRING, 0, 0, 0, + "", NULL, NULL, &(cfg_server.notify_levels), config_change_notify_levels }, + { NULL, NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } +}; + +/* all weechat options */ + +t_config_option *weechat_options[CONFIG_NUMBER_SECTIONS] = +{ weechat_options_look, weechat_options_colors, weechat_options_history, + weechat_options_log, weechat_options_irc, weechat_options_dcc, + weechat_options_proxy, weechat_options_plugins, NULL, NULL, NULL, + weechat_options_server +}; + + +/* + * config_get_pos_array_values: return position of a string in an array of values + * return -1 if not found, otherwise position + */ + +int +config_get_pos_array_values (char **array, char *string) +{ + int i; + + i = 0; + while (array[i]) + { + if (ascii_strcasecmp (array[i], string) == 0) + return i; + i++; + } + /* string not found in array */ + return -1; +} + +/* + * config_get_section: get section name from option pointer + */ + +char * +config_get_section (t_config_option *ptr_option) +{ + int i, j; + + for (i = 0; i < CONFIG_NUMBER_SECTIONS; i++) + { + if ((i != CONFIG_SECTION_KEYS) && (i != CONFIG_SECTION_ALIAS) + && (i != CONFIG_SECTION_IGNORE) && (i != CONFIG_SECTION_SERVER)) + { + for (j = 0; weechat_options[i][j].option_name; j++) + { + /* if option found, return pointer to section name */ + if (ptr_option == &weechat_options[i][j]) + return config_sections[i].section_name; + } + } + } + /* option not found */ + return NULL; +} + +/* + * config_change_noop: called when an option is changed by /set command + * and that no special action is needed after that + */ + +void +config_change_noop () +{ + /* do nothing */ +} + +/* + * config_change_save_on_exit: called when "save_on_exit" flag is changed + */ + +void +config_change_save_on_exit () +{ + if (!cfg_look_save_on_exit) + { + gui_printf (NULL, "\n"); + gui_printf (NULL, _("%s you should now issue /save to write " + "\"save_on_exit\" option in config file.\n"), + WEECHAT_WARNING); + } +} + +/* + * config_change_title: called when title is changed + */ + +void +config_change_title () +{ + if (cfg_look_set_title) + gui_window_set_title (); + else + gui_window_reset_title (); +} + +/* + * config_change_buffers: called when buffers change (for example nicklist) + */ + +void +config_change_buffers () +{ + gui_window_refresh_windows (); +} + +/* + * config_change_buffer_content: called when content of a buffer changes + */ + +void +config_change_buffer_content () +{ + gui_window_redraw_buffer (gui_current_window->buffer); +} + +/* + * config_change_hotlist: called when hotlist changes + */ + +void +config_change_hotlist () +{ + hotlist_resort (); + gui_status_draw (gui_current_window->buffer, 1); +} + +/* + * config_change_read_marker: called when read marker is changed + */ + +void +config_change_read_marker () +{ + gui_window_redraw_all_buffers (); +} + +/* + * config_change_one_server_buffer: called when the "one server buffer" + * setting is changed + */ + +void +config_change_one_server_buffer () +{ + if (cfg_look_one_server_buffer) + gui_buffer_merge_servers (gui_current_window); + else + gui_buffer_split_server (gui_current_window); +} + +/* + * config_change_color: called when a color is changed by /set command + */ + +void +config_change_color () +{ + gui_color_init_pairs (); + gui_color_rebuild_weechat (); + gui_window_refresh_windows (); +} + +/* + * config_change_nicks_colors: called when number of nicks color changed + */ + +void +config_change_nicks_colors () +{ + t_irc_server *ptr_server; + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (ptr_server->is_connected) + { + for (ptr_channel = ptr_server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + for (ptr_nick = ptr_channel->nicks; ptr_nick; + ptr_nick = ptr_nick->next_nick) + { + if (ptr_nick->color != GUI_COLOR_WIN_NICK_SELF) + ptr_nick->color = irc_nick_find_color (ptr_nick); + } + } + } + } +} + +/* + * config_change_away_check: called when away check is changed + */ + +void +config_change_away_check () +{ + if (cfg_irc_away_check == 0) + { + /* reset away flag for all nicks/chans/servers */ + irc_server_remove_away (); + } + irc_check_away = cfg_irc_away_check * 60; +} + +/* + * config_change_fifo_pipe: called when FIFO pipe is changed + */ + +void +config_change_fifo_pipe () +{ + if (cfg_irc_fifo_pipe) + { + if (weechat_fifo == -1) + fifo_create (); + } + else + { + if (weechat_fifo != -1) + fifo_remove (); + } +} + +/* + * config_change_notify_levels: called when notify levels is changed for a server + */ + +void +config_change_notify_levels () +{ + t_gui_buffer *ptr_buffer; + + for (ptr_buffer = gui_buffers; ptr_buffer; ptr_buffer = ptr_buffer->next_buffer) + { + if (GUI_BUFFER_IS_CHANNEL(ptr_buffer) || GUI_BUFFER_IS_PRIVATE(ptr_buffer)) + ptr_buffer->notify_level = + irc_channel_get_notify_level (GUI_SERVER(ptr_buffer), GUI_CHANNEL(ptr_buffer)); + } +} + +/* + * config_change_log: called when log settings are changed (for server/channel/private logging) + */ + +void +config_change_log () +{ + t_gui_buffer *ptr_buffer; + + for (ptr_buffer = gui_buffers; ptr_buffer; + ptr_buffer = ptr_buffer->next_buffer) + { + if (GUI_BUFFER_IS_SERVER(ptr_buffer)) + { + if (cfg_log_auto_server && !ptr_buffer->log_file) + gui_log_start (ptr_buffer); + else if (!cfg_log_auto_server && ptr_buffer->log_file) + gui_log_end (ptr_buffer); + } + if (GUI_BUFFER_IS_CHANNEL(ptr_buffer)) + { + if (cfg_log_auto_channel && !ptr_buffer->log_file) + gui_log_start (ptr_buffer); + else if (!cfg_log_auto_channel && ptr_buffer->log_file) + gui_log_end (ptr_buffer); + } + if (GUI_BUFFER_IS_PRIVATE(ptr_buffer)) + { + if (cfg_log_auto_private && !ptr_buffer->log_file) + gui_log_start (ptr_buffer); + else if (!cfg_log_auto_private && ptr_buffer->log_file) + gui_log_end (ptr_buffer); + } + } +} + +/* + * config_option_get_boolean_value: get boolean value with user text + * return: BOOL_FALSE or BOOL_TRUE + */ + +int +config_option_get_boolean_value (char *text) +{ + if ((ascii_strcasecmp (text, "on") == 0) + || (ascii_strcasecmp (text, "yes") == 0) + || (ascii_strcasecmp (text, "y") == 0) + || (ascii_strcasecmp (text, "true") == 0) + || (ascii_strcasecmp (text, "t") == 0) + || (ascii_strcasecmp (text, "1") == 0)) + return BOOL_TRUE; + + if ((ascii_strcasecmp (text, "off") == 0) + || (ascii_strcasecmp (text, "no") == 0) + || (ascii_strcasecmp (text, "n") == 0) + || (ascii_strcasecmp (text, "false") == 0) + || (ascii_strcasecmp (text, "f") == 0) + || (ascii_strcasecmp (text, "0") == 0)) + return BOOL_FALSE; + + /* invalid text */ + return -1; +} + +/* + * config_option_set_value: set new value for an option + * return: 0 if success + * -1 if error (bad value) + */ + +int +config_option_set_value (t_config_option *option, char *value) +{ + int int_value; + + switch (option->option_type) + { + case OPTION_TYPE_BOOLEAN: + int_value = config_option_get_boolean_value (value); + switch (int_value) + { + case BOOL_TRUE: + *(option->ptr_int) = BOOL_TRUE; + break; + case BOOL_FALSE: + *(option->ptr_int) = BOOL_FALSE; + break; + default: + return -1; + } + break; + case OPTION_TYPE_INT: + int_value = atoi (value); + if ((int_value < option->min) || (int_value > option->max)) + return -1; + *(option->ptr_int) = int_value; + break; + case OPTION_TYPE_INT_WITH_STRING: + int_value = config_get_pos_array_values (option->array_values, + value); + if (int_value < 0) + return -1; + *(option->ptr_int) = int_value; + break; + case OPTION_TYPE_COLOR: + if (!gui_color_assign (option->ptr_int, value)) + return -1; + break; + case OPTION_TYPE_STRING: + if ((option->max > 0) && (utf8_strlen (value) > option->max)) + return -1; + if (*(option->ptr_string)) + free (*(option->ptr_string)); + *(option->ptr_string) = strdup (value); + break; + } + return 0; +} + +/* + * config_option_list_remove: remove an item from a list for an option + * (for options with value like: "abc:1,def:blabla") + */ + +void +config_option_list_remove (char **string, char *item) +{ + char *name, *pos, *pos2; + + if (!string || !(*string)) + return; + + name = (char *) malloc (strlen (item) + 2); + strcpy (name, item); + strcat (name, ":"); + pos = ascii_strcasestr (*string, name); + free (name); + if (pos) + { + pos2 = pos + strlen (item); + if (pos2[0] == ':') + { + pos2++; + if (pos2[0]) + { + while (pos2[0] && (pos2[0] != ',')) + pos2++; + if (pos2[0] == ',') + pos2++; + if (!pos2[0] && (pos != (*string))) + pos--; + strcpy (pos, pos2); + if (!(*string)[0]) + { + free (*string); + *string = NULL; + } + else + (*string) = (char *) realloc (*string, strlen (*string) + 1); + } + } + } +} + +/* + * config_option_list_set: set an item from a list for an option + * (for options with value like: "abc:1,def:blabla") + */ + +void +config_option_list_set (char **string, char *item, char *value) +{ + config_option_list_remove (string, item); + + if (!(*string)) + { + (*string) = (char *) malloc (strlen (item) + 1 + strlen (value) + 1); + (*string)[0] = '\0'; + } + else + (*string) = (char *) realloc (*string, + strlen (*string) + 1 + + strlen (item) + 1 + strlen (value) + 1); + + if ((*string)[0]) + strcat (*string, ","); + strcat (*string, item); + strcat (*string, ":"); + strcat (*string, value); +} + +/* + * config_option_list_get_value: return position of item value in the list + * (for options with value like: "abc:1,def:blabla") + */ + +void +config_option_list_get_value (char **string, char *item, + char **pos_found, int *length) +{ + char *name, *pos, *pos2, *pos_comma; + + *pos_found = NULL; + *length = 0; + + if (!string || !(*string)) + return; + + name = (char *) malloc (strlen (item) + 2); + strcpy (name, item); + strcat (name, ":"); + pos = ascii_strcasestr (*string, name); + free (name); + if (pos) + { + pos2 = pos + strlen (item); + if (pos2[0] == ':') + { + pos2++; + *pos_found = pos2; + pos_comma = strchr (pos2, ','); + if (pos_comma) + *length = pos_comma - pos2; + else + *length = strlen (pos2); + } + } +} + +/* + * config_get_server_option_ptr: get a pointer to a server config option + */ + +void * +config_get_server_option_ptr (t_irc_server *server, char *option_name) +{ + if (ascii_strcasecmp (option_name, "server_name") == 0) + return (void *)(&server->name); + if (ascii_strcasecmp (option_name, "server_autoconnect") == 0) + return (void *)(&server->autoconnect); + if (ascii_strcasecmp (option_name, "server_autoreconnect") == 0) + return (void *)(&server->autoreconnect); + if (ascii_strcasecmp (option_name, "server_autoreconnect_delay") == 0) + return (void *)(&server->autoreconnect_delay); + if (ascii_strcasecmp (option_name, "server_address") == 0) + return (void *)(&server->address); + if (ascii_strcasecmp (option_name, "server_port") == 0) + return (void *)(&server->port); + if (ascii_strcasecmp (option_name, "server_ipv6") == 0) + return (void *)(&server->ipv6); + if (ascii_strcasecmp (option_name, "server_ssl") == 0) + return (void *)(&server->ssl); + if (ascii_strcasecmp (option_name, "server_password") == 0) + return (void *)(&server->password); + if (ascii_strcasecmp (option_name, "server_nick1") == 0) + return (void *)(&server->nick1); + if (ascii_strcasecmp (option_name, "server_nick2") == 0) + return (void *)(&server->nick2); + if (ascii_strcasecmp (option_name, "server_nick3") == 0) + return (void *)(&server->nick3); + if (ascii_strcasecmp (option_name, "server_username") == 0) + return (void *)(&server->username); + if (ascii_strcasecmp (option_name, "server_realname") == 0) + return (void *)(&server->realname); + if (ascii_strcasecmp (option_name, "server_hostname") == 0) + return (void *)(&server->hostname); + if (ascii_strcasecmp (option_name, "server_command") == 0) + return (void *)(&server->command); + if (ascii_strcasecmp (option_name, "server_command_delay") == 0) + return (void *)(&server->command_delay); + if (ascii_strcasecmp (option_name, "server_autojoin") == 0) + return (void *)(&server->autojoin); + if (ascii_strcasecmp (option_name, "server_autorejoin") == 0) + return (void *)(&server->autorejoin); + if (ascii_strcasecmp (option_name, "server_notify_levels") == 0) + return (void *)(&server->notify_levels); + /* option not found */ + return NULL; +} + +/* + * config_set_server_value: set new value for an option + * return: 0 if success + * -1 if option not found + * -2 if bad value + */ + +int +config_set_server_value (t_irc_server *server, char *option_name, + char *value) +{ + t_config_option *ptr_option; + int i; + void *ptr_data; + int int_value; + + ptr_data = config_get_server_option_ptr (server, option_name); + if (!ptr_data) + return -1; + + ptr_option = NULL; + for (i = 0; weechat_options[CONFIG_SECTION_SERVER][i].option_name; i++) + { + /* if option found, return pointer */ + if (ascii_strcasecmp (weechat_options[CONFIG_SECTION_SERVER][i].option_name, option_name) == 0) + { + ptr_option = &weechat_options[CONFIG_SECTION_SERVER][i]; + break; + } + } + if (!ptr_option) + return -1; + + switch (ptr_option->option_type) + { + case OPTION_TYPE_BOOLEAN: + int_value = config_option_get_boolean_value (value); + switch (int_value) + { + case BOOL_TRUE: + *((int *)(ptr_data)) = BOOL_TRUE; + break; + case BOOL_FALSE: + *((int *)(ptr_data)) = BOOL_FALSE; + break; + default: + return -2; + } + break; + case OPTION_TYPE_INT: + int_value = atoi (value); + if ((int_value < ptr_option->min) || (int_value > ptr_option->max)) + return -2; + *((int *)(ptr_data)) = int_value; + break; + case OPTION_TYPE_INT_WITH_STRING: + int_value = config_get_pos_array_values (ptr_option->array_values, + value); + if (int_value < 0) + return -2; + *((int *)(ptr_data)) = int_value; + break; + case OPTION_TYPE_COLOR: + if (!gui_color_assign ((int *)ptr_data, value)) + return -2; + break; + case OPTION_TYPE_STRING: + if (*((char **)ptr_data)) + free (*((char **)ptr_data)); + *((char **)ptr_data) = strdup (value); + break; + } + if (ptr_option->handler_change != NULL) + (void) (ptr_option->handler_change()); + return 0; +} + +/* + * config_option_search: look for an option and return pointer to this option + * if option is not found, NULL is returned + */ + +t_config_option * +config_option_search (char *option_name) +{ + int i, j; + + for (i = 0; i < CONFIG_NUMBER_SECTIONS; i++) + { + if ((i != CONFIG_SECTION_KEYS) && (i != CONFIG_SECTION_ALIAS) + && (i != CONFIG_SECTION_IGNORE) && (i != CONFIG_SECTION_SERVER)) + { + for (j = 0; weechat_options[i][j].option_name; j++) + { + /* if option found, return pointer */ + if (ascii_strcasecmp (weechat_options[i][j].option_name, option_name) == 0) + return &weechat_options[i][j]; + } + } + } + /* option not found */ + return NULL; +} + +/* + * config_option_search_option_value: look for type and value of an option + * (including server options) + * if option is not found, NULL is returned + */ + +void +config_option_search_option_value (char *option_name, t_config_option **option, + void **option_value) +{ + t_config_option *ptr_option; + t_irc_server *ptr_server; + int i; + void *ptr_value; + char *pos; + + ptr_option = NULL; + ptr_value = NULL; + + ptr_option = config_option_search (option_name); + if (!ptr_option) + { + pos = strchr (option_name, '.'); + if (pos) + { + pos[0] = '\0'; + ptr_server = irc_server_search (option_name); + if (ptr_server) + { + for (i = 0; weechat_options[CONFIG_SECTION_SERVER][i].option_name; i++) + { + if (strcmp (weechat_options[CONFIG_SECTION_SERVER][i].option_name, + pos + 1) == 0) + { + ptr_option = &weechat_options[CONFIG_SECTION_SERVER][i]; + ptr_value = config_get_server_option_ptr (ptr_server, pos + 1); + break; + } + } + } + pos[0] = '.'; + } + } + else + { + switch (ptr_option->option_type) + { + case OPTION_TYPE_BOOLEAN: + case OPTION_TYPE_INT: + case OPTION_TYPE_INT_WITH_STRING: + case OPTION_TYPE_COLOR: + ptr_value = (void *)(ptr_option->ptr_int); + break; + case OPTION_TYPE_STRING: + ptr_value = (void *)(ptr_option->ptr_string); + break; + } + } + + if (ptr_option) + { + *option = ptr_option; + *option_value = ptr_value; + } +} + +/* + * config_set_value: set new value for an option (found by name) + * return: 0 if success + * -1 if bad value for option + * -2 if option is not found + */ + +int +config_set_value (char *option_name, char *value) +{ + t_config_option *ptr_option; + + ptr_option = config_option_search (option_name); + if (ptr_option) + return config_option_set_value (ptr_option, value); + else + return -2; +} + +/* + * config_allocate_server: allocate a new server + */ + +int +config_allocate_server (char *filename, int line_number) +{ + if (!cfg_server.name + || !cfg_server.address + || cfg_server.port < 0 + || !cfg_server.nick1 + || !cfg_server.nick2 + || !cfg_server.nick3 + || !cfg_server.username + || !cfg_server.realname) + { + irc_server_free_all (); + gui_printf (NULL, + _("%s %s, line %d: new server, but previous was incomplete\n"), + WEECHAT_WARNING, filename, line_number); + return 0; + + } + if (irc_server_name_already_exists (cfg_server.name)) + { + irc_server_free_all (); + gui_printf (NULL, + _("%s %s, line %d: server '%s' already exists\n"), + WEECHAT_WARNING, filename, line_number, cfg_server.name); + return 0; + } + if (!irc_server_new (cfg_server.name, + cfg_server.autoconnect, cfg_server.autoreconnect, + cfg_server.autoreconnect_delay, 0, cfg_server.address, + cfg_server.port, cfg_server.ipv6, cfg_server.ssl, + cfg_server.password, cfg_server.nick1, cfg_server.nick2, + cfg_server.nick3, cfg_server.username, cfg_server.realname, + cfg_server.hostname, cfg_server.command, + cfg_server.command_delay, cfg_server.autojoin, + cfg_server.autorejoin, cfg_server.notify_levels)) + { + irc_server_free_all (); + gui_printf (NULL, + _("%s %s, line %d: unable to create server\n"), + WEECHAT_WARNING, filename, line_number); + return 0; + } + + irc_server_destroy (&cfg_server); + irc_server_init (&cfg_server); + + return 1; +} + +/* + * config_default_values: initialize config variables with default values + */ + +void +config_default_values () +{ + int i, j, int_value; + + for (i = 0; i < CONFIG_NUMBER_SECTIONS; i++) + { + if ((i != CONFIG_SECTION_KEYS) && (i != CONFIG_SECTION_ALIAS) + && (i != CONFIG_SECTION_IGNORE) && (i != CONFIG_SECTION_SERVER)) + { + for (j = 0; weechat_options[i][j].option_name; j++) + { + switch (weechat_options[i][j].option_type) + { + case OPTION_TYPE_BOOLEAN: + case OPTION_TYPE_INT: + *weechat_options[i][j].ptr_int = + weechat_options[i][j].default_int; + break; + case OPTION_TYPE_INT_WITH_STRING: + int_value = config_get_pos_array_values ( + weechat_options[i][j].array_values, + weechat_options[i][j].default_string); + if (int_value < 0) + gui_printf (NULL, + _("%s unable to assign default int with string (\"%s\")\n"), + WEECHAT_WARNING, weechat_options[i][j].default_string); + else + *weechat_options[i][j].ptr_int = + int_value; + break; + case OPTION_TYPE_COLOR: + if (!gui_color_assign ( + weechat_options[i][j].ptr_int, + weechat_options[i][j].default_string)) + gui_printf (NULL, + _("%s unable to assign default color (\"%s\")\n"), + WEECHAT_WARNING, weechat_options[i][j].default_string); + break; + case OPTION_TYPE_STRING: + *weechat_options[i][j].ptr_string = + strdup (weechat_options[i][j].default_string); + break; + } + } + } + } +} + +/* + * config_read: read WeeChat configuration + * returns: 0 = successful + * -1 = config file file not found + * < -1 = other error (fatal) + */ + +int +config_read () +{ + int filename_length; + char *filename; + FILE *file; + int section, line_number, i, option_number; + int server_found; + char line[1024], *ptr_line, *ptr_line2, *pos, *pos2; + + filename_length = strlen (weechat_home) + strlen (WEECHAT_CONFIG_NAME) + 2; + filename = (char *) malloc (filename_length * sizeof (char)); + if (!filename) + return -2; + snprintf (filename, filename_length, "%s%s" WEECHAT_CONFIG_NAME, + weechat_home, DIR_SEPARATOR); + if ((file = fopen (filename, "r")) == NULL) + { + gui_printf (NULL, _("%s config file \"%s\" not found.\n"), + WEECHAT_WARNING, filename); + free (filename); + return -1; + } + + config_default_values (); + irc_server_init (&cfg_server); + + /* read config file */ + section = CONFIG_SECTION_NONE; + server_found = 0; + line_number = 0; + while (!feof (file)) + { + ptr_line = fgets (line, sizeof (line) - 1, file); + line_number++; + if (ptr_line) + { + /* encode line to internal charset */ + ptr_line2 = weechat_iconv_to_internal (NULL, ptr_line); + if (ptr_line2) + { + snprintf (line, sizeof (line) - 1, "%s", ptr_line2); + free (ptr_line2); + } + + /* skip spaces */ + while (ptr_line[0] == ' ') + ptr_line++; + /* not a comment and not an empty line */ + if ((ptr_line[0] != '#') && (ptr_line[0] != '\r') + && (ptr_line[0] != '\n')) + { + /* beginning of section */ + if (ptr_line[0] == '[') + { + pos = strchr (line, ']'); + if (pos == NULL) + gui_printf (NULL, + _("%s %s, line %d: invalid syntax, missing \"]\"\n"), + WEECHAT_WARNING, filename, line_number); + else + { + pos[0] = '\0'; + pos = ptr_line + 1; + section = CONFIG_SECTION_NONE; + for (i = 0; config_sections[i].section_name; i++) + { + if (strcmp (config_sections[i].section_name, pos) == 0) + { + section = i; + break; + } + } + if (section == CONFIG_SECTION_NONE) + gui_printf (NULL, + _("%s %s, line %d: unknown section identifier (\"%s\")\n"), + WEECHAT_WARNING, filename, line_number, pos); + else + { + if (server_found) + { + /* if server already started => create it */ + config_allocate_server (filename, line_number); + } + server_found = (section == CONFIG_SECTION_SERVER) ? 1 : 0; + } + } + } + else + { + if (section == CONFIG_SECTION_NONE) + { + gui_printf (NULL, + _("%s %s, line %d: invalid section for option, line is ignored\n"), + WEECHAT_WARNING, filename, line_number); + } + else + { + pos = strchr (line, '='); + if (pos == NULL) + gui_printf (NULL, + _("%s %s, line %d: invalid syntax, missing \"=\"\n"), + WEECHAT_WARNING, filename, line_number); + else + { + pos[0] = '\0'; + pos++; + + /* remove spaces before '=' */ + pos2 = pos - 2; + while ((pos2 > line) && (pos2[0] == ' ')) + { + pos2[0] = '\0'; + pos2--; + } + + /* skip spaces after '=' */ + while (pos[0] && (pos[0] == ' ')) + { + pos++; + } + + /* remove CR/LF */ + pos2 = strchr (pos, '\r'); + if (pos2 != NULL) + pos2[0] = '\0'; + pos2 = strchr (pos, '\n'); + if (pos2 != NULL) + pos2[0] = '\0'; + + /* remove simple or double quotes + and spaces at the end */ + if (strlen(pos) > 1) + { + pos2 = pos + strlen (pos) - 1; + while ((pos2 > pos) && (pos2[0] == ' ')) + { + pos2[0] = '\0'; + pos2--; + } + pos2 = pos + strlen (pos) - 1; + if (((pos[0] == '\'') && + (pos2[0] == '\'')) || + ((pos[0] == '"') && + (pos2[0] == '"'))) + { + pos2[0] = '\0'; + pos++; + } + } + + if (section == CONFIG_SECTION_KEYS) + { + if (pos[0]) + { + /* bind key (overwrite any binding with same key) */ + gui_keyboard_bind (line, pos); + } + else + { + /* unbin key if no value given */ + gui_keyboard_unbind (line); + } + } + else if (section == CONFIG_SECTION_ALIAS) + { + /* create new alias */ + if (alias_new (line, pos)) + weelist_add (&index_commands, &last_index_command, + line, WEELIST_POS_SORT); + } + else if (section == CONFIG_SECTION_IGNORE) + { + /* create new ignore */ + if (ascii_strcasecmp (line, "ignore") != 0) + gui_printf (NULL, + _("%s %s, line %d: invalid option \"%s\"\n"), + WEECHAT_WARNING, filename, line_number, line); + else + { + if (!irc_ignore_add_from_config (pos)) + gui_printf (NULL, + _("%s %s, line %d: invalid ignore options \"%s\"\n"), + WEECHAT_WARNING, filename, line_number, pos); + } + } + else + { + option_number = -1; + for (i = 0; + weechat_options[section][i].option_name; i++) + { + if (strcmp + (weechat_options[section][i].option_name, + ptr_line) == 0) + { + option_number = i; + break; + } + } + if (option_number < 0) + gui_printf (NULL, + _("%s %s, line %d: invalid option \"%s\"\n"), + WEECHAT_WARNING, filename, line_number, ptr_line); + else + { + if (config_option_set_value (&weechat_options[section][option_number], pos) < 0) + { + switch (weechat_options[section] + [option_number].option_type) + { + case OPTION_TYPE_BOOLEAN: + gui_printf (NULL, + _("%s %s, line %d: invalid value for " + "option '%s'\n" + "Expected: boolean value: " + "'off' or 'on'\n"), + WEECHAT_WARNING, filename, + line_number, ptr_line); + break; + case OPTION_TYPE_INT: + gui_printf (NULL, + _("%s %s, line %d: invalid value for " + "option '%s'\n" + "Expected: integer between %d " + "and %d\n"), + WEECHAT_WARNING, filename, + line_number, ptr_line, + weechat_options[section][option_number].min, + weechat_options[section][option_number].max); + break; + case OPTION_TYPE_INT_WITH_STRING: + gui_printf (NULL, + _("%s %s, line %d: invalid value for " + "option '%s'\n" + "Expected: one of these strings: "), + WEECHAT_WARNING, filename, + line_number, ptr_line); + i = 0; + while (weechat_options[section][option_number].array_values[i]) + { + gui_printf (NULL, "\"%s\" ", + weechat_options[section][option_number].array_values[i]); + i++; + } + gui_printf (NULL, "\n"); + break; + case OPTION_TYPE_COLOR: + gui_printf (NULL, + _("%s %s, line %d: invalid color " + "name for option '%s'\n"), + WEECHAT_WARNING, filename, + line_number, + ptr_line); + break; + } + } + } + } + } + } + } + } + } + } + + if (server_found) + { + if (!config_allocate_server (filename, line_number)) + { + fclose (file); + free (filename); + return -2; + } + } + + fclose (file); + free (filename); + + return 0; +} + + +/* + * config_create_default: create default WeeChat config + * return: 0 if ok + * < 0 if error + */ + +int +config_create_default () +{ + int filename_length; + char *filename; + FILE *file; + int i, j; + time_t current_time; + struct passwd *my_passwd; + char *realname, *pos; + t_gui_key *ptr_key; + char *expanded_name, *function_name; + + filename_length = strlen (weechat_home) + + strlen (WEECHAT_CONFIG_NAME) + 2; + filename = + (char *) malloc (filename_length * sizeof (char)); + if (!filename) + return -2; + snprintf (filename, filename_length, "%s%s" WEECHAT_CONFIG_NAME, + weechat_home, DIR_SEPARATOR); + if ((file = fopen (filename, "w")) == NULL) + { + gui_printf (NULL, _("%s cannot create file \"%s\"\n"), + WEECHAT_ERROR, filename); + free (filename); + return -1; + } + + weechat_iconv_fprintf (stdout, _("%s: creating default config file...\n"), PACKAGE_NAME); + weechat_log_printf (_("Creating default config file\n")); + + current_time = time (NULL); + weechat_iconv_fprintf (file, _("#\n# %s configuration file, created by " + "%s v%s on %s"), + PACKAGE_NAME, PACKAGE_NAME, PACKAGE_VERSION, + ctime (¤t_time)); + weechat_iconv_fprintf (file, _("# WARNING! Be careful when editing this file, " + "WeeChat writes this file when exiting.\n#\n")); + + for (i = 0; i < CONFIG_NUMBER_SECTIONS; i++) + { + if ((i != CONFIG_SECTION_KEYS) && (i != CONFIG_SECTION_ALIAS) + && (i != CONFIG_SECTION_IGNORE) && (i != CONFIG_SECTION_SERVER)) + { + weechat_iconv_fprintf (file, "\n[%s]\n", config_sections[i].section_name); + for (j = 0; weechat_options[i][j].option_name; j++) + { + switch (weechat_options[i][j].option_type) + { + case OPTION_TYPE_BOOLEAN: + weechat_iconv_fprintf (file, "%s = %s\n", + weechat_options[i][j].option_name, + (weechat_options[i][j].default_int) ? + "on" : "off"); + break; + case OPTION_TYPE_INT: + weechat_iconv_fprintf (file, "%s = %d\n", + weechat_options[i][j].option_name, + weechat_options[i][j].default_int); + break; + case OPTION_TYPE_INT_WITH_STRING: + case OPTION_TYPE_COLOR: + weechat_iconv_fprintf (file, "%s = %s\n", + weechat_options[i][j].option_name, + weechat_options[i][j].default_string); + break; + case OPTION_TYPE_STRING: + weechat_iconv_fprintf (file, "%s = \"%s\"\n", + weechat_options[i][j].option_name, + weechat_options[i][j].default_string); + break; + } + } + } + } + + /* default key bindings */ + weechat_iconv_fprintf (file, "\n[keys]\n"); + for (ptr_key = gui_keys; ptr_key; ptr_key = ptr_key->next_key) + { + expanded_name = gui_keyboard_get_expanded_name (ptr_key->key); + if (ptr_key->function) + { + function_name = gui_keyboard_function_search_by_ptr (ptr_key->function); + if (function_name) + weechat_iconv_fprintf (file, "%s = \"%s%s%s\"\n", + (expanded_name) ? expanded_name : ptr_key->key, + function_name, + (ptr_key->args) ? " " : "", + (ptr_key->args) ? ptr_key->args : ""); + } + else + weechat_iconv_fprintf (file, "%s = \"%s\"\n", + (expanded_name) ? expanded_name : ptr_key->key, + ptr_key->command); + if (expanded_name) + free (expanded_name); + } + + /* default aliases */ + weechat_iconv_fprintf (file, "\n[alias]\n"); + weechat_iconv_fprintf (file, "SAY = \"msg *\"\n"); + weechat_iconv_fprintf (file, "BYE = \"quit\"\n"); + weechat_iconv_fprintf (file, "EXIT = \"quit\"\n"); + weechat_iconv_fprintf (file, "SIGNOFF = \"quit\"\n"); + weechat_iconv_fprintf (file, "C = \"clear\"\n"); + weechat_iconv_fprintf (file, "CL = \"clear\"\n"); + weechat_iconv_fprintf (file, "CLOSE = \"buffer close\"\n"); + weechat_iconv_fprintf (file, "CHAT = \"dcc chat\"\n"); + weechat_iconv_fprintf (file, "IG = \"ignore\"\n"); + weechat_iconv_fprintf (file, "J = \"join\"\n"); + weechat_iconv_fprintf (file, "K = \"kick\"\n"); + weechat_iconv_fprintf (file, "KB = \"kickban\"\n"); + weechat_iconv_fprintf (file, "LEAVE = \"part\"\n"); + weechat_iconv_fprintf (file, "M = \"msg\"\n"); + weechat_iconv_fprintf (file, "MUB = \"unban *\"\n"); + weechat_iconv_fprintf (file, "N = \"names\"\n"); + weechat_iconv_fprintf (file, "Q = \"query\"\n"); + weechat_iconv_fprintf (file, "T = \"topic\"\n"); + weechat_iconv_fprintf (file, "UB = \"unban\"\n"); + weechat_iconv_fprintf (file, "UNIG = \"unignore\"\n"); + weechat_iconv_fprintf (file, "W = \"who\"\n"); + weechat_iconv_fprintf (file, "WC = \"window merge\"\n"); + weechat_iconv_fprintf (file, "WI = \"whois\"\n"); + weechat_iconv_fprintf (file, "WW = \"whowas\"\n"); + + /* no ignore by default */ + + /* default server is freenode */ + weechat_iconv_fprintf (file, "\n[server]\n"); + weechat_iconv_fprintf (file, "server_name = \"freenode\"\n"); + weechat_iconv_fprintf (file, "server_autoconnect = on\n"); + weechat_iconv_fprintf (file, "server_autoreconnect = on\n"); + weechat_iconv_fprintf (file, "server_autoreconnect_delay = 30\n"); + weechat_iconv_fprintf (file, "server_address = \"irc.freenode.net\"\n"); + weechat_iconv_fprintf (file, "server_port = 6667\n"); + weechat_iconv_fprintf (file, "server_ipv6 = off\n"); + weechat_iconv_fprintf (file, "server_ssl = off\n"); + weechat_iconv_fprintf (file, "server_password = \"\"\n"); + + /* Get the user's name from /etc/passwd */ + if ((my_passwd = getpwuid (geteuid ())) != NULL) + { + weechat_iconv_fprintf (file, "server_nick1 = \"%s\"\n", my_passwd->pw_name); + weechat_iconv_fprintf (file, "server_nick2 = \"%s1\"\n", my_passwd->pw_name); + weechat_iconv_fprintf (file, "server_nick3 = \"%s2\"\n", my_passwd->pw_name); + weechat_iconv_fprintf (file, "server_username = \"%s\"\n", my_passwd->pw_name); + if ((!my_passwd->pw_gecos) + || (my_passwd->pw_gecos[0] == '\0') + || (my_passwd->pw_gecos[0] == ',') + || (my_passwd->pw_gecos[0] == ' ')) + weechat_iconv_fprintf (file, "server_realname = \"%s\"\n", my_passwd->pw_name); + else + { + realname = strdup (my_passwd->pw_gecos); + pos = strchr (realname, ','); + if (pos) + pos[0] = '\0'; + weechat_iconv_fprintf (file, "server_realname = \"%s\"\n", + realname); + if (pos) + pos[0] = ','; + free (realname); + } + } + else + { + /* default values if /etc/passwd can't be read */ + weechat_iconv_fprintf (stderr, "%s: %s (%s).", + WEECHAT_WARNING, + _("Unable to get user's name"), + strerror (errno)); + weechat_iconv_fprintf (file, "server_nick1 = \"weechat1\"\n"); + weechat_iconv_fprintf (file, "server_nick2 = \"weechat2\"\n"); + weechat_iconv_fprintf (file, "server_nick3 = \"weechat3\"\n"); + weechat_iconv_fprintf (file, "server_username = \"weechat\"\n"); + weechat_iconv_fprintf (file, "server_realname = \"WeeChat default realname\"\n"); + } + + weechat_iconv_fprintf (file, "server_hostname = \"\"\n"); + weechat_iconv_fprintf (file, "server_command = \"\"\n"); + weechat_iconv_fprintf (file, "server_command_delay = 0\n"); + weechat_iconv_fprintf (file, "server_autojoin = \"\"\n"); + weechat_iconv_fprintf (file, "server_autorejoin = on\n"); + weechat_iconv_fprintf (file, "server_notify_levels = \"\"\n"); + + fclose (file); + chmod (filename, 0600); + free (filename); + return 0; +} + +/* + * config_write: write WeeChat configurtion + * return: 0 if ok + * < 0 if error + */ + +int +config_write (char *config_name) +{ + int filename_length; + char *filename, *filename2; + FILE *file; + int i, j, rc; + time_t current_time; + t_irc_server *ptr_server; + t_weechat_alias *ptr_alias; + t_irc_ignore *ptr_ignore; + t_gui_key *ptr_key; + char *expanded_name, *function_name; + + if (config_name) + { + filename_length = strlen (config_name); + filename = strdup (config_name); + } + else + { + filename_length = strlen (weechat_home) + + strlen (WEECHAT_CONFIG_NAME) + 2; + filename = + (char *) malloc (filename_length * sizeof (char)); + if (!filename) + return -2; + snprintf (filename, filename_length, "%s%s" WEECHAT_CONFIG_NAME, + weechat_home, DIR_SEPARATOR); + } + + filename2 = (char *) malloc ((filename_length + 32) * sizeof (char)); + if (!filename2) + { + free (filename); + return -2; + } + snprintf (filename2, filename_length + 32, "%s.weechattmp", filename); + + if ((file = fopen (filename2, "w")) == NULL) + { + gui_printf (NULL, _("%s cannot create file \"%s\"\n"), + WEECHAT_ERROR, filename2); + free (filename); + free (filename2); + return -1; + } + + weechat_log_printf (_("Saving config to disk\n")); + + current_time = time (NULL); + weechat_iconv_fprintf (file, _("#\n# %s configuration file, created by " + "%s v%s on %s"), + PACKAGE_NAME, PACKAGE_NAME, PACKAGE_VERSION, + ctime (¤t_time)); + weechat_iconv_fprintf (file, _("# WARNING! Be careful when editing this file, " + "WeeChat writes this file when exiting.\n#\n")); + + for (i = 0; i < CONFIG_NUMBER_SECTIONS; i++) + { + if ((i != CONFIG_SECTION_KEYS) && (i != CONFIG_SECTION_ALIAS) + && (i != CONFIG_SECTION_IGNORE) && (i != CONFIG_SECTION_SERVER)) + { + weechat_iconv_fprintf (file, "\n[%s]\n", config_sections[i].section_name); + for (j = 0; weechat_options[i][j].option_name; j++) + { + switch (weechat_options[i][j].option_type) + { + case OPTION_TYPE_BOOLEAN: + weechat_iconv_fprintf (file, "%s = %s\n", + weechat_options[i][j].option_name, + (weechat_options[i][j].ptr_int && + *weechat_options[i][j].ptr_int) ? + "on" : "off"); + break; + case OPTION_TYPE_INT: + weechat_iconv_fprintf (file, "%s = %d\n", + weechat_options[i][j].option_name, + (weechat_options[i][j].ptr_int) ? + *weechat_options[i][j].ptr_int : + weechat_options[i][j].default_int); + break; + case OPTION_TYPE_INT_WITH_STRING: + weechat_iconv_fprintf (file, "%s = %s\n", + weechat_options[i][j].option_name, + (weechat_options[i][j].ptr_int) ? + weechat_options[i][j].array_values[*weechat_options[i][j].ptr_int] : + weechat_options[i][j].array_values[weechat_options[i][j].default_int]); + break; + case OPTION_TYPE_COLOR: + weechat_iconv_fprintf (file, "%s = %s\n", + weechat_options[i][j].option_name, + (weechat_options[i][j].ptr_int) ? + gui_color_get_name (*weechat_options[i][j].ptr_int) : + weechat_options[i][j].default_string); + break; + case OPTION_TYPE_STRING: + weechat_iconv_fprintf (file, "%s = \"%s\"\n", + weechat_options[i][j].option_name, + (weechat_options[i][j].ptr_string) ? + *weechat_options[i][j].ptr_string : + weechat_options[i][j].default_string); + break; + } + } + } + } + + /* keys section */ + weechat_iconv_fprintf (file, "\n[keys]\n"); + for (ptr_key = gui_keys; ptr_key; ptr_key = ptr_key->next_key) + { + expanded_name = gui_keyboard_get_expanded_name (ptr_key->key); + if (ptr_key->function) + { + function_name = gui_keyboard_function_search_by_ptr (ptr_key->function); + if (function_name) + weechat_iconv_fprintf (file, "%s = \"%s%s%s\"\n", + (expanded_name) ? expanded_name : ptr_key->key, + function_name, + (ptr_key->args) ? " " : "", + (ptr_key->args) ? ptr_key->args : ""); + } + else + weechat_iconv_fprintf (file, "%s = \"%s\"\n", + (expanded_name) ? expanded_name : ptr_key->key, + ptr_key->command); + if (expanded_name) + free (expanded_name); + } + + /* alias section */ + weechat_iconv_fprintf (file, "\n[alias]\n"); + for (ptr_alias = weechat_alias; ptr_alias; + ptr_alias = ptr_alias->next_alias) + { + weechat_iconv_fprintf (file, "%s = \"%s\"\n", + ptr_alias->alias_name, ptr_alias->alias_command); + } + + /* ignore section */ + weechat_iconv_fprintf (file, "\n[ignore]\n"); + for (ptr_ignore = irc_ignore; ptr_ignore; + ptr_ignore = ptr_ignore->next_ignore) + { + weechat_iconv_fprintf (file, "ignore = \"%s,%s,%s,%s\"\n", + ptr_ignore->mask, + ptr_ignore->type, + ptr_ignore->channel_name, + ptr_ignore->server_name); + } + + /* server section */ + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (!ptr_server->temp_server) + { + weechat_iconv_fprintf (file, "\n[server]\n"); + weechat_iconv_fprintf (file, "server_name = \"%s\"\n", ptr_server->name); + weechat_iconv_fprintf (file, "server_autoconnect = %s\n", + (ptr_server->autoconnect) ? "on" : "off"); + weechat_iconv_fprintf (file, "server_autoreconnect = %s\n", + (ptr_server->autoreconnect) ? "on" : "off"); + weechat_iconv_fprintf (file, "server_autoreconnect_delay = %d\n", + ptr_server->autoreconnect_delay); + weechat_iconv_fprintf (file, "server_address = \"%s\"\n", ptr_server->address); + weechat_iconv_fprintf (file, "server_port = %d\n", ptr_server->port); + weechat_iconv_fprintf (file, "server_ipv6 = %s\n", + (ptr_server->ipv6) ? "on" : "off"); + weechat_iconv_fprintf (file, "server_ssl = %s\n", + (ptr_server->ssl) ? "on" : "off"); + weechat_iconv_fprintf (file, "server_password = \"%s\"\n", + (ptr_server->password) ? ptr_server->password : ""); + weechat_iconv_fprintf (file, "server_nick1 = \"%s\"\n", ptr_server->nick1); + weechat_iconv_fprintf (file, "server_nick2 = \"%s\"\n", ptr_server->nick2); + weechat_iconv_fprintf (file, "server_nick3 = \"%s\"\n", ptr_server->nick3); + weechat_iconv_fprintf (file, "server_username = \"%s\"\n", ptr_server->username); + weechat_iconv_fprintf (file, "server_realname = \"%s\"\n", ptr_server->realname); + weechat_iconv_fprintf (file, "server_hostname = \"%s\"\n", + (ptr_server->hostname) ? ptr_server->hostname : ""); + weechat_iconv_fprintf (file, "server_command = \"%s\"\n", + (ptr_server->command) ? ptr_server->command : ""); + weechat_iconv_fprintf (file, "server_command_delay = %d\n", ptr_server->command_delay); + weechat_iconv_fprintf (file, "server_autojoin = \"%s\"\n", + (ptr_server->autojoin) ? ptr_server->autojoin : ""); + weechat_iconv_fprintf (file, "server_autorejoin = %s\n", + (ptr_server->autorejoin) ? "on" : "off"); + weechat_iconv_fprintf (file, "server_notify_levels = \"%s\"\n", + (ptr_server->notify_levels) ? ptr_server->notify_levels : ""); + } + } + + fclose (file); + chmod (filename2, 0600); + unlink (filename); + rc = rename (filename2, filename); + free (filename); + free (filename2); + if (rc != 0) + return -1; + return 0; +} diff --git a/src/core/wee-config.h b/src/core/wee-config.h new file mode 100644 index 000000000..cac086840 --- /dev/null +++ b/src/core/wee-config.h @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_CONFIG_H +#define __WEECHAT_CONFIG_H 1 + +#include "../protocols/irc/irc.h" + +#define WEECHAT_CONFIG_NAME "weechat.rc" + +#define CONFIG_SECTION_NONE -1 +#define CONFIG_SECTION_LOOK 0 +#define CONFIG_SECTION_COLORS 1 +#define CONFIG_SECTION_HISTORY 2 +#define CONFIG_SECTION_LOG 3 +#define CONFIG_SECTION_IRC 4 +#define CONFIG_SECTION_DCC 5 +#define CONFIG_SECTION_PROXY 6 +#define CONFIG_SECTION_PLUGINS 7 +#define CONFIG_SECTION_KEYS 8 +#define CONFIG_SECTION_ALIAS 9 +#define CONFIG_SECTION_IGNORE 10 +#define CONFIG_SECTION_SERVER 11 +#define CONFIG_NUMBER_SECTIONS 12 + +#define OPTION_TYPE_BOOLEAN 1 /* values: on/off */ +#define OPTION_TYPE_INT 2 /* values: from min to max */ +#define OPTION_TYPE_INT_WITH_STRING 3 /* values: one from **array_values */ +#define OPTION_TYPE_COLOR 4 /* values: a color name */ +#define OPTION_TYPE_STRING 5 /* values: any string, may be empty */ + +#define BOOL_FALSE 0 +#define BOOL_TRUE 1 + +#define CFG_LOOK_NICKLIST_LEFT 0 +#define CFG_LOOK_NICKLIST_RIGHT 1 +#define CFG_LOOK_NICKLIST_TOP 2 +#define CFG_LOOK_NICKLIST_BOTTOM 3 + +#define CFG_LOOK_ALIGN_NICK_NONE 0 +#define CFG_LOOK_ALIGN_NICK_LEFT 1 +#define CFG_LOOK_ALIGN_NICK_RIGHT 2 + +#define CFG_LOOK_HOTLIST_SORT_GROUP_TIME_ASC 0 +#define CFG_LOOK_HOTLIST_SORT_GROUP_TIME_DESC 1 +#define CFG_LOOK_HOTLIST_SORT_GROUP_NUMBER_ASC 2 +#define CFG_LOOK_HOTLIST_SORT_GROUP_NUMBER_DESC 3 +#define CFG_LOOK_HOTLIST_SORT_NUMBER_ASC 4 +#define CFG_LOOK_HOTLIST_SORT_NUMBER_DESC 5 + +#define CFG_IRC_DISPLAY_AWAY_OFF 0 +#define CFG_IRC_DISPLAY_AWAY_LOCAL 1 +#define CFG_IRC_DISPLAY_AWAY_CHANNEL 2 + +typedef struct t_config_section t_config_section; + +struct t_config_section +{ + int section_number; + char *section_name; +}; + +typedef struct t_config_option t_config_option; + +struct t_config_option +{ + char *option_name; + char *short_description; + char *long_description; + int option_type; + int min, max; + int default_int; + char *default_string; + char **array_values; + int *ptr_int; + char **ptr_string; + void (*handler_change)(); +}; + +extern int cfg_look_save_on_exit; +extern int cfg_look_set_title; +extern int cfg_look_startup_logo; +extern int cfg_look_startup_version; +extern char *cfg_look_weechat_slogan; +extern int cfg_look_one_server_buffer; +extern int cfg_look_open_near_server; +extern int cfg_look_scroll_amount; +extern char *cfg_look_buffer_timestamp; +extern int cfg_look_color_nicks_number; +extern int cfg_look_color_actions; +extern int cfg_look_nicklist; +extern int cfg_look_nicklist_position; +extern int cfg_look_nicklist_min_size; +extern int cfg_look_nicklist_max_size; +extern int cfg_look_nicklist_separator; +extern int cfg_look_nickmode; +extern int cfg_look_nickmode_empty; +extern char *cfg_look_no_nickname; +extern char *cfg_look_nick_prefix; +extern char *cfg_look_nick_suffix; +extern int cfg_look_align_nick; +extern int cfg_look_align_other; +extern int cfg_look_align_size; +extern int cfg_look_align_size_max; +extern int cfg_look_align_text_offset; +extern char *cfg_look_nick_completor; +extern char *cfg_look_nick_completion_ignore; +extern int cfg_look_nick_completion_smart; +extern int cfg_look_nick_complete_first; +extern int cfg_look_infobar; +extern char *cfg_look_infobar_timestamp; +extern int cfg_look_infobar_seconds; +extern int cfg_look_infobar_delay_highlight; +extern int cfg_look_hotlist_names_count; +extern int cfg_look_hotlist_names_level; +extern int cfg_look_hotlist_names_length; +extern int cfg_look_hotlist_sort; +extern int cfg_look_day_change; +extern char *cfg_look_day_change_timestamp; +extern char *cfg_look_read_marker; +extern char *cfg_look_input_format; +extern int cfg_look_paste_max_lines; + +extern int cfg_col_real_white; +extern int cfg_col_separator; +extern int cfg_col_title; +extern int cfg_col_title_more; +extern int cfg_col_title_bg; +extern int cfg_col_chat; +extern int cfg_col_chat_time; +extern int cfg_col_chat_time_sep; +extern int cfg_col_chat_prefix1; +extern int cfg_col_chat_prefix2; +extern int cfg_col_chat_server; +extern int cfg_col_chat_join; +extern int cfg_col_chat_part; +extern int cfg_col_chat_nick; +extern int cfg_col_chat_host; +extern int cfg_col_chat_channel; +extern int cfg_col_chat_dark; +extern int cfg_col_chat_highlight; +extern int cfg_col_chat_bg; +extern int cfg_col_chat_read_marker; +extern int cfg_col_chat_read_marker_bg; +extern int cfg_col_status; +extern int cfg_col_status_delimiters; +extern int cfg_col_status_channel; +extern int cfg_col_status_data_msg; +extern int cfg_col_status_data_private; +extern int cfg_col_status_data_highlight; +extern int cfg_col_status_data_other; +extern int cfg_col_status_more; +extern int cfg_col_status_bg; +extern int cfg_col_infobar; +extern int cfg_col_infobar_delimiters; +extern int cfg_col_infobar_highlight; +extern int cfg_col_infobar_bg; +extern int cfg_col_input; +extern int cfg_col_input_server; +extern int cfg_col_input_channel; +extern int cfg_col_input_nick; +extern int cfg_col_input_delimiters; +extern int cfg_col_input_text_not_found; +extern int cfg_col_input_actions; +extern int cfg_col_input_bg; +extern int cfg_col_nick; +extern int cfg_col_nick_away; +extern int cfg_col_nick_chanowner; +extern int cfg_col_nick_chanadmin; +extern int cfg_col_nick_op; +extern int cfg_col_nick_halfop; +extern int cfg_col_nick_voice; +extern int cfg_col_nick_user; +extern int cfg_col_nick_more; +extern int cfg_col_nick_sep; +extern int cfg_col_nick_self; +extern int cfg_col_nick_colors[GUI_COLOR_WIN_NICK_NUMBER]; +extern int cfg_col_nick_private; +extern int cfg_col_nick_bg; +extern int cfg_col_dcc_selected; +extern int cfg_col_dcc_waiting; +extern int cfg_col_dcc_connecting; +extern int cfg_col_dcc_active; +extern int cfg_col_dcc_done; +extern int cfg_col_dcc_failed; +extern int cfg_col_dcc_aborted; + +extern int cfg_history_max_lines; +extern int cfg_history_max_commands; +extern int cfg_history_display_default; + +extern int cfg_log_auto_server; +extern int cfg_log_auto_channel; +extern int cfg_log_auto_private; +extern int cfg_log_plugin_msg; +extern char *cfg_log_path; +extern char *cfg_log_timestamp; +extern int cfg_log_hide_nickserv_pwd; + +extern int cfg_irc_display_away; +extern int cfg_irc_show_away_once; +extern char *cfg_irc_default_msg_part; +extern char *cfg_irc_default_msg_quit; +extern int cfg_irc_notice_as_pv; +extern int cfg_irc_away_check; +extern int cfg_irc_away_check_max_nicks; +extern int cfg_irc_lag_check; +extern int cfg_irc_lag_min_show; +extern int cfg_irc_lag_disconnect; +extern int cfg_irc_anti_flood; +extern int cfg_irc_fifo_pipe; +extern char *cfg_irc_highlight; +extern int cfg_irc_colors_receive; +extern int cfg_irc_colors_send; +extern int cfg_irc_send_unknown_commands; + +extern int cfg_dcc_auto_accept_files; +extern int cfg_dcc_auto_accept_chats; +extern int cfg_dcc_timeout; +extern int cfg_dcc_blocksize; +extern int cfg_dcc_fast_send; +extern char *cfg_dcc_port_range; +extern char *cfg_dcc_own_ip; +extern char *cfg_dcc_download_path; +extern char *cfg_dcc_upload_path; +extern int cfg_dcc_convert_spaces; +extern int cfg_dcc_auto_rename; +extern int cfg_dcc_auto_resume; + +extern int cfg_proxy_use; +extern int cfg_proxy_type; +extern char *cfg_proxy_type_values[]; +extern int cfg_proxy_ipv6; +extern char *cfg_proxy_address; +extern int cfg_proxy_port; +extern char *cfg_proxy_username; +extern char *cfg_proxy_password; + +extern char *cfg_plugins_path; +extern char *cfg_plugins_autoload; +extern char *cfg_plugins_extension; + +extern t_config_section config_sections [CONFIG_NUMBER_SECTIONS]; +extern t_config_option * weechat_options [CONFIG_NUMBER_SECTIONS]; + +extern char *config_get_section (); +extern void config_change_noop (); +extern void config_change_save_on_exit (); +extern void config_change_title (); +extern void config_change_buffers (); +extern void config_change_buffer_content (); +extern void config_change_hotlist (); +extern void config_change_read_marker (); +extern void config_change_charset (); +extern void config_change_one_server_buffer (); +extern void config_change_color (); +extern void config_change_nicks_colors (); +extern void config_change_away_check (); +extern void config_change_fifo_pipe (); +extern void config_change_notify_levels (); +extern void config_change_log (); +extern int config_option_set_value (t_config_option *, char *); +extern void config_option_list_remove (char **, char *); +extern void config_option_list_set (char **, char *, char *); +extern void config_option_list_get_value (char **, char *, char **, int *); +extern t_config_option *config_option_search (char *); +extern void config_option_search_option_value (char *, t_config_option **, void **); +extern int config_set_value (char *, char *); +extern void *config_get_server_option_ptr (t_irc_server *, char *); +extern int config_set_server_value (t_irc_server *, char *, char *); +extern int config_read (); +extern int config_create_default (); +extern int config_write (); + +#endif /* weeconfig.h */ diff --git a/src/core/wee-list.c b/src/core/wee-list.c new file mode 100644 index 000000000..3e0872eb6 --- /dev/null +++ b/src/core/wee-list.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* weelist.c: sorted lists management */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "weechat.h" +#include "weelist.h" +#include "log.h" +#include "util.h" + + +/* + * weelist_get_size: get list size (number of elements) + */ + +int +weelist_get_size (t_weelist *weelist) +{ + t_weelist *ptr_weelist; + int count; + + count = 0; + + for (ptr_weelist = weelist; ptr_weelist; ptr_weelist = ptr_weelist->next_weelist) + { + count++; + } + + return count; +} + +/* + * weelist_search: search date in a list + */ + +t_weelist * +weelist_search (t_weelist *weelist, char *data) +{ + t_weelist *ptr_weelist; + + for (ptr_weelist = weelist; ptr_weelist; ptr_weelist = ptr_weelist->next_weelist) + { + if (ascii_strcasecmp (data, ptr_weelist->data) == 0) + return ptr_weelist; + } + /* word not found in list */ + return NULL; +} + +/* + * weelist_find_pos: find position for data (keeping list sorted) + */ + +t_weelist * +weelist_find_pos (t_weelist *weelist, char *data) +{ + t_weelist *ptr_weelist; + + for (ptr_weelist = weelist; ptr_weelist; ptr_weelist = ptr_weelist->next_weelist) + { + if (ascii_strcasecmp (data, ptr_weelist->data) < 0) + return ptr_weelist; + } + /* position not found, best position is at the end */ + return NULL; +} + +/* + * weelist_insert: insert an element to the list (at good position) + */ + +void +weelist_insert (t_weelist **weelist, t_weelist **last_weelist, t_weelist *element, + int position) +{ + t_weelist *pos_weelist; + + if (*weelist) + { + /* remove element if already in list */ + pos_weelist = weelist_search (*weelist, element->data); + if (pos_weelist) + weelist_remove (weelist, last_weelist, pos_weelist); + } + + if (*weelist) + { + /* search position for new element, according to pos asked */ + pos_weelist = NULL; + switch (position) + { + case WEELIST_POS_SORT: + pos_weelist = weelist_find_pos (*weelist, element->data); + break; + case WEELIST_POS_BEGINNING: + pos_weelist = *weelist; + break; + case WEELIST_POS_END: + pos_weelist = NULL; + break; + } + + if (pos_weelist) + { + /* insert data into the list (before position found) */ + element->prev_weelist = pos_weelist->prev_weelist; + element->next_weelist = pos_weelist; + if (pos_weelist->prev_weelist) + pos_weelist->prev_weelist->next_weelist = element; + else + *weelist = element; + pos_weelist->prev_weelist = element; + } + else + { + /* add data to the end */ + element->prev_weelist = *last_weelist; + element->next_weelist = NULL; + (*last_weelist)->next_weelist = element; + *last_weelist = element; + } + } + else + { + element->prev_weelist = NULL; + element->next_weelist = NULL; + *weelist = element; + *last_weelist = element; + } +} + +/* + * weelist_add: create new data and add it to list + */ + +t_weelist * +weelist_add (t_weelist **weelist, t_weelist **last_weelist, char *data, + int position) +{ + t_weelist *new_weelist; + + if (!data || (!data[0])) + return NULL; + + if ((new_weelist = ((t_weelist *) malloc (sizeof (t_weelist))))) + { + new_weelist->data = strdup (data); + weelist_insert (weelist, last_weelist, new_weelist, position); + return new_weelist; + } + /* failed to allocate new element */ + return NULL; +} + +/* + * weelist_remove: remove an element from a list + */ + +void +weelist_remove (t_weelist **weelist, t_weelist **last_weelist, t_weelist *element) +{ + t_weelist *new_weelist; + + if (!element || !(*weelist)) + return; + + /* remove element from list */ + if (*last_weelist == element) + *last_weelist = element->prev_weelist; + if (element->prev_weelist) + { + (element->prev_weelist)->next_weelist = element->next_weelist; + new_weelist = *weelist; + } + else + new_weelist = element->next_weelist; + + if (element->next_weelist) + (element->next_weelist)->prev_weelist = element->prev_weelist; + + /* free data */ + if (element->data) + free (element->data); + free (element); + *weelist = new_weelist; +} + +/* + * weelist_remove_all: remove all elements from a list + */ + +void +weelist_remove_all (t_weelist **weelist, t_weelist **last_weelist) +{ + while (*weelist) + { + weelist_remove (weelist, last_weelist, *weelist); + } +} + +/* + * weelist_print_log: print weelist in log (usually for crash dump) + */ + +void +weelist_print_log (t_weelist *weelist, char *name) +{ + t_weelist *ptr_weelist; + + for (ptr_weelist = weelist; ptr_weelist; + ptr_weelist = ptr_weelist->next_weelist) + { + weechat_log_printf ("[%s (addr:0x%X)]\n", name, ptr_weelist); + weechat_log_printf (" data . . . . . . . . . : '%s'\n", ptr_weelist->data); + weechat_log_printf (" prev_weelist . . . . . : 0x%X\n", ptr_weelist->prev_weelist); + weechat_log_printf (" next_weelist . . . . . : 0x%X\n", ptr_weelist->next_weelist); + } +} diff --git a/src/core/wee-list.h b/src/core/wee-list.h new file mode 100644 index 000000000..4cc9ffb83 --- /dev/null +++ b/src/core/wee-list.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_LIST_H +#define __WEECHAT_LIST_H 1 + +#define WEELIST_POS_SORT 0 +#define WEELIST_POS_BEGINNING 1 +#define WEELIST_POS_END 2 + +typedef struct t_weelist t_weelist; + +struct t_weelist +{ + char *data; + t_weelist *prev_weelist; + t_weelist *next_weelist; +}; + +extern int weelist_get_size (t_weelist *); +extern t_weelist *weelist_search (t_weelist *, char *); +extern t_weelist *weelist_add (t_weelist **, t_weelist **, char *, int); +extern void weelist_remove (t_weelist **, t_weelist **, t_weelist *); +extern void weelist_remove_all (t_weelist **, t_weelist **); +extern void weelist_print_log (t_weelist *, char *); + +#endif /* weelist.h */ diff --git a/src/core/wee-log.c b/src/core/wee-log.c new file mode 100644 index 000000000..2132e67a8 --- /dev/null +++ b/src/core/wee-log.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* log.c: WeeChat log file */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdarg.h> +#include <errno.h> + +#ifdef HAVE_FLOCK +#include <sys/file.h> +#endif + +#include <sys/types.h> +#include <time.h> + +#include "weechat.h" +#include "log.h" +#include "util.h" + + +char *weechat_log_filename = NULL; /* log name (~/.weechat/weechat.log) */ +FILE *weechat_log_file = NULL; /* WeeChat log file */ + + +/* + * weechat_log_open: initialize log file + */ + +int +weechat_log_open (char *filename, char *mode) +{ + int filename_length; + + /* exit if log already opened */ + if (weechat_log_file) + return 0; + + if (filename) + weechat_log_filename = strdup (filename); + else + { + filename_length = strlen (weechat_home) + 64; + weechat_log_filename = + (char *) malloc (filename_length * sizeof (char)); + snprintf (weechat_log_filename, filename_length, + "%s/%s", weechat_home, WEECHAT_LOG_NAME); + } + + weechat_log_file = fopen (weechat_log_filename, mode); + if (!weechat_log_file) + { + free (weechat_log_filename); + weechat_log_filename = NULL; + return 0; + } + +#ifdef HAVE_FLOCK + if ((flock (fileno (weechat_log_file), LOCK_EX | LOCK_NB) != 0)) + { + if (errno == EWOULDBLOCK) + { + fclose (weechat_log_file); + weechat_log_file = NULL; + free (weechat_log_filename); + weechat_log_filename = NULL; + return 0; + } + } +#endif + + return 1; +} + +/* + * weechat_log_init: initialize log file + */ + +void +weechat_log_init () +{ + if (!weechat_log_open (NULL, "w")) + { + weechat_iconv_fprintf (stderr, + _("%s unable to create/append to log file (weechat.log)\n" + "If another WeeChat process is using this file, try to run WeeChat\n" + "with another home using \"--dir\" command line option.\n"), + WEECHAT_ERROR); + exit (1); + } +} + +/* + * weechat_log_printf: write a message in WeeChat log (<weechat_home>/weechat.log) + */ + +void +weechat_log_printf (char *message, ...) +{ + static char buffer[4096]; + char *ptr_buffer; + va_list argptr; + static time_t seconds; + struct tm *date_tmp; + + if (!weechat_log_file) + return; + + va_start (argptr, message); + vsnprintf (buffer, sizeof (buffer) - 1, message, argptr); + va_end (argptr); + + /* keep only valid chars */ + ptr_buffer = buffer; + while (ptr_buffer[0]) + { + if ((ptr_buffer[0] != '\n') + && (ptr_buffer[0] != '\r') + && ((unsigned char)(ptr_buffer[0]) < 32)) + ptr_buffer[0] = '.'; + ptr_buffer++; + } + + seconds = time (NULL); + date_tmp = localtime (&seconds); + if (date_tmp) + weechat_iconv_fprintf (weechat_log_file, "[%04d-%02d-%02d %02d:%02d:%02d] %s", + date_tmp->tm_year + 1900, date_tmp->tm_mon + 1, date_tmp->tm_mday, + date_tmp->tm_hour, date_tmp->tm_min, date_tmp->tm_sec, + buffer); + else + weechat_iconv_fprintf (weechat_log_file, "%s", buffer); + + fflush (weechat_log_file); +} + +/* + * weechat_log_close: close log file + */ + +void +weechat_log_close () +{ + /* close log file */ + if (weechat_log_file) + { +#ifdef HAVE_FLOCK + flock (fileno (weechat_log_file), LOCK_UN); +#endif + fclose (weechat_log_file); + weechat_log_file = NULL; + } + + /* free filename */ + if (weechat_log_filename) + { + free (weechat_log_filename); + weechat_log_filename = NULL; + } +} + +/* + * weechat_log_crash_rename: rename log file when crashing + */ + +int +weechat_log_crash_rename () +{ + char *old_name, *new_name; + int length; + time_t time_now; + struct tm *local_time; + + if (!weechat_log_filename) + return 0; + + old_name = strdup (weechat_log_filename); + if (!old_name) + return 0; + + weechat_log_close (); + + length = strlen (weechat_home) + 128; + new_name = (char *) malloc (length); + if (new_name) + { + time_now = time (NULL); + local_time = localtime (&time_now); + snprintf (new_name, length, + "%s/weechat_crash_%04d%02d%02d_%d.log", + weechat_home, + local_time->tm_year + 1900, + local_time->tm_mon + 1, + local_time->tm_mday, + getpid()); + if (rename (old_name, new_name) == 0) + { + weechat_iconv_fprintf (stderr, "*** Full crash dump was saved to %s file.\n", + new_name); + weechat_log_open (new_name, "a"); + free (old_name); + free (new_name); + return 1; + } + free (new_name); + } + + free (old_name); + weechat_log_open (NULL, "a"); + return 0; +} diff --git a/src/core/wee-log.h b/src/core/wee-log.h new file mode 100644 index 000000000..4f2a9e5ce --- /dev/null +++ b/src/core/wee-log.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_LOG_H +#define __WEECHAT_LOG_H 1 + +extern char *weechat_log_filename; +extern FILE *weechat_log_file; + +extern void weechat_log_init (); +extern void weechat_log_close (); +extern void weechat_log_printf (char *, ...); +extern int weechat_log_crash_rename (); + +#endif /* log.h */ diff --git a/src/core/wee-upgrade.c b/src/core/wee-upgrade.c new file mode 100644 index 000000000..f74b015e5 --- /dev/null +++ b/src/core/wee-upgrade.c @@ -0,0 +1,1867 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* session.c: save/restore session data */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> + +#ifdef HAVE_GNUTLS +#include <gnutls/gnutls.h> +#endif + +#include "weechat.h" +#include "session.h" +#include "hotlist.h" +#include "log.h" +#include "utf8.h" +#include "util.h" +#include "../protocols/irc/irc.h" +#include "../gui/gui.h" + + +/* current server/channel (used when loading session) */ +t_irc_server *session_current_server = NULL; +t_irc_channel *session_current_channel = NULL; +t_gui_buffer *session_current_buffer = NULL; + +long session_last_read_pos = 0; +int session_last_read_length = 0; + + +/* + * session_write_id: write object ID to file + */ + +int +session_write_id (FILE *file, int id) +{ + return (fwrite ((void *)(&id), sizeof (int), 1, file) > 0); +} + +/* + * session_write_int: write an integer to file + */ + +int +session_write_int (FILE *file, int id, int value) +{ + char type; + + if (id >= 0) + { + if (!session_write_id (file, id)) + return 0; + } + type = SESSION_TYPE_INT; + if (fwrite ((void *)(&type), sizeof (char), 1, file) == 0) + return 0; + return (fwrite ((void *)(&value), sizeof (int), 1, file) > 0); +} + +/* + * session_write_str: write a string to file + */ + +int +session_write_str (FILE *file, int id, char *string) +{ + char type; + int length; + + if (id >= 0) + { + if (!session_write_id (file, id)) + return 0; + } + type = SESSION_TYPE_STR; + if (fwrite ((void *)(&type), sizeof (char), 1, file) == 0) + return 0; + if (string && string[0]) + { + length = strlen (string); + if (fwrite ((void *)(&length), sizeof (int), 1, file) == 0) + return 0; + return (fwrite ((void *)string, length, 1, file) > 0); + } + else + { + length = 0; + return (fwrite ((void *)(&length), sizeof (int), 1, file) > 0); + } +} + +/* + * session_write_buf: write a buffer to file + */ + +int +session_write_buf (FILE *file, int id, void *buffer, int size) +{ + char type; + + if (id >= 0) + { + if (!session_write_id (file, id)) + return 0; + } + type = SESSION_TYPE_BUF; + if (fwrite ((void *)(&type), sizeof (char), 1, file) == 0) + return 0; + if (fwrite ((void *)(&size), sizeof (int), 1, file) == 0) + return 0; + return (fwrite (buffer, size, 1, file) > 0); +} + +/* + * session_save_nick: save a nick into session file + */ + +int +session_save_nick (FILE *file, t_irc_nick *nick) +{ + int rc; + + rc = 1; + rc = rc && (session_write_id (file, SESSION_OBJ_NICK)); + rc = rc && (session_write_str (file, SESSION_NICK_NICK, nick->nick)); + rc = rc && (session_write_int (file, SESSION_NICK_FLAGS, nick->flags)); + rc = rc && (session_write_int (file, SESSION_NICK_COLOR, nick->color)); + rc = rc && (session_write_str (file, SESSION_NICK_HOST, nick->host)); + rc = rc && (session_write_id (file, SESSION_NICK_END)); + return rc; +} + +/* + * session_save_channel: save a channel into session file + */ + +int +session_save_channel (FILE *file, t_irc_channel *channel) +{ + int rc; + t_irc_nick *ptr_nick; + + rc = 1; + rc = rc && (session_write_id (file, SESSION_OBJ_CHANNEL)); + rc = rc && (session_write_int (file, SESSION_CHAN_TYPE, channel->type)); + rc = rc && (session_write_str (file, SESSION_CHAN_NAME, channel->name)); + rc = rc && (session_write_str (file, SESSION_CHAN_TOPIC, channel->topic)); + rc = rc && (session_write_str (file, SESSION_CHAN_MODES, channel->modes)); + rc = rc && (session_write_int (file, SESSION_CHAN_LIMIT, channel->limit)); + rc = rc && (session_write_str (file, SESSION_CHAN_KEY, channel->key)); + rc = rc && (session_write_int (file, SESSION_CHAN_NICKS_COUNT, channel->nicks_count)); + rc = rc && (session_write_int (file, SESSION_CHAN_CHECKING_AWAY, channel->checking_away)); + rc = rc && (session_write_str (file, SESSION_CHAN_AWAY_MESSAGE, channel->away_message)); + rc = rc && (session_write_int (file, SESSION_CHAN_CYCLE, channel->cycle)); + rc = rc && (session_write_int (file, SESSION_CHAN_CLOSE, channel->close)); + rc = rc && (session_write_int (file, SESSION_CHAN_DISPLAY_CREATION_DATE, channel->display_creation_date)); + rc = rc && (session_write_id (file, SESSION_CHAN_END)); + + if (!rc) + return 0; + + for (ptr_nick = channel->nicks; ptr_nick; + ptr_nick = ptr_nick->next_nick) + { + if (!session_save_nick (file, ptr_nick)) + return 0; + } + + return 1; +} + +/* + * session_save_servers: save all servers into session file + */ + +int +session_save_servers (FILE *file) +{ + int rc; +#ifdef HAVE_GNUTLS + void *session_data; + size_t session_size; +#endif + t_irc_server *ptr_server; + t_irc_channel *ptr_channel; + + rc = 1; + + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + rc = rc && (session_write_id (file, SESSION_OBJ_SERVER)); + rc = rc && (session_write_str (file, SESSION_SERV_NAME, ptr_server->name)); + rc = rc && (session_write_int (file, SESSION_SERV_AUTOCONNECT, ptr_server->autoconnect)); + rc = rc && (session_write_int (file, SESSION_SERV_AUTORECONNECT, ptr_server->autoreconnect)); + rc = rc && (session_write_int (file, SESSION_SERV_AUTORECONNECT_DELAY, ptr_server->autoreconnect_delay)); + rc = rc && (session_write_int (file, SESSION_SERV_TEMP_SERVER, ptr_server->temp_server)); + rc = rc && (session_write_str (file, SESSION_SERV_ADDRESS, ptr_server->address)); + rc = rc && (session_write_int (file, SESSION_SERV_PORT, ptr_server->port)); + rc = rc && (session_write_int (file, SESSION_SERV_IPV6, ptr_server->ipv6)); + rc = rc && (session_write_int (file, SESSION_SERV_SSL, ptr_server->ssl)); + rc = rc && (session_write_str (file, SESSION_SERV_PASSWORD, ptr_server->password)); + rc = rc && (session_write_str (file, SESSION_SERV_NICK1, ptr_server->nick1)); + rc = rc && (session_write_str (file, SESSION_SERV_NICK2, ptr_server->nick2)); + rc = rc && (session_write_str (file, SESSION_SERV_NICK3, ptr_server->nick3)); + rc = rc && (session_write_str (file, SESSION_SERV_USERNAME, ptr_server->username)); + rc = rc && (session_write_str (file, SESSION_SERV_REALNAME, ptr_server->realname)); + rc = rc && (session_write_str (file, SESSION_SERV_HOSTNAME, ptr_server->hostname)); + rc = rc && (session_write_str (file, SESSION_SERV_COMMAND, ptr_server->command)); + rc = rc && (session_write_int (file, SESSION_SERV_COMMAND_DELAY, ptr_server->command_delay)); + rc = rc && (session_write_str (file, SESSION_SERV_AUTOJOIN, ptr_server->autojoin)); + rc = rc && (session_write_int (file, SESSION_SERV_AUTOREJOIN, ptr_server->autorejoin)); + rc = rc && (session_write_str (file, SESSION_SERV_NOTIFY_LEVELS, ptr_server->notify_levels)); + rc = rc && (session_write_int (file, SESSION_SERV_CHILD_PID, ptr_server->child_pid)); + rc = rc && (session_write_int (file, SESSION_SERV_CHILD_READ, ptr_server->child_read)); + rc = rc && (session_write_int (file, SESSION_SERV_CHILD_WRITE, ptr_server->child_write)); + rc = rc && (session_write_int (file, SESSION_SERV_SOCK, ptr_server->sock)); + rc = rc && (session_write_int (file, SESSION_SERV_IS_CONNECTED, ptr_server->is_connected)); + rc = rc && (session_write_int (file, SESSION_SERV_SSL_CONNECTED, ptr_server->ssl_connected)); +#ifdef HAVE_GNUTLS + if (ptr_server->is_connected && ptr_server->ssl_connected) + { + gnutls_session_get_data (ptr_server->gnutls_sess, NULL, &session_size); + if (session_size > 0) + { + session_data = malloc (session_size); + gnutls_session_get_data (ptr_server->gnutls_sess, session_data, &session_size); + rc = rc && (session_write_buf (file, SESSION_SERV_GNUTLS_SESS, &(session_data), session_size)); + free (session_data); + } + } +#endif + rc = rc && (session_write_str (file, SESSION_SERV_UNTERMINATED_MESSAGE, ptr_server->unterminated_message)); + rc = rc && (session_write_str (file, SESSION_SERV_NICK, ptr_server->nick)); + rc = rc && (session_write_str (file, SESSION_SERV_NICK_MODES, ptr_server->nick_modes)); + rc = rc && (session_write_str (file, SESSION_SERV_PREFIX, ptr_server->prefix)); + rc = rc && (session_write_buf (file, SESSION_SERV_RECONNECT_START, &(ptr_server->reconnect_start), sizeof (time_t))); + rc = rc && (session_write_buf (file, SESSION_SERV_COMMAND_TIME, &(ptr_server->command_time), sizeof (time_t))); + rc = rc && (session_write_int (file, SESSION_SERV_RECONNECT_JOIN, ptr_server->reconnect_join)); + rc = rc && (session_write_int (file, SESSION_SERV_IS_AWAY, ptr_server->is_away)); + rc = rc && (session_write_str (file, SESSION_SERV_AWAY_MESSAGE, ptr_server->away_message)); + rc = rc && (session_write_buf (file, SESSION_SERV_AWAY_TIME, &(ptr_server->away_time), sizeof (time_t))); + rc = rc && (session_write_int (file, SESSION_SERV_LAG, ptr_server->lag)); + rc = rc && (session_write_buf (file, SESSION_SERV_LAG_CHECK_TIME, &(ptr_server->lag_check_time), sizeof (struct timeval))); + rc = rc && (session_write_buf (file, SESSION_SERV_LAG_NEXT_CHECK, &(ptr_server->lag_next_check), sizeof (time_t))); + rc = rc && (session_write_id (file, SESSION_SERV_END)); + + if (!rc) + return 0; + + for (ptr_channel = ptr_server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (!session_save_channel (file, ptr_channel)) + return 0; + } + } + return 1; +} + +/* + * session_save_dcc: save all DCC into session file + */ + +int +session_save_dcc (FILE *file) +{ + int rc; + t_irc_dcc *ptr_dcc; + + rc = 1; + + for (ptr_dcc = irc_last_dcc; ptr_dcc; + ptr_dcc = ptr_dcc->prev_dcc) + { + rc = rc && (session_write_id (file, SESSION_OBJ_DCC)); + rc = rc && (session_write_str (file, SESSION_DCC_SERVER, (ptr_dcc->server) ? ptr_dcc->server->name : NULL)); + rc = rc && (session_write_str (file, SESSION_DCC_CHANNEL, (ptr_dcc->channel) ? ptr_dcc->channel->name : NULL)); + rc = rc && (session_write_int (file, SESSION_DCC_TYPE, ptr_dcc->type)); + rc = rc && (session_write_int (file, SESSION_DCC_STATUS, ptr_dcc->status)); + rc = rc && (session_write_buf (file, SESSION_DCC_START_TIME, &(ptr_dcc->start_time), sizeof (time_t))); + rc = rc && (session_write_buf (file, SESSION_DCC_START_TRANSFER, &(ptr_dcc->start_transfer), sizeof (time_t))); + rc = rc && (session_write_buf (file, SESSION_DCC_ADDR, &(ptr_dcc->addr), sizeof (unsigned long))); + rc = rc && (session_write_int (file, SESSION_DCC_PORT, ptr_dcc->port)); + rc = rc && (session_write_str (file, SESSION_DCC_NICK, ptr_dcc->nick)); + rc = rc && (session_write_int (file, SESSION_DCC_SOCK, ptr_dcc->sock)); + rc = rc && (session_write_str (file, SESSION_DCC_UNTERMINATED_MESSAGE, ptr_dcc->unterminated_message)); + rc = rc && (session_write_int (file, SESSION_DCC_FILE, ptr_dcc->file)); + rc = rc && (session_write_str (file, SESSION_DCC_FILENAME, ptr_dcc->filename)); + rc = rc && (session_write_str (file, SESSION_DCC_LOCAL_FILENAME, ptr_dcc->local_filename)); + rc = rc && (session_write_int (file, SESSION_DCC_FILENAME_SUFFIX, ptr_dcc->filename_suffix)); + rc = rc && (session_write_buf (file, SESSION_DCC_SIZE, &(ptr_dcc->size), sizeof (unsigned long))); + rc = rc && (session_write_buf (file, SESSION_DCC_POS, &(ptr_dcc->pos), sizeof (unsigned long))); + rc = rc && (session_write_buf (file, SESSION_DCC_ACK, &(ptr_dcc->ack), sizeof (unsigned long))); + rc = rc && (session_write_buf (file, SESSION_DCC_START_RESUME, &(ptr_dcc->start_resume), sizeof (unsigned long))); + rc = rc && (session_write_buf (file, SESSION_DCC_LAST_CHECK_TIME, &(ptr_dcc->last_check_time), sizeof (time_t))); + rc = rc && (session_write_buf (file, SESSION_DCC_LAST_CHECK_POS, &(ptr_dcc->last_check_pos), sizeof (unsigned long))); + rc = rc && (session_write_buf (file, SESSION_DCC_LAST_ACTIVITY, &(ptr_dcc->last_activity), sizeof (time_t))); + rc = rc && (session_write_buf (file, SESSION_DCC_BYTES_PER_SEC, &(ptr_dcc->bytes_per_sec), sizeof (unsigned long))); + rc = rc && (session_write_buf (file, SESSION_DCC_ETA, &(ptr_dcc->eta), sizeof (unsigned long))); + rc = rc && (session_write_int (file, SESSION_DCC_CHILD_PID, ptr_dcc->child_pid)); + rc = rc && (session_write_int (file, SESSION_DCC_CHILD_READ, ptr_dcc->child_read)); + rc = rc && (session_write_int (file, SESSION_DCC_CHILD_WRITE, ptr_dcc->child_write)); + rc = rc && (session_write_id (file, SESSION_DCC_END)); + + if (!rc) + return 0; + } + return 1; +} + +/* + * session_save_history: save history into session file + * (from last to first, to restore it in good order) + */ + +int +session_save_history (FILE *file, t_history *last_history) +{ + int rc; + t_history *ptr_history; + + rc = 1; + rc = rc && (session_write_id (file, SESSION_OBJ_HISTORY)); + ptr_history = last_history; + while (ptr_history) + { + rc = rc && (session_write_str (file, SESSION_HIST_TEXT, ptr_history->text)); + ptr_history = ptr_history->prev_history; + } + rc = rc && (session_write_id (file, SESSION_HIST_END)); + return rc; +} + +/* + * session_save_line: save a buffer line into session file + */ + +int +session_save_line (FILE *file, t_gui_line *line) +{ + int rc; + + rc = 1; + rc = rc && (session_write_id (file, SESSION_OBJ_LINE)); + rc = rc && (session_write_int (file, SESSION_LINE_LENGTH, line->length)); + rc = rc && (session_write_int (file, SESSION_LINE_LENGTH_ALIGN, line->length_align)); + rc = rc && (session_write_int (file, SESSION_LINE_LOG_WRITE, line->log_write)); + rc = rc && (session_write_int (file, SESSION_LINE_WITH_MESSAGE, line->line_with_message)); + rc = rc && (session_write_int (file, SESSION_LINE_WITH_HIGHLIGHT, line->line_with_highlight)); + rc = rc && (session_write_str (file, SESSION_LINE_DATA, line->data)); + rc = rc && (session_write_int (file, SESSION_LINE_OFS_AFTER_DATE, line->ofs_after_date)); + rc = rc && (session_write_int (file, SESSION_LINE_OFS_START_MESSAGE, line->ofs_start_message)); + rc = rc && (session_write_str (file, SESSION_LINE_NICK, line->nick)); + rc = rc && (session_write_buf (file, SESSION_LINE_DATE, &(line->date), sizeof (time_t))); + rc = rc && (session_write_id (file, SESSION_LINE_END)); + return rc; +} + +/* + * session_save_buffers: save all buffers into session file + */ + +int +session_save_buffers (FILE *file) +{ + int rc; + t_gui_buffer *ptr_buffer; + t_gui_line *ptr_line; + + rc = 1; + + for (ptr_buffer = gui_buffers; ptr_buffer; + ptr_buffer = ptr_buffer->next_buffer) + { + rc = rc && (session_write_id (file, SESSION_OBJ_BUFFER)); + rc = rc && (session_write_str (file, SESSION_BUFF_SERVER, GUI_SERVER(ptr_buffer) ? GUI_SERVER(ptr_buffer)->name : NULL)); + rc = rc && (session_write_str (file, SESSION_BUFF_CHANNEL, GUI_CHANNEL(ptr_buffer) ? GUI_CHANNEL(ptr_buffer)->name : NULL)); + rc = rc && (session_write_int (file, SESSION_BUFF_TYPE, ptr_buffer->type)); + rc = rc && (session_write_int (file, SESSION_BUFF_ALL_SERVERS, ptr_buffer->all_servers)); + rc = rc && (session_write_id (file, SESSION_BUFF_END)); + + if (!rc) + return 0; + + for (ptr_line = ptr_buffer->lines; ptr_line; + ptr_line = ptr_line->next_line) + { + if (!session_save_line (file, ptr_line)) + return 0; + } + + if (!session_save_history (file, ptr_buffer->last_history)) + return 0; + } + return 1; +} + +/* + * session_save_uptime: save uptime into session file + */ + +int +session_save_uptime (FILE *file) +{ + int rc; + + rc = 1; + + rc = rc && (session_write_id (file, SESSION_OBJ_UPTIME)); + rc = rc && (session_write_buf (file, SESSION_UPT_START_TIME, &weechat_start_time, sizeof (time_t))); + rc = rc && (session_write_id (file, SESSION_UPT_END)); + return rc; +} + +/* + * session_save_hotlist: save hotlist into session file + */ + +int +session_save_hotlist (FILE *file) +{ + int rc; + t_weechat_hotlist *ptr_hotlist; + + rc = 1; + + for (ptr_hotlist = weechat_hotlist; ptr_hotlist; + ptr_hotlist = ptr_hotlist->next_hotlist) + { + rc = rc && (session_write_id (file, SESSION_OBJ_HOTLIST)); + rc = rc && (session_write_int (file, SESSION_HOTL_PRIORITY, ptr_hotlist->priority)); + rc = rc && (session_write_str (file, SESSION_HOTL_SERVER, (ptr_hotlist->server) ? ptr_hotlist->server->name : NULL)); + rc = rc && (session_write_int (file, SESSION_HOTL_BUFFER_NUMBER, ptr_hotlist->buffer->number)); + rc = rc && (session_write_buf (file, SESSION_HOTL_CREATION_TIME, &(ptr_hotlist->creation_time), sizeof (struct timeval))); + rc = rc && (session_write_id (file, SESSION_HOTL_END)); + + if (!rc) + return 0; + } + return rc; +} + +/* + * session_save: save current session + */ + +int +session_save (char *filename) +{ + FILE *file; + int rc; + + if ((file = fopen (filename, "wb")) == NULL) + return 0; + + rc = 1; + rc = rc && (session_write_str (file, -1, SESSION_SIGNATURE)); + rc = rc && (session_save_servers (file)); + rc = rc && (session_save_dcc (file)); + rc = rc && (session_save_history (file, history_global_last)); + rc = rc && (session_save_buffers (file)); + rc = rc && (session_save_uptime (file)); + rc = rc && (session_save_hotlist (file)); + + fclose (file); + + return rc; +} + +/* ========================================================================== */ + +/* + * session_crash: stop WeeChat if problem during session loading + */ + +void +session_crash (FILE *file, char *message, ...) +{ + char buffer[4096]; + va_list argptr; + + va_start (argptr, message); + vsnprintf (buffer, sizeof (buffer) - 1, message, argptr); + va_end (argptr); + + fclose (file); + gui_main_end (); + weechat_iconv_fprintf (stderr, "%s %s\n", + WEECHAT_ERROR, buffer); + weechat_iconv_fprintf (stderr, + _("Last operation with session file was at position %ld, " + "read of %d bytes\n"), + session_last_read_pos, + session_last_read_length); + weechat_iconv_fprintf (stderr, + _("Please send %s/%s, %s/%s and " + "above messages to WeeChat developers for support.\n" + "Be careful, private info may be in these files.\n"), + weechat_home, + WEECHAT_LOG_NAME, + weechat_home, + WEECHAT_SESSION_NAME); + exit (EXIT_FAILURE); +} + +/* + * session_read_int: read integer from file + */ + +int +session_read_int (FILE *file, int *value) +{ + char type; + + session_last_read_pos = ftell (file); + session_last_read_length = sizeof (char); + + if (fread ((void *)(&type), sizeof (char), 1, file) == 0) + return 0; + if (type != SESSION_TYPE_INT) + { + session_crash (file, _("wrong type in file (expected: %d, read: %d)"), + SESSION_TYPE_INT, type); + return 0; + } + + session_last_read_pos = ftell (file); + session_last_read_length = sizeof (int); + + if (value) + return (fread ((void *)value, sizeof (int), 1, file) > 0); + else + return (fseek (file, sizeof (int), SEEK_CUR) >= 0); +} + +/* + * session_read_str: read string from file + */ + +int +session_read_str (FILE *file, char **string) +{ + char type; + int length; + + if (string && *string) + free (*string); + + session_last_read_pos = ftell (file); + session_last_read_length = sizeof (char); + + if (fread ((void *)(&type), sizeof (char), 1, file) == 0) + return 0; + if (type != SESSION_TYPE_STR) + { + session_crash (file, _("wrong type in file (expected: %d, read: %d)"), + SESSION_TYPE_STR, type); + return 0; + } + + session_last_read_pos = ftell (file); + session_last_read_length = sizeof (int); + + if (fread ((void *)(&length), sizeof (int), 1, file) == 0) + return 0; + + if (length == 0) + { + if (string) + (*string) = NULL; + return 1; + } + + session_last_read_pos = ftell (file); + session_last_read_length = length; + + if (string) + { + (*string) = (char *) malloc (length + 1); + if (!(*string)) + return 0; + + if (fread ((void *)(*string), length, 1, file) == 0) + { + free (*string); + return 0; + } + (*string)[length] = '\0'; + } + else + return (fseek (file, length, SEEK_CUR) >= 0); + + return 1; +} + +/* + * session_read_str_utf8: read string from file, then normalize UTF-8 + */ + +int +session_read_str_utf8 (FILE *file, char **string) +{ + int rc; + + rc = session_read_str (file, string); + if (rc && *string) + utf8_normalize (*string, '?'); + + return rc; +} + +/* + * session_read_buf: read buffer from file + */ + +int +session_read_buf (FILE *file, void *buffer, int length_expected) +{ + char type; + int length; + + session_last_read_pos = ftell (file); + session_last_read_length = sizeof (char); + + if (fread ((void *)(&type), sizeof (char), 1, file) == 0) + return 0; + if (type != SESSION_TYPE_BUF) + { + session_crash (file, _("wrong type in file (expected: %d, read: %d)"), + SESSION_TYPE_BUF, type); + return 0; + } + + session_last_read_pos = ftell (file); + session_last_read_length = sizeof (int); + + if (fread ((void *)(&length), sizeof (int), 1, file) == 0) + return 0; + if ((length <= 0) || ((length_expected > 0) && (length != length_expected))) + { + session_crash (file, _("invalid length for a buffer")); + return 0; + } + + session_last_read_pos = ftell (file); + session_last_read_length = length; + + if (buffer) + return (fread (buffer, length, 1, file) > 0); + else + return (fseek (file, length, SEEK_CUR) >= 0); +} + +/* + * session_read_buf_alloc: read buffer from file and allocate it in memory + */ + +int +session_read_buf_alloc (FILE *file, void **buffer, int *buffer_length) +{ + char type; + + session_last_read_pos = ftell (file); + session_last_read_length = sizeof (char); + + if (fread ((void *)(&type), sizeof (char), 1, file) == 0) + return 0; + if (type != SESSION_TYPE_BUF) + { + session_crash (file, _("wrong type in file (expected: %d, read: %d)"), + SESSION_TYPE_BUF, type); + return 0; + } + + session_last_read_pos = ftell (file); + session_last_read_length = sizeof (int); + + if (fread ((void *)(buffer_length), sizeof (int), 1, file) == 0) + return 0; + if (*buffer_length <= 0) + { + session_crash (file, _("invalid length for a buffer")); + return 0; + } + + *buffer = malloc (*buffer_length); + + session_last_read_pos = ftell (file); + session_last_read_length = *buffer_length; + + return (fread (*buffer, *buffer_length, 1, file) > 0); +} + +/* + * session_read_object: read an object in file + */ + +int +session_read_object (FILE *file, int object_id, int type, void *target, int max_buf_length) +{ + int object_id_read; + char type_read; + + if (fread ((void *)(&object_id_read), sizeof (int), 1, file) == 0) + { + session_crash (file, _("object read error")); + return 0; + } + if (object_id_read != object_id) + { + session_crash (file, _("wrong object (expected: %d, read: %d)"), + object_id, object_id_read); + return 0; + } + + session_last_read_pos = ftell (file); + session_last_read_length = sizeof (char); + + if (fread ((void *)(&type_read), sizeof (char), 1, file) == 0) + { + session_crash (file, _("type read error")); + return 0; + } + if (type_read != type) + { + session_crash (file, _("wrong type (expected: %d, read: %d)"), + type, type_read); + return 0; + } + if (fseek (file, sizeof (char) * (-1), SEEK_CUR) < 0) + return 0; + switch (type) + { + case SESSION_TYPE_INT: + return session_read_int (file, (int *)target); + case SESSION_TYPE_STR: + return session_read_str (file, (char **)target); + case SESSION_TYPE_BUF: + return session_read_buf (file, target, max_buf_length); + } + return 0; +} + +/* + * session_read_ignore_value: ignore a value from file + */ + +int +session_read_ignore_value (FILE *file) +{ + char type; + + if (fread ((void *)(&type), sizeof (char), 1, file) == 0) + return 0; + if (fseek (file, sizeof (char) * (-1), SEEK_CUR) < 0) + return 0; + switch (type) + { + case SESSION_TYPE_INT: + return session_read_int (file, NULL); + case SESSION_TYPE_STR: + return session_read_str (file, NULL); + case SESSION_TYPE_BUF: + return session_read_buf (file, NULL, 0); + } + return 0; +} + +/* + * session_read_ignore_object: ignore an object from file + */ + +int +session_read_ignore_object (FILE *file) +{ + int object_id; + + while (1) + { + if (fread ((void *)(&object_id), sizeof (int), 1, file) == 0) + return 0; + if (feof (file)) + return 0; + if (object_id == SESSION_OBJ_END) + return 1; + if (!session_read_ignore_value (file)) + return 0; + } +} + +/* + * session_load_server: load server from file + */ + +int +session_load_server (FILE *file) +{ + int object_id, rc; + char *server_name; +#ifdef HAVE_GNUTLS + void *session_data; + size_t session_size; + int session_size_int; +#endif + + /* read server name */ + server_name = NULL; + if (!session_read_object (file, SESSION_SERV_NAME, SESSION_TYPE_STR, &server_name, 0)) + { + session_crash (file, _("server name not found")); + return 0; + } + + /* use or allocate server */ + weechat_log_printf (_("session: loading server \"%s\"\n"), + server_name); + session_current_server = irc_server_search (server_name); + if (session_current_server) + weechat_log_printf (_("server found, updating values\n")); + else + { + weechat_log_printf (_("server not found, creating new one\n")); + session_current_server = irc_server_alloc (); + if (!session_current_server) + { + free (server_name); + session_crash (file, _("can't create new server")); + return 0; + } + irc_server_init (session_current_server); + session_current_server->name = strdup (server_name); + } + free (server_name); + + /* read server values */ + rc = 1; + while (rc) + { + if (feof (file)) + { + session_crash (file, _("unexpected end of file (reading server)")); + return 0; + } + if (fread ((void *)(&object_id), sizeof (int), 1, file) == 0) + return 0; + switch (object_id) + { + case SESSION_SERV_END: + return 1; + case SESSION_SERV_AUTOCONNECT: + rc = rc && (session_read_int (file, &(session_current_server->autoconnect))); + break; + case SESSION_SERV_AUTORECONNECT: + rc = rc && (session_read_int (file, &(session_current_server->autoreconnect))); + break; + case SESSION_SERV_AUTORECONNECT_DELAY: + rc = rc && (session_read_int (file, &(session_current_server->autoreconnect_delay))); + break; + case SESSION_SERV_TEMP_SERVER: + rc = rc && (session_read_int (file, &(session_current_server->temp_server))); + break; + case SESSION_SERV_ADDRESS: + rc = rc && (session_read_str (file, &(session_current_server->address))); + break; + case SESSION_SERV_PORT: + rc = rc && (session_read_int (file, &(session_current_server->port))); + break; + case SESSION_SERV_IPV6: + rc = rc && (session_read_int (file, &(session_current_server->ipv6))); + break; + case SESSION_SERV_SSL: + rc = rc && (session_read_int (file, &(session_current_server->ssl))); + break; + case SESSION_SERV_PASSWORD: + rc = rc && (session_read_str (file, &(session_current_server->password))); + break; + case SESSION_SERV_NICK1: + rc = rc && (session_read_str (file, &(session_current_server->nick1))); + break; + case SESSION_SERV_NICK2: + rc = rc && (session_read_str (file, &(session_current_server->nick2))); + break; + case SESSION_SERV_NICK3: + rc = rc && (session_read_str (file, &(session_current_server->nick3))); + break; + case SESSION_SERV_USERNAME: + rc = rc && (session_read_str (file, &(session_current_server->username))); + break; + case SESSION_SERV_REALNAME: + rc = rc && (session_read_str (file, &(session_current_server->realname))); + break; + case SESSION_SERV_HOSTNAME: + rc = rc && (session_read_str (file, &(session_current_server->hostname))); + break; + case SESSION_SERV_COMMAND: + rc = rc && (session_read_str (file, &(session_current_server->command))); + break; + case SESSION_SERV_COMMAND_DELAY: + rc = rc && (session_read_int (file, &(session_current_server->command_delay))); + break; + case SESSION_SERV_AUTOJOIN: + rc = rc && (session_read_str (file, &(session_current_server->autojoin))); + break; + case SESSION_SERV_AUTOREJOIN: + rc = rc && (session_read_int (file, &(session_current_server->autorejoin))); + break; + case SESSION_SERV_NOTIFY_LEVELS: + rc = rc && (session_read_str (file, &(session_current_server->notify_levels))); + break; + case SESSION_SERV_CHILD_PID: + rc = rc && (session_read_int (file, &(session_current_server->child_pid))); + break; + case SESSION_SERV_CHILD_READ: + rc = rc && (session_read_int (file, &(session_current_server->child_read))); + break; + case SESSION_SERV_CHILD_WRITE: + rc = rc && (session_read_int (file, &(session_current_server->child_write))); + break; + case SESSION_SERV_SOCK: + rc = rc && (session_read_int (file, &(session_current_server->sock))); + break; + case SESSION_SERV_IS_CONNECTED: + rc = rc && (session_read_int (file, &(session_current_server->is_connected))); + break; + case SESSION_SERV_SSL_CONNECTED: + rc = rc && (session_read_int (file, &(session_current_server->ssl_connected))); + break; +#ifdef HAVE_GNUTLS + case SESSION_SERV_GNUTLS_SESS: + if (gnutls_init (&(session_current_server->gnutls_sess), GNUTLS_CLIENT) != 0) + { + session_crash (file, _("gnutls init error")); + return 0; + } + gnutls_set_default_priority (session_current_server->gnutls_sess); + gnutls_certificate_type_set_priority (session_current_server->gnutls_sess, gnutls_cert_type_prio); + gnutls_protocol_set_priority (session_current_server->gnutls_sess, gnutls_prot_prio); + gnutls_credentials_set (session_current_server->gnutls_sess, GNUTLS_CRD_CERTIFICATE, gnutls_xcred); + session_data = NULL; + rc = rc && (session_read_buf_alloc (file, &session_data, &session_size_int)); + if (rc) + { + session_size = session_size_int; + gnutls_session_set_data (session_current_server->gnutls_sess, session_data, session_size); + free (session_data); + gnutls_transport_set_ptr (session_current_server->gnutls_sess, + (gnutls_transport_ptr) ((unsigned long) session_current_server->sock)); + if (gnutls_handshake (session_current_server->gnutls_sess) < 0) + { + session_crash (file, _("gnutls handshake failed")); + return 0; + } + } + break; +#endif + case SESSION_SERV_UNTERMINATED_MESSAGE: + rc = rc && (session_read_str (file, &(session_current_server->unterminated_message))); + break; + case SESSION_SERV_NICK: + rc = rc && (session_read_str (file, &(session_current_server->nick))); + break; + case SESSION_SERV_NICK_MODES: + rc = rc && (session_read_str (file, &(session_current_server->nick_modes))); + break; + case SESSION_SERV_PREFIX: + rc = rc && (session_read_str (file, &(session_current_server->prefix))); + break; + case SESSION_SERV_RECONNECT_START: + rc = rc && (session_read_buf (file, &(session_current_server->reconnect_start), sizeof (time_t))); + break; + case SESSION_SERV_COMMAND_TIME: + rc = rc && (session_read_buf (file, &(session_current_server->command_time), sizeof (time_t))); + break; + case SESSION_SERV_RECONNECT_JOIN: + rc = rc && (session_read_int (file, &(session_current_server->reconnect_join))); + break; + case SESSION_SERV_IS_AWAY: + rc = rc && (session_read_int (file, &(session_current_server->is_away))); + break; + case SESSION_SERV_AWAY_MESSAGE: + rc = rc && (session_read_str (file, &(session_current_server->away_message))); + break; + case SESSION_SERV_AWAY_TIME: + rc = rc && (session_read_buf (file, &(session_current_server->away_time), sizeof (time_t))); + break; + case SESSION_SERV_LAG: + rc = rc && (session_read_int (file, &(session_current_server->lag))); + break; + case SESSION_SERV_LAG_CHECK_TIME: + rc = rc && (session_read_buf (file, &(session_current_server->lag_check_time), sizeof (struct timeval))); + break; + case SESSION_SERV_LAG_NEXT_CHECK: + rc = rc && (session_read_buf (file, &(session_current_server->lag_next_check), sizeof (time_t))); + break; + case SESSION_SERV_CHARSET_DECODE_ISO__UNUSED: + rc = rc && (session_read_ignore_value (file)); + break; + case SESSION_SERV_CHARSET_DECODE_UTF__UNUSED: + rc = rc && (session_read_ignore_value (file)); + break; + case SESSION_SERV_CHARSET_ENCODE__UNUSED: + rc = rc && (session_read_ignore_value (file)); + break; + default: + weechat_log_printf (_("session: warning: ignoring value from " + "server (object id: %d)\n"), + object_id); + rc = rc && (session_read_ignore_value (file)); + break; + } + } + return 0; +} + +/* + * session_load_channel: load channel from file + */ + +int +session_load_channel (FILE *file) +{ + int object_id, rc, channel_type; + char *channel_name; + + /* check if server is allocated for this channel */ + if (!session_current_server) + { + session_crash (file, _("channel found without server")); + return 0; + } + + /* read channel type */ + if (!session_read_object (file, SESSION_CHAN_TYPE, SESSION_TYPE_INT, &channel_type, 0)) + { + session_crash (file, _("channel type not found")); + return 0; + } + + /* read channel name */ + channel_name = NULL; + if (!session_read_object (file, SESSION_CHAN_NAME, SESSION_TYPE_STR, &channel_name, 0)) + { + session_crash (file, _("channel name not found")); + return 0; + } + + /* allocate channel */ + weechat_log_printf (_("session: loading channel \"%s\"\n"), + channel_name); + session_current_channel = irc_channel_new (session_current_server, + channel_type, + channel_name); + free (channel_name); + if (!session_current_channel) + { + session_crash (file, _("can't create new channel")); + return 0; + } + + /* read channel values */ + rc = 1; + while (rc) + { + if (feof (file)) + { + session_crash (file, _("unexpected end of file (reading channel)")); + return 0; + } + if (fread ((void *)(&object_id), sizeof (int), 1, file) == 0) + return 0; + switch (object_id) + { + case SESSION_CHAN_END: + return 1; + case SESSION_CHAN_TOPIC: + rc = rc && (session_read_str_utf8 (file, &(session_current_channel->topic))); + break; + case SESSION_CHAN_MODES: + rc = rc && (session_read_str (file, (char **)(&(session_current_channel->modes)))); + break; + case SESSION_CHAN_LIMIT: + rc = rc && (session_read_int (file, &(session_current_channel->limit))); + break; + case SESSION_CHAN_KEY: + rc = rc && (session_read_str (file, &(session_current_channel->key))); + break; + case SESSION_CHAN_NICKS_COUNT: + rc = rc && (session_read_int (file, &(session_current_channel->nicks_count))); + /* will be incremented when adding nicks */ + session_current_channel->nicks_count = 0; + break; + case SESSION_CHAN_CHECKING_AWAY: + rc = rc && (session_read_int (file, &(session_current_channel->checking_away))); + break; + case SESSION_CHAN_AWAY_MESSAGE: + rc = rc && (session_read_str (file, &(session_current_channel->away_message))); + break; + case SESSION_CHAN_CYCLE: + rc = rc && (session_read_int (file, &(session_current_channel->cycle))); + break; + case SESSION_CHAN_CLOSE: + rc = rc && (session_read_int (file, &(session_current_channel->close))); + break; + case SESSION_CHAN_DISPLAY_CREATION_DATE: + rc = rc && (session_read_int (file, &(session_current_channel->display_creation_date))); + break; + default: + weechat_log_printf (_("session: warning: ignoring value from " + "channel (object id: %d)\n"), + object_id); + rc = rc && (session_read_ignore_value (file)); + break; + } + } + return 0; +} + +/* + * session_load_nick: load nick from file + */ + +int +session_load_nick (FILE *file) +{ + int rc, object_id; + char *nick_name; + t_irc_nick *nick; + + /* check if channel is allocated for this nick */ + if (!session_current_channel) + { + session_crash (file, _("nick found without channel")); + return 0; + } + + /* read nick name */ + nick_name = NULL; + if (!session_read_object (file, SESSION_NICK_NICK, SESSION_TYPE_STR, &nick_name, 0)) + { + session_crash (file, _("nick name not found")); + return 0; + } + + /* allocate nick */ + nick = irc_nick_new (session_current_server, session_current_channel, + nick_name, 0, 0, 0, 0, 0, 0, 0); + free (nick_name); + if (!nick) + { + session_crash (file, _("can't create new nick")); + return 0; + } + + /* read nick values */ + rc = 1; + while (rc) + { + if (feof (file)) + { + session_crash (file, _("unexpected end of file (reading nick)")); + return 0; + } + if (fread ((void *)(&object_id), sizeof (int), 1, file) == 0) + return 0; + switch (object_id) + { + case SESSION_NICK_END: + return 1; + case SESSION_NICK_FLAGS: + rc = rc && (session_read_int (file, &(nick->flags))); + break; + case SESSION_NICK_COLOR: + rc = rc && (session_read_int (file, &(nick->color))); + break; + case SESSION_NICK_HOST: + rc = rc && (session_read_str (file, &(nick->host))); + break; + default: + weechat_log_printf (_("session: warning: ignoring value from " + "nick (object id: %d)\n"), + object_id); + rc = rc && (session_read_ignore_value (file)); + break; + } + } + return 0; +} + +/* + * session_load_dcc: load DCC from file + */ + +int +session_load_dcc (FILE *file) +{ + int object_id, rc; + t_irc_dcc *dcc; + char *string; + t_irc_server *ptr_server; + t_irc_channel *ptr_channel; + + /* allocate DCC */ + dcc = irc_dcc_alloc (); + if (!dcc) + { + session_crash (file, _("can't create new DCC")); + return 0; + } + + weechat_log_printf (_("session: loading DCC\n")); + + /* read DCC values */ + ptr_server = NULL; + ptr_channel = NULL; + rc = 1; + while (rc) + { + if (feof (file)) + { + session_crash (file, _("unexpected end of file (reading DCC)")); + return 0; + } + if (fread ((void *)(&object_id), sizeof (int), 1, file) == 0) + return 0; + switch (object_id) + { + case SESSION_DCC_END: + return 1; + case SESSION_DCC_SERVER: + string = NULL; + rc = rc && (session_read_str (file, &string)); + if (!rc) + return 0; + if (string && string[0]) + { + ptr_server = irc_server_search (string); + if (!ptr_server) + { + session_crash (file, _("server not found for DCC")); + return 0; + } + dcc->server = ptr_server; + } + break; + case SESSION_DCC_CHANNEL: + if (!ptr_server) + { + session_crash (file, _("DCC with channel but without server")); + return 0; + } + string = NULL; + rc = rc && (session_read_str (file, &string)); + if (!rc) + return 0; + if (string && string[0]) + { + ptr_channel = irc_channel_search_any (ptr_server, string); + if (!ptr_channel) + { + session_crash (file, _("channel not found for DCC")); + return 0; + } + dcc->channel = ptr_channel; + ptr_channel->dcc_chat = dcc; + } + break; + case SESSION_DCC_TYPE: + rc = rc && (session_read_int (file, &(dcc->type))); + break; + case SESSION_DCC_STATUS: + rc = rc && (session_read_int (file, &(dcc->status))); + break; + case SESSION_DCC_START_TIME: + rc = rc && (session_read_buf (file, &(dcc->start_time), sizeof (time_t))); + break; + case SESSION_DCC_START_TRANSFER: + rc = rc && (session_read_buf (file, &(dcc->start_transfer), sizeof (time_t))); + break; + case SESSION_DCC_ADDR: + rc = rc && (session_read_buf (file, &(dcc->addr), sizeof (unsigned long))); + break; + case SESSION_DCC_PORT: + rc = rc && (session_read_int (file, &(dcc->port))); + break; + case SESSION_DCC_NICK: + rc = rc && (session_read_str (file, &(dcc->nick))); + break; + case SESSION_DCC_SOCK: + rc = rc && (session_read_int (file, &(dcc->sock))); + break; + case SESSION_DCC_UNTERMINATED_MESSAGE: + rc = rc && (session_read_str (file, &(dcc->unterminated_message))); + break; + case SESSION_DCC_FILE: + rc = rc && (session_read_int (file, &(dcc->file))); + break; + case SESSION_DCC_FILENAME: + rc = rc && (session_read_str (file, &(dcc->filename))); + break; + case SESSION_DCC_LOCAL_FILENAME: + rc = rc && (session_read_str (file, &(dcc->local_filename))); + break; + case SESSION_DCC_FILENAME_SUFFIX: + rc = rc && (session_read_int (file, &(dcc->filename_suffix))); + break; + case SESSION_DCC_SIZE: + rc = rc && (session_read_buf (file, &(dcc->size), sizeof (unsigned long))); + break; + case SESSION_DCC_POS: + rc = rc && (session_read_buf (file, &(dcc->pos), sizeof (unsigned long))); + break; + case SESSION_DCC_ACK: + rc = rc && (session_read_buf (file, &(dcc->ack), sizeof (unsigned long))); + break; + case SESSION_DCC_START_RESUME: + rc = rc && (session_read_buf (file, &(dcc->start_resume), sizeof (unsigned long))); + break; + case SESSION_DCC_LAST_CHECK_TIME: + rc = rc && (session_read_buf (file, &(dcc->last_check_time), sizeof (time_t))); + break; + case SESSION_DCC_LAST_CHECK_POS: + rc = rc && (session_read_buf (file, &(dcc->last_check_pos), sizeof (unsigned long))); + break; + case SESSION_DCC_LAST_ACTIVITY: + rc = rc && (session_read_buf (file, &(dcc->last_activity), sizeof (time_t))); + break; + case SESSION_DCC_BYTES_PER_SEC: + rc = rc && (session_read_buf (file, &(dcc->bytes_per_sec), sizeof (unsigned long))); + break; + case SESSION_DCC_ETA: + rc = rc && (session_read_buf (file, &(dcc->eta), sizeof (unsigned long))); + break; + case SESSION_DCC_CHILD_PID: + rc = rc && (session_read_int (file, &(dcc->child_pid))); + break; + case SESSION_DCC_CHILD_READ: + rc = rc && (session_read_int (file, &(dcc->child_read))); + break; + case SESSION_DCC_CHILD_WRITE: + rc = rc && (session_read_int (file, &(dcc->child_write))); + break; + default: + weechat_log_printf (_("session: warning: ignoring value from " + "DCC (object id: %d)\n"), + object_id); + rc = rc && (session_read_ignore_value (file)); + break; + } + } + return 0; +} + +/* + * session_load_history: load history from file (global or for a buffer) + */ + +int +session_load_history (FILE *file) +{ + int object_id, rc; + char *text; + + if (session_current_buffer) + weechat_log_printf (_("session: loading buffer history\n")); + else + weechat_log_printf (_("session: loading global history\n")); + + /* read history values */ + rc = 1; + while (rc) + { + if (feof (file)) + { + session_crash (file, _("unexpected end of file (reading history)")); + return 0; + } + if (fread ((void *)(&object_id), sizeof (int), 1, file) == 0) + return 0; + switch (object_id) + { + case SESSION_HIST_END: + return 1; + case SESSION_HIST_TEXT: + text = NULL; + if (!session_read_str_utf8 (file, &text)) + return 0; + if (session_current_buffer) + history_buffer_add (session_current_buffer, text); + else + history_global_add (text); + free (text); + break; + default: + weechat_log_printf (_("session: warning: ignoring value from " + "history (object id: %d)\n"), + object_id); + rc = rc && (session_read_ignore_value (file)); + break; + } + } + return 0; +} + +/* + * session_load_buffer: load buffer from file + */ + +int +session_load_buffer (FILE *file) +{ + int object_id, rc; + char *server_name, *channel_name; + int buffer_type; + t_irc_server *ptr_server; + t_irc_channel *ptr_channel; + + /* read server name */ + server_name = NULL; + if (!session_read_object (file, SESSION_BUFF_SERVER, SESSION_TYPE_STR, &server_name, 0)) + { + session_crash (file, _("server name not found for buffer")); + return 0; + } + + /* read channel name */ + channel_name = NULL; + if (!session_read_object (file, SESSION_BUFF_CHANNEL, SESSION_TYPE_STR, &channel_name, 0)) + { + session_crash (file, _("channel name not found for buffer")); + return 0; + } + + /* read buffer type */ + if (!session_read_object (file, SESSION_BUFF_TYPE, SESSION_TYPE_INT, &buffer_type, 0)) + { + session_crash (file, _("buffer type not found")); + return 0; + } + + /* allocate buffer */ + weechat_log_printf (_("session: loading buffer (server: %s, channel: %s, type: %d)\n"), + (server_name) ? server_name : "-", + (channel_name) ? channel_name : "-", + buffer_type); + ptr_server = NULL; + ptr_channel = NULL; + if (server_name) + { + ptr_server = irc_server_search (server_name); + if (!ptr_server) + { + session_crash (file, _("server not found for buffer")); + return 0; + } + } + + if (channel_name) + { + ptr_channel = irc_channel_search_any_without_buffer (ptr_server, channel_name); + if (!ptr_channel) + { + session_crash (file, _("channel not found for buffer")); + return 0; + } + } + + session_current_buffer = gui_buffer_new (gui_windows, ptr_server, + ptr_channel, buffer_type, 1); + if (!session_current_buffer) + { + session_crash (file, _("can't create new buffer")); + return 0; + } + + free (server_name); + free (channel_name); + + /* read buffer values */ + rc = 1; + while (rc) + { + if (feof (file)) + { + session_crash (file, _("unexpected end of file (reading buffer)")); + return 0; + } + if (fread ((void *)(&object_id), sizeof (int), 1, file) == 0) + return 0; + switch (object_id) + { + case SESSION_BUFF_END: + return 1; + case SESSION_BUFF_ALL_SERVERS: + rc = rc && (session_read_int (file, &(session_current_buffer->all_servers))); + break; + default: + weechat_log_printf (_("session: warning: ignoring value from " + "buffer (object id: %d)\n"), + object_id); + rc = rc && (session_read_ignore_value (file)); + break; + } + } + return 0; +} + +/* + * session_load_line: load buffer line from file + */ + +int +session_load_line (FILE *file) +{ + int object_id, rc; + t_gui_line *line; + + /* check if buffer is allocated for this line */ + if (!session_current_buffer) + { + session_crash (file, _("line found without buffer")); + return 0; + } + + /* allocate line */ + line = gui_buffer_line_new (session_current_buffer, time (NULL)); + if (!line) + { + session_crash (file, _("can't create new line")); + return 0; + } + + /* read line values */ + rc = 1; + while (rc) + { + if (feof (file)) + { + session_crash (file, _("unexpected end of file (reading line)")); + return 0; + } + if (fread ((void *)(&object_id), sizeof (int), 1, file) == 0) + return 0; + switch (object_id) + { + case SESSION_LINE_END: + return 1; + case SESSION_LINE_LENGTH: + rc = rc && (session_read_int (file, &(line->length))); + break; + case SESSION_LINE_LENGTH_ALIGN: + rc = rc && (session_read_int (file, &(line->length_align))); + break; + case SESSION_LINE_LOG_WRITE: + rc = rc && (session_read_int (file, &(line->log_write))); + break; + case SESSION_LINE_WITH_MESSAGE: + rc = rc && (session_read_int (file, &(line->line_with_message))); + break; + case SESSION_LINE_WITH_HIGHLIGHT: + rc = rc && (session_read_int (file, &(line->line_with_highlight))); + break; + case SESSION_LINE_DATA: + rc = rc && (session_read_str_utf8 (file, &(line->data))); + break; + case SESSION_LINE_OFS_AFTER_DATE: + rc = rc && (session_read_int (file, &(line->ofs_after_date))); + break; + case SESSION_LINE_OFS_START_MESSAGE: + rc = rc && (session_read_int (file, &(line->ofs_start_message))); + break; + case SESSION_LINE_NICK: + rc = rc && (session_read_str (file, &(line->nick))); + break; + case SESSION_LINE_DATE: + rc = rc && (session_read_buf (file, &(line->date), sizeof (time_t))); + break; + default: + weechat_log_printf (_("session: warning: ignoring value from " + "line (object id: %d)\n"), + object_id); + rc = rc && (session_read_ignore_value (file)); + break; + } + } + return 0; +} + +/* + * session_load_uptime: load uptime from file + */ + +int +session_load_uptime (FILE *file) +{ + int object_id, rc; + + /* read uptime values */ + rc = 1; + while (rc) + { + if (feof (file)) + { + session_crash (file, _("unexpected end of file (reading uptime)")); + return 0; + } + if (fread ((void *)(&object_id), sizeof (int), 1, file) == 0) + return 0; + switch (object_id) + { + case SESSION_UPT_END: + return 1; + case SESSION_UPT_START_TIME: + rc = rc && (session_read_buf (file, &weechat_start_time, sizeof (time_t))); + break; + default: + weechat_log_printf (_("session: warning: ignoring value from " + "uptime (object id: %d)\n"), + object_id); + rc = rc && (session_read_ignore_value (file)); + break; + } + } + return 0; +} + +/* + * session_load_hotlist: load hotlist from file + */ + +int +session_load_hotlist (FILE *file) +{ + int object_id, rc; + int priority; + struct timeval creation_time; + char *server_name; + t_irc_server *ptr_server; + int buffer_number; + t_gui_buffer *ptr_buffer; + + priority = 0; + creation_time.tv_sec = 0; + creation_time.tv_usec = 0; + ptr_server = NULL; + ptr_buffer = NULL; + + /* read hotlist values */ + rc = 1; + while (rc) + { + if (feof (file)) + { + session_crash (file, _("unexpected end of file (reading hotlist)")); + return 0; + } + if (fread ((void *)(&object_id), sizeof (int), 1, file) == 0) + return 0; + switch (object_id) + { + case SESSION_HOTL_END: + hotlist_add (priority, &creation_time, ptr_server, ptr_buffer, 1); + return 1; + case SESSION_HOTL_PRIORITY: + rc = rc && (session_read_int (file, &priority)); + break; + case SESSION_HOTL_SERVER: + server_name = NULL; + if (!session_read_str (file, &server_name)) + return 0; + ptr_server = irc_server_search (server_name); + free (server_name); + break; + case SESSION_HOTL_BUFFER_NUMBER: + rc = rc && (session_read_int (file, &buffer_number)); + ptr_buffer = gui_buffer_search_by_number (buffer_number); + break; + case SESSION_HOTL_CREATION_TIME: + rc = rc && (session_read_buf (file, &creation_time, sizeof (struct timeval))); + break; + default: + weechat_log_printf (_("session: warning: ignoring value from " + "history (object id: %d)\n"), + object_id); + rc = rc && (session_read_ignore_value (file)); + break; + } + } + return 0; +} + +/* + * session_load: load session from file + */ + +int +session_load (char *filename) +{ + FILE *file; + char *signature; + int object_id; + t_irc_server *ptr_server; + + session_current_server = NULL; + session_current_channel = NULL; + session_current_buffer = NULL; + + session_last_read_pos = -1; + session_last_read_length = -1; + + if ((file = fopen (filename, "rb")) == NULL) + { + session_crash (file, _("session file not found")); + return 0; + } + + signature = NULL; + if (!session_read_str (file, &signature)) + { + session_crash (file, _("signature not found")); + return 0; + } + if (!signature || (strcmp (signature, SESSION_SIGNATURE) != 0)) + { + session_crash (file, _("bad session signature")); + return 0; + } + free (signature); + + while (!feof (file)) + { + if (fread ((void *)(&object_id), sizeof (int), 1, file) == 0) + { + if (feof (file)) + break; + session_crash (file, _("object id not found")); + return 0; + } + switch (object_id) + { + case SESSION_OBJ_SERVER: + if (!session_load_server (file)) + { + session_crash (file, _("failed to load server")); + return 0; + } + break; + case SESSION_OBJ_CHANNEL: + if (!session_load_channel (file)) + { + session_crash (file, _("failed to load channel")); + return 0; + } + break; + case SESSION_OBJ_NICK: + if (!session_load_nick (file)) + { + session_crash (file, _("failed to load nick")); + return 0; + } + break; + case SESSION_OBJ_DCC: + if (!session_load_dcc (file)) + { + session_crash (file, _("failed to load DCC")); + return 0; + } + break; + case SESSION_OBJ_HISTORY: + if (!session_load_history (file)) + { + session_crash (file, _("failed to load history")); + return 0; + } + break; + case SESSION_OBJ_BUFFER: + if (!session_load_buffer (file)) + { + session_crash (file, _("failed to load buffer")); + return 0; + } + break; + case SESSION_OBJ_LINE: + if (!session_load_line (file)) + { + session_crash (file, _("failed to load line")); + return 0; + } + break; + case SESSION_OBJ_UPTIME: + if (!session_load_uptime (file)) + { + session_crash (file, _("failed to load uptime")); + return 0; + } + break; + case SESSION_OBJ_HOTLIST: + if (!session_load_hotlist (file)) + { + session_crash (file, _("failed to load hotlist")); + return 0; + } + break; + default: + weechat_log_printf (_("ignoring object (id: %d)\n"), + object_id); + if (!session_read_ignore_object (file)) + { + session_crash (file, _("failed to ignore object (id: %d)"), + object_id); + return 0; + } + } + } + + /* assign a buffer to all connected servers */ + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if ((ptr_server->is_connected) && (!ptr_server->buffer)) + ptr_server->buffer = gui_buffers; + } + + gui_window_switch_to_buffer (gui_windows, gui_buffers); + gui_window_redraw_buffer (gui_current_window->buffer); + + fclose (file); + + if (unlink (filename) < 0) + { + irc_display_prefix (NULL, gui_current_window->buffer, GUI_PREFIX_ERROR); + gui_printf_nolog (gui_current_window->buffer, + _("%s can't delete session file (%s)\n"), + WEECHAT_ERROR, filename); + } + + irc_display_prefix (NULL, gui_current_window->buffer, GUI_PREFIX_INFO); + gui_printf_nolog (gui_current_window->buffer, + _("Upgrade completed successfully\n")); + + return 1; +} diff --git a/src/core/wee-upgrade.h b/src/core/wee-upgrade.h new file mode 100644 index 000000000..7728220f3 --- /dev/null +++ b/src/core/wee-upgrade.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_SESSION_H +#define __WEECHAT_SESSION_H 1 + +#define WEECHAT_SESSION_NAME "weechat_session.bin" + +#define SESSION_SIGNATURE "== WeeChat Upgrade file v1.0 - binary, do not edit! ==" + +/* For developers: please add new values ONLY AT THE END of enums */ + +enum t_session_type +{ + SESSION_TYPE_INT = 0, + SESSION_TYPE_STR, + SESSION_TYPE_BUF +}; + +enum t_session_object +{ + SESSION_OBJ_END = 0, + SESSION_OBJ_SERVER, + SESSION_OBJ_CHANNEL, + SESSION_OBJ_NICK, + SESSION_OBJ_DCC, + SESSION_OBJ_HISTORY, + SESSION_OBJ_BUFFER, + SESSION_OBJ_LINE, + SESSION_OBJ_UPTIME, + SESSION_OBJ_HOTLIST +}; + +enum t_session_server +{ + SESSION_SERV_END = 0, + SESSION_SERV_NAME, + SESSION_SERV_AUTOCONNECT, + SESSION_SERV_AUTORECONNECT, + SESSION_SERV_AUTORECONNECT_DELAY, + SESSION_SERV_TEMP_SERVER, + SESSION_SERV_ADDRESS, + SESSION_SERV_PORT, + SESSION_SERV_IPV6, + SESSION_SERV_SSL, + SESSION_SERV_PASSWORD, + SESSION_SERV_NICK1, + SESSION_SERV_NICK2, + SESSION_SERV_NICK3, + SESSION_SERV_USERNAME, + SESSION_SERV_REALNAME, + SESSION_SERV_COMMAND, + SESSION_SERV_COMMAND_DELAY, + SESSION_SERV_AUTOJOIN, + SESSION_SERV_AUTOREJOIN, + SESSION_SERV_NOTIFY_LEVELS, + SESSION_SERV_CHILD_PID, + SESSION_SERV_CHILD_READ, + SESSION_SERV_CHILD_WRITE, + SESSION_SERV_SOCK, + SESSION_SERV_IS_CONNECTED, + SESSION_SERV_SSL_CONNECTED, + SESSION_SERV_GNUTLS_SESS, + SESSION_SERV_UNTERMINATED_MESSAGE, + SESSION_SERV_NICK, + SESSION_SERV_RECONNECT_START, + SESSION_SERV_RECONNECT_JOIN, + SESSION_SERV_IS_AWAY, + SESSION_SERV_AWAY_TIME, + SESSION_SERV_LAG, + SESSION_SERV_LAG_CHECK_TIME, + SESSION_SERV_LAG_NEXT_CHECK, + SESSION_SERV_CHARSET_DECODE_ISO__UNUSED, + SESSION_SERV_CHARSET_DECODE_UTF__UNUSED, + SESSION_SERV_CHARSET_ENCODE__UNUSED, + SESSION_SERV_HOSTNAME, + SESSION_SERV_NICK_MODES, + SESSION_SERV_AWAY_MESSAGE, + SESSION_SERV_PREFIX, + SESSION_SERV_COMMAND_TIME +}; + +enum t_session_channel +{ + SESSION_CHAN_END = 0, + SESSION_CHAN_TYPE, + SESSION_CHAN_NAME, + SESSION_CHAN_TOPIC, + SESSION_CHAN_MODES, + SESSION_CHAN_LIMIT, + SESSION_CHAN_KEY, + SESSION_CHAN_NICKS_COUNT, + SESSION_CHAN_CHECKING_AWAY, + SESSION_CHAN_AWAY_MESSAGE, + SESSION_CHAN_CYCLE, + SESSION_CHAN_CLOSE, + SESSION_CHAN_DISPLAY_CREATION_DATE +}; + +enum t_session_nick +{ + SESSION_NICK_END = 0, + SESSION_NICK_NICK, + SESSION_NICK_FLAGS, + SESSION_NICK_COLOR, + SESSION_NICK_HOST +}; + +enum t_session_dcc +{ + SESSION_DCC_END = 0, + SESSION_DCC_SERVER, + SESSION_DCC_CHANNEL, + SESSION_DCC_TYPE, + SESSION_DCC_STATUS, + SESSION_DCC_START_TIME, + SESSION_DCC_START_TRANSFER, + SESSION_DCC_ADDR, + SESSION_DCC_PORT, + SESSION_DCC_NICK, + SESSION_DCC_SOCK, + SESSION_DCC_UNTERMINATED_MESSAGE, + SESSION_DCC_FILE, + SESSION_DCC_FILENAME, + SESSION_DCC_LOCAL_FILENAME, + SESSION_DCC_FILENAME_SUFFIX, + SESSION_DCC_SIZE, + SESSION_DCC_POS, + SESSION_DCC_ACK, + SESSION_DCC_START_RESUME, + SESSION_DCC_LAST_CHECK_TIME, + SESSION_DCC_LAST_CHECK_POS, + SESSION_DCC_LAST_ACTIVITY, + SESSION_DCC_BYTES_PER_SEC, + SESSION_DCC_ETA, + SESSION_DCC_CHILD_PID, + SESSION_DCC_CHILD_READ, + SESSION_DCC_CHILD_WRITE +}; + +enum t_session_history +{ + SESSION_HIST_END = 0, + SESSION_HIST_TEXT +}; + +enum t_session_buffer +{ + SESSION_BUFF_END = 0, + SESSION_BUFF_SERVER, + SESSION_BUFF_CHANNEL, + SESSION_BUFF_TYPE, + SESSION_BUFF_ALL_SERVERS +}; + +enum t_session_line +{ + SESSION_LINE_END = 0, + SESSION_LINE_LENGTH, + SESSION_LINE_LENGTH_ALIGN, + SESSION_LINE_LOG_WRITE, + SESSION_LINE_WITH_MESSAGE, + SESSION_LINE_WITH_HIGHLIGHT, + SESSION_LINE_DATA, + SESSION_LINE_OFS_AFTER_DATE, + SESSION_LINE_OFS_START_MESSAGE, + SESSION_LINE_NICK, + SESSION_LINE_DATE +}; + +enum t_session_uptime +{ + SESSION_UPT_END = 0, + SESSION_UPT_START_TIME +}; + +enum t_session_hotlist +{ + SESSION_HOTL_END = 0, + SESSION_HOTL_PRIORITY, + SESSION_HOTL_SERVER, + SESSION_HOTL_BUFFER_NUMBER, + SESSION_HOTL_CREATION_TIME +}; + +int session_save (char *filename); +int session_load (char *filename); + +#endif /* session.h */ diff --git a/src/core/wee-utf8.c b/src/core/wee-utf8.c new file mode 100644 index 000000000..176fee797 --- /dev/null +++ b/src/core/wee-utf8.c @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* utf8.c: UTF-8 string functions for WeeChat */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "weechat.h" +#include "utf8.h" +#include "util.h" +#include "weeconfig.h" + + +int local_utf8 = 0; + +/* + * utf8_init: initializes UTF-8 in WeeChat + */ + +void +utf8_init () +{ + local_utf8 = (ascii_strcasecmp (local_charset, "UTF-8") == 0); +} + +/* + * utf8_has_8bits: return 1 if string has 8-bits chars, 0 if only 7-bits chars + */ + +int +utf8_has_8bits (char *string) +{ + while (string && string[0]) + { + if (string[0] & 0x80) + return 1; + string++; + } + return 0; +} + +/* + * utf8_is_valid: return 1 if UTF-8 string is valid, 0 otherwise + * if error is not NULL, it's set with first non valid UTF-8 + * char in string, if any + */ + +int +utf8_is_valid (char *string, char **error) +{ + while (string && string[0]) + { + /* UTF-8, 2 bytes, should be: 110vvvvv 10vvvvvv */ + if (((unsigned char)(string[0]) & 0xE0) == 0xC0) + { + if (!string[1] || (((unsigned char)(string[1]) & 0xC0) != 0x80)) + { + if (error) + *error = string; + return 0; + } + string += 2; + } + /* UTF-8, 3 bytes, should be: 1110vvvv 10vvvvvv 10vvvvvv */ + else if (((unsigned char)(string[0]) & 0xF0) == 0xE0) + { + if (!string[1] || !string[2] + || (((unsigned char)(string[1]) & 0xC0) != 0x80) + || (((unsigned char)(string[2]) & 0xC0) != 0x80)) + { + if (error) + *error = string; + return 0; + } + string += 3; + } + /* UTF-8, 4 bytes, should be: 11110vvv 10vvvvvv 10vvvvvv 10vvvvvv */ + else if (((unsigned char)(string[0]) & 0xF8) == 0xF0) + { + if (!string[1] || !string[2] || !string[3] + || (((unsigned char)(string[1]) & 0xC0) != 0x80) + || (((unsigned char)(string[2]) & 0xC0) != 0x80) + || (((unsigned char)(string[3]) & 0xC0) != 0x80)) + { + if (error) + *error = string; + return 0; + } + string += 4; + } + /* UTF-8, 1 byte, should be: 0vvvvvvv */ + else if ((unsigned char)(string[0]) >= 0x80) + { + if (error) + *error = string; + return 0; + } + else + string++; + } + if (error) + *error = NULL; + return 1; +} + +/* + * utf8_normalize: normalize UTF-8 string: remove non UTF-8 chars and + * replace them by a char + */ + +void +utf8_normalize (char *string, char replacement) +{ + char *error; + + while (string && string[0]) + { + if (utf8_is_valid (string, &error)) + return; + error[0] = replacement; + string = error + 1; + } +} + +/* + * utf8_prev_char: return previous UTF-8 char in a string + */ + +char * +utf8_prev_char (char *string_start, char *string) +{ + if (!string || (string <= string_start)) + return NULL; + + string--; + + if (((unsigned char)(string[0]) & 0xC0) == 0x80) + { + /* UTF-8, at least 2 bytes */ + string--; + if (string < string_start) + return string + 1; + if (((unsigned char)(string[0]) & 0xC0) == 0x80) + { + /* UTF-8, at least 3 bytes */ + string--; + if (string < string_start) + return string + 1; + if (((unsigned char)(string[0]) & 0xC0) == 0x80) + { + /* UTF-8, 4 bytes */ + string--; + if (string < string_start) + return string + 1; + return string; + } + else + return string; + } + else + return string; + } + return string; +} + +/* + * utf8_next_char: return next UTF-8 char in a string + */ + +char * +utf8_next_char (char *string) +{ + if (!string) + return NULL; + + /* UTF-8, 2 bytes: 110vvvvv 10vvvvvv */ + if (((unsigned char)(string[0]) & 0xE0) == 0xC0) + { + if (!string[1]) + return string + 1; + return string + 2; + } + /* UTF-8, 3 bytes: 1110vvvv 10vvvvvv 10vvvvvv */ + else if (((unsigned char)(string[0]) & 0xF0) == 0xE0) + { + if (!string[1]) + return string + 1; + if (!string[2]) + return string + 2; + return string + 3; + } + /* UTF-8, 4 bytes: 11110vvv 10vvvvvv 10vvvvvv 10vvvvvv */ + else if (((unsigned char)(string[0]) & 0xF8) == 0xF0) + { + if (!string[1]) + return string + 1; + if (!string[2]) + return string + 2; + if (!string[3]) + return string + 3; + return string + 4; + } + /* UTF-8, 1 byte: 0vvvvvvv */ + return string + 1; +} + +/* + * utf8_char_size: return UTF-8 char size + */ + +int +utf8_char_size (char *string) +{ + if (!string) + return 0; + + return utf8_next_char (string) - string; +} + +/* + * utf8_strlen: return length of an UTF-8 string (<= strlen(string)) + */ + +int +utf8_strlen (char *string) +{ + int length; + + if (!string) + return 0; + + length = 0; + while (string && string[0]) + { + string = utf8_next_char (string); + length++; + } + return length; +} + +/* + * utf8_strnlen: return length of an UTF-8 string, for N bytes + */ + +int +utf8_strnlen (char *string, int bytes) +{ + char *start; + int length; + + if (!string) + return 0; + + start = string; + length = 0; + while (string && string[0] && (string - start < bytes)) + { + string = utf8_next_char (string); + length++; + } + return length; +} + +/* + * utf8_width_screen: return number of chars needed on screen to display UTF-8 string + */ + +int +utf8_width_screen (char *string) +{ + int length, num_char; + wchar_t *wstring; + + if (!string) + return 0; + + if (!local_utf8) + return utf8_strlen (string); + + num_char = mbstowcs (NULL, string, 0) + 1; + wstring = (wchar_t *) malloc ((num_char + 1) * sizeof (wchar_t)); + if (!wstring) + return utf8_strlen (string); + + if (mbstowcs (wstring, string, num_char) == (size_t)(-1)) + { + free (wstring); + return utf8_strlen (string); + } + + length = wcswidth (wstring, num_char); + free (wstring); + return length; +} + +/* + * utf8_add_offset: moves forward N chars in an UTF-8 string + */ + +char * +utf8_add_offset (char *string, int offset) +{ + int count; + + if (!string) + return string; + + count = 0; + while (string && string[0] && (count < offset)) + { + string = utf8_next_char (string); + count++; + } + return string; +} + +/* + * utf8_real_pos: get real position in UTF-8 + * for example: ("aébc", 2) returns 3 + */ + +int +utf8_real_pos (char *string, int pos) +{ + int count, real_pos; + char *next_char; + + if (!string) + return pos; + + count = 0; + real_pos = 0; + while (string && string[0] && (count < pos)) + { + next_char = utf8_next_char (string); + real_pos += (next_char - string); + string = next_char; + count++; + } + return real_pos; +} + +/* + * utf8_pos: get position in UTF-8 + * for example: ("aébc", 3) returns 2 + */ + +int +utf8_pos (char *string, int real_pos) +{ + int count; + char *limit; + + if (!string || !local_charset) + return real_pos; + + count = 0; + limit = string + real_pos; + while (string && string[0] && (string < limit)) + { + string = utf8_next_char (string); + count++; + } + return count; +} + +/* + * utf8_get_wc: get wide char from string (first char) + */ + +wint_t +utf8_get_wc (char *string) +{ + int char_size; + wint_t result; + + if (!string || !string[0]) + return WEOF; + + char_size = utf8_char_size (string); + switch (char_size) + { + case 1: + result = (wint_t)string[0]; + break; + case 2: + result = ((wint_t)((unsigned char)string[0])) << 8 + | ((wint_t)((unsigned char)string[1])); + break; + case 3: + result = ((wint_t)((unsigned char)string[0])) << 16 + | ((wint_t)((unsigned char)string[1])) << 8 + | ((wint_t)((unsigned char)string[2])); + break; + case 4: + result = ((wint_t)((unsigned char)string[0])) << 24 + | ((wint_t)((unsigned char)string[1])) << 16 + | ((wint_t)((unsigned char)string[2])) << 8 + | ((wint_t)((unsigned char)string[3])); + break; + default: + result = WEOF; + } + return result; +} diff --git a/src/core/wee-utf8.h b/src/core/wee-utf8.h new file mode 100644 index 000000000..caf7b7b10 --- /dev/null +++ b/src/core/wee-utf8.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_UTF8_H +#define __WEECHAT_UTF8_H 1 + +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif + +#if defined(__OpenBSD__) +#include <utf8/wchar.h> +#else +#include <wchar.h> +#endif + +extern int local_utf8; + +extern void utf8_init (); +extern int utf8_has_8bits (char *); +extern int utf8_is_valid (char *, char **); +extern void utf8_normalize (char *, char); +extern char *utf8_prev_char (char *, char *); +extern char *utf8_next_char (char *); +extern int utf8_char_size (char *); +extern int utf8_strlen (char *); +extern int utf8_strnlen (char *, int); +extern int utf8_width_screen (char *); +extern char *utf8_add_offset (char *, int); +extern int utf8_real_pos (char *, int); +extern int utf8_pos (char *, int); +extern wint_t utf8_get_wc (char *); + +#endif /* utf8.h */ diff --git a/src/core/wee-util.c b/src/core/wee-util.c new file mode 100644 index 000000000..4fe9395c5 --- /dev/null +++ b/src/core/wee-util.c @@ -0,0 +1,730 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* util.c: some useful functions for WeeChat */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> + +#ifdef HAVE_ICONV +#include <iconv.h> +#endif + +#ifndef ICONV_CONST + #ifdef ICONV_2ARG_IS_CONST + #define ICONV_CONST const + #else + #define ICONV_CONST + #endif +#endif + +#include "weechat.h" +#include "utf8.h" +#include "weeconfig.h" + + +/* + * strndup: define strndup function if not existing (FreeBSD and maybe other) + */ + +#ifndef HAVE_STRNDUP +char * +strndup (char *string, int length) +{ + char *result; + + if ((int)strlen (string) < length) + return strdup (string); + + result = (char *)malloc (length + 1); + if (!result) + return NULL; + + memcpy (result, string, length); + result[length] = '\0'; + + return result; +} +#endif + +/* + * ascii_tolower: locale independant string conversion to lower case + */ + +void +ascii_tolower (char *string) +{ + while (string && string[0]) + { + if ((string[0] >= 'A') && (string[0] <= 'Z')) + string[0] += ('a' - 'A'); + string++; + } +} + +/* + * ascii_toupper: locale independant string conversion to upper case + */ + +void +ascii_toupper (char *string) +{ + while (string && string[0]) + { + if ((string[0] >= 'a') && (string[0] <= 'z')) + string[0] -= ('a' - 'A'); + string++; + } +} + +/* + * ascii_strcasecmp: locale and case independent string comparison + */ + +int +ascii_strcasecmp (char *string1, char *string2) +{ + int c1, c2; + + if (!string1 || !string2) + return (string1) ? 1 : ((string2) ? -1 : 0); + + while (string1[0] && string2[0]) + { + c1 = (int)((unsigned char) string1[0]); + c2 = (int)((unsigned char) string2[0]); + + if ((c1 >= 'A') && (c1 <= 'Z')) + c1 += ('a' - 'A'); + + if ((c2 >= 'A') && (c2 <= 'Z')) + c2 += ('a' - 'A'); + + if ((c1 - c2) != 0) + return c1 - c2; + + string1++; + string2++; + } + + return (string1[0]) ? 1 : ((string2[0]) ? -1 : 0); +} + +/* + * ascii_strncasecmp: locale and case independent string comparison + * with max length + */ + +int +ascii_strncasecmp (char *string1, char *string2, int max) +{ + int c1, c2, count; + + if (!string1 || !string2) + return (string1) ? 1 : ((string2) ? -1 : 0); + + count = 0; + while ((count < max) && string1[0] && string2[0]) + { + c1 = (int)((unsigned char) string1[0]); + c2 = (int)((unsigned char) string2[0]); + + if ((c1 >= 'A') && (c1 <= 'Z')) + c1 += ('a' - 'A'); + + if ((c2 >= 'A') && (c2 <= 'Z')) + c2 += ('a' - 'A'); + + if ((c1 - c2) != 0) + return c1 - c2; + + string1++; + string2++; + count++; + } + + if (count >= max) + return 0; + else + return (string1[0]) ? 1 : ((string2[0]) ? -1 : 0); +} + +/* + * ascii_strcasestr: locale and case independent string search + */ + +char * +ascii_strcasestr (char *string, char *search) +{ + int length_search; + + length_search = strlen (search); + + if (!string || !search || (length_search == 0)) + return NULL; + + while (string[0]) + { + if (ascii_strncasecmp (string, search, length_search) == 0) + return string; + + string++; + } + + return NULL; +} + +/* + * weechat_iconv: convert string to another charset + */ + +char * +weechat_iconv (int from_utf8, char *from_code, char *to_code, char *string) +{ + char *outbuf; + +#ifdef HAVE_ICONV + iconv_t cd; + char *inbuf, *ptr_inbuf, *ptr_outbuf, *next_char; + char *ptr_inbuf_shift; + int done; + size_t err, inbytesleft, outbytesleft; + + if (from_code && from_code[0] && to_code && to_code[0] + && (ascii_strcasecmp(from_code, to_code) != 0)) + { + cd = iconv_open (to_code, from_code); + if (cd == (iconv_t)(-1)) + outbuf = strdup (string); + else + { + inbuf = strdup (string); + ptr_inbuf = inbuf; + inbytesleft = strlen (inbuf); + outbytesleft = inbytesleft * 4; + outbuf = (char *) malloc (outbytesleft + 2); + ptr_outbuf = outbuf; + ptr_inbuf_shift = NULL; + done = 0; + while (!done) + { + err = iconv (cd, (ICONV_CONST char **)(&ptr_inbuf), &inbytesleft, + &ptr_outbuf, &outbytesleft); + if (err == (size_t)(-1)) + { + switch (errno) + { + case EINVAL: + done = 1; + break; + case E2BIG: + done = 1; + break; + case EILSEQ: + if (from_utf8) + { + next_char = utf8_next_char (ptr_inbuf); + if (next_char) + { + inbytesleft -= next_char - ptr_inbuf; + ptr_inbuf = next_char; + } + else + { + inbytesleft--; + ptr_inbuf++; + } + } + else + { + ptr_inbuf++; + inbytesleft--; + } + ptr_outbuf[0] = '?'; + ptr_outbuf++; + outbytesleft--; + break; + } + } + else + { + if (!ptr_inbuf_shift) + { + ptr_inbuf_shift = ptr_inbuf; + ptr_inbuf = NULL; + inbytesleft = 0; + } + else + done = 1; + } + } + if (ptr_inbuf_shift) + ptr_inbuf = ptr_inbuf_shift; + ptr_outbuf[0] = '\0'; + free (inbuf); + iconv_close (cd); + } + } + else + outbuf = strdup (string); +#else + /* make C compiler happy */ + (void) from_utf8; + (void) from_code; + (void) to_code; + outbuf = strdup (string); +#endif /* HAVE_ICONV */ + + return outbuf; +} + +/* + * weechat_iconv_to_internal: convert user string (input, script, ..) to + * WeeChat internal storage charset + */ + +char * +weechat_iconv_to_internal (char *charset, char *string) +{ + char *input, *output; + + input = strdup (string); + + /* optimize for UTF-8: if charset is NULL => we use term charset => + if ths charset is already UTF-8, then no iconv needed */ + if (local_utf8 && (!charset || !charset[0])) + return input; + + if (input) + { + if (utf8_has_8bits (input) && utf8_is_valid (input, NULL)) + return input; + + output = weechat_iconv (0, + (charset && charset[0]) ? + charset : local_charset, + WEECHAT_INTERNAL_CHARSET, + input); + utf8_normalize (output, '?'); + free (input); + return output; + } + return NULL; +} + +/* + * weechat_iconv_from_internal: convert internal string to terminal charset, + * for display + */ + +char * +weechat_iconv_from_internal (char *charset, char *string) +{ + char *input, *output; + + input = strdup (string); + + /* optimize for UTF-8: if charset is NULL => we use term charset => + if ths charset is already UTF-8, then no iconv needed */ + if (local_utf8 && (!charset || !charset[0])) + return input; + + if (input) + { + utf8_normalize (input, '?'); + output = weechat_iconv (1, + WEECHAT_INTERNAL_CHARSET, + (charset && charset[0]) ? + charset : local_charset, + input); + free (input); + return output; + } + return NULL; +} + +/* + * weechat_iconv_fprintf: encode to terminal charset, then call fprintf on a file + */ + +void +weechat_iconv_fprintf (FILE *file, char *data, ...) +{ + va_list argptr; + static char buf[4096]; + char *buf2; + + va_start (argptr, data); + vsnprintf (buf, sizeof (buf) - 1, data, argptr); + va_end (argptr); + + buf2 = weechat_iconv_from_internal (NULL, buf); + fprintf (file, "%s", (buf2) ? buf2 : buf); + if (buf2) + free (buf2); +} + +/* + * weechat_strreplace: replace a string by new one in a string + * note: returned value has to be free() after use + */ + +char * +weechat_strreplace (char *string, char *search, char *replace) +{ + char *pos, *new_string; + int length1, length2, length_new, count; + + if (!string || !search || !replace) + return NULL; + + length1 = strlen (search); + length2 = strlen (replace); + + /* count number of strings to replace */ + count = 0; + pos = string; + while (pos && pos[0] && (pos = strstr (pos, search))) + { + count++; + pos += length1; + } + + /* easy: no string to replace! */ + if (count == 0) + return strdup (string); + + /* compute needed memory for new string */ + length_new = strlen (string) - (count * length1) + (count * length2) + 1; + + /* allocate new string */ + new_string = (char *)malloc (length_new * sizeof (char)); + if (!new_string) + return strdup (string); + + /* replace all occurences */ + new_string[0] = '\0'; + while (string && string[0]) + { + pos = strstr (string, search); + if (pos) + { + strncat (new_string, string, pos - string); + strcat (new_string, replace); + pos += length1; + } + else + strcat (new_string, string); + string = pos; + } + return new_string; +} + +/* + * weechat_convert_hex_chars: convert hex chars (\x??) to value + */ + +char * +weechat_convert_hex_chars (char *string) +{ + char *output, hex_str[8], *error; + int pos_output; + long number; + + output = (char *)malloc (strlen (string) + 1); + if (output) + { + pos_output = 0; + while (string && string[0]) + { + if (string[0] == '\\') + { + string++; + switch (string[0]) + { + case '\\': + output[pos_output++] = '\\'; + string++; + break; + case 'x': + case 'X': + if (isxdigit (string[1]) + && isxdigit (string[2])) + { + snprintf (hex_str, sizeof (hex_str), + "0x%c%c", string[1], string[2]); + number = strtol (hex_str, &error, 16); + if ((error) && (error[0] == '\0')) + { + output[pos_output++] = number; + string += 3; + } + else + { + output[pos_output++] = '\\'; + output[pos_output++] = string[0]; + string++; + } + } + else + { + output[pos_output++] = string[0]; + string++; + } + break; + default: + output[pos_output++] = '\\'; + output[pos_output++] = string[0]; + string++; + break; + } + } + else + { + output[pos_output++] = string[0]; + string++; + } + } + output[pos_output] = '\0'; + } + + return output; +} + +/* + * get_timeval_diff: calculates difference between two times (return in milliseconds) + */ + +long +get_timeval_diff (struct timeval *tv1, struct timeval *tv2) +{ + long diff_sec, diff_usec; + + diff_sec = tv2->tv_sec - tv1->tv_sec; + diff_usec = tv2->tv_usec - tv1->tv_usec; + + if (diff_usec < 0) + { + diff_usec += 1000000; + diff_sec--; + } + return ((diff_usec / 1000) + (diff_sec * 1000)); +} + +/* + * explode_string: explode a string according to separators + */ + +char ** +explode_string (char *string, char *separators, int num_items_max, + int *num_items) +{ + int i, n_items; + char **array; + char *ptr, *ptr1, *ptr2; + + if (num_items != NULL) + *num_items = 0; + + if (!string || !string[0]) + return NULL; + + /* calculate number of items */ + ptr = string; + i = 1; + while ((ptr = strpbrk (ptr, separators))) + { + while (strchr (separators, ptr[0]) != NULL) + ptr++; + i++; + } + n_items = i; + + if ((num_items_max != 0) && (n_items > num_items_max)) + n_items = num_items_max; + + array = + (char **) malloc ((n_items + 1) * sizeof (char *)); + + ptr1 = string; + ptr2 = string; + + for (i = 0; i < n_items; i++) + { + while (strchr (separators, ptr1[0]) != NULL) + ptr1++; + if (i == (n_items - 1) || (ptr2 = strpbrk (ptr1, separators)) == NULL) + if ((ptr2 = strchr (ptr1, '\r')) == NULL) + if ((ptr2 = strchr (ptr1, '\n')) == NULL) + ptr2 = strchr (ptr1, '\0'); + + if ((ptr1 == NULL) || (ptr2 == NULL)) + { + array[i] = NULL; + } + else + { + if (ptr2 - ptr1 > 0) + { + array[i] = + (char *) malloc ((ptr2 - ptr1 + 1) * sizeof (char)); + array[i] = strncpy (array[i], ptr1, ptr2 - ptr1); + array[i][ptr2 - ptr1] = '\0'; + ptr1 = ++ptr2; + } + else + { + array[i] = NULL; + } + } + } + + array[i] = NULL; + if (num_items != NULL) + *num_items = i; + + return array; +} + +/* + * free_exploded_string: free an exploded string + */ + +void +free_exploded_string (char **exploded_string) +{ + int i; + + if (exploded_string) + { + for (i = 0; exploded_string[i]; i++) + free (exploded_string[i]); + free (exploded_string); + } +} + +/* + * split_multi_command: split a list of commands separated by 'sep' + * and ecscaped with '\' + * - empty commands are removed + * - spaces on the left of each commands are stripped + * Result must be freed with free_multi_command + */ + +char ** +split_multi_command (char *command, char sep) +{ + int nb_substr, arr_idx, str_idx, type; + char **array; + char *buffer, *ptr, *p; + + if (command == NULL) + return NULL; + + nb_substr = 1; + ptr = command; + while ( (p = strchr(ptr, sep)) != NULL) + { + nb_substr++; + ptr = ++p; + } + + array = (char **) malloc ((nb_substr + 1) * sizeof(char *)); + if (!array) + return NULL; + + buffer = (char *) malloc ( (strlen(command) + 1) * sizeof (char)); + if (!buffer) + { + free (array); + return NULL; + } + + ptr = command; + str_idx = 0; + arr_idx = 0; + while(*ptr != '\0') + { + type = 0; + if (*ptr == ';') + { + if (ptr == command) + type = 1; + else if ( *(ptr-1) != '\\') + type = 1; + else if ( *(ptr-1) == '\\') + type = 2; + } + if (type == 1) + { + buffer[str_idx] = '\0'; + str_idx = -1; + p = buffer; + /* strip white spaces a the begining of the line */ + while (*p == ' ') p++; + if (p && p[0]) + array[arr_idx++] = strdup (p); + } + else if (type == 2) + buffer[--str_idx] = *ptr; + else + buffer[str_idx] = *ptr; + str_idx++; + ptr++; + } + + buffer[str_idx] = '\0'; + p = buffer; + while (*p == ' ') p++; + if (p && p[0]) + array[arr_idx++] = strdup (p); + + array[arr_idx] = NULL; + + free (buffer); + + array = (char **) realloc (array, (arr_idx + 1) * sizeof(char *)); + + return array; +} + +/* + * free_multi_command : free a list of commands splitted + * with split_multi_command + */ + +void +free_multi_command (char **commands) +{ + int i; + + if (commands) + { + for (i = 0; commands[i]; i++) + free (commands[i]); + free (commands); + } +} diff --git a/src/core/wee-util.h b/src/core/wee-util.h new file mode 100644 index 000000000..e3ec09e38 --- /dev/null +++ b/src/core/wee-util.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_UTIL_H +#define __WEECHAT_UTIL_H 1 + +#ifndef HAVE_STRNDUP +extern char *strndup (char *, int); +#endif +extern void ascii_tolower (char *); +extern void ascii_toupper (char *); +extern int ascii_strcasecmp (char *, char *); +extern int ascii_strncasecmp (char *, char *, int); +extern char *ascii_strcasestr (char *, char *); +extern char *weechat_iconv (char *, char *, char *); +extern char *weechat_iconv_to_internal (char *, char *); +extern char *weechat_iconv_from_internal (char *, char *); +extern void weechat_iconv_fprintf (FILE *, char *, ...); +extern char *weechat_strreplace (char *, char *, char *); +extern char *weechat_convert_hex_chars (char *); +extern long get_timeval_diff (struct timeval *, struct timeval *); +extern char **explode_string (char *, char *, int, int *); +extern void free_exploded_string (char **); + +#endif /* util.h */ diff --git a/src/core/weechat.c b/src/core/weechat.c new file mode 100644 index 000000000..60fc100e1 --- /dev/null +++ b/src/core/weechat.c @@ -0,0 +1,884 @@ +/* ############################################################################ + * ### ___ __ ______________ _____ ### + * ### __ | / /___________ ____/__ /_______ __ /_ ### + * ### __ | /| / /_ _ \ _ \ / __ __ \ __ `/ __/ ### + * ### __ |/ |/ / / __/ __/ /___ _ / / / /_/ // /_ ### + * ### ____/|__/ \___/\___/\____/ /_/ /_/\__,_/ \__/ ### + * ### ### + * ### WeeChat - Wee Enhanced Environment for Chat ### + * ### Fast & light environment for Chat ### + * ### ### + * ### By FlashCode <flashcode@flashtux.org> ### + * ### ### + * ### http://weechat.flashtux.org ### + * ### ### + * ############################################################################ + * + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* weechat.c: core functions for WeeChat */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <time.h> +#include <signal.h> + +#ifdef HAVE_GNUTLS +#include <gnutls/gnutls.h> +#endif + +#ifdef HAVE_LANGINFO_CODESET +#include <langinfo.h> +#endif + +#include "weechat.h" +#include "alias.h" +#include "backtrace.h" +#include "command.h" +#include "fifo.h" +#include "hotlist.h" +#include "log.h" +#include "session.h" +#include "utf8.h" +#include "util.h" +#include "weeconfig.h" +#include "../protocols/irc/irc.h" +#include "../gui/gui.h" + +#ifdef PLUGINS +#include "../plugins/plugins.h" +#endif + + +char *weechat_argv0 = NULL; /* WeeChat binary file name (argv[0]) */ +char *weechat_session = NULL; /* WeeChat session file (for /upgrade cmd) */ +time_t weechat_start_time; /* WeeChat start time (used by /uptime cmd) */ +int quit_weechat; /* = 1 if quit request from user... why ? :'( */ +int sigsegv = 0; /* SIGSEGV received? */ +char *weechat_home = NULL; /* WeeChat home dir. (default: ~/.weechat) */ + +char *local_charset = NULL; /* local charset, for ex.: ISO-8859-1, UTF-8 */ + +int server_cmd_line; /* at least 1 server on WeeChat command line */ +int auto_connect; /* enabled by default (cmd option to disable) */ +int auto_load_plugins; /* enabled by default (cmd option to disable) */ + +#ifdef HAVE_GNUTLS +gnutls_certificate_credentials gnutls_xcred; /* gnutls client credentials */ +#endif + + +/* + * weechat_display_usage: display WeeChat usage + */ + +void +weechat_display_usage (char *exec_name) +{ + weechat_iconv_fprintf (stdout, "\n"); + weechat_iconv_fprintf (stdout, + _("%s (c) Copyright 2003-2007, compiled on %s %s\n" + "Developed by FlashCode <flashcode@flashtux.org> - %s"), + PACKAGE_STRING, __DATE__, __TIME__, WEECHAT_WEBSITE); + weechat_iconv_fprintf (stdout, "\n\n"); + weechat_iconv_fprintf (stdout, + _("Usage: %s [options ...]\n" \ + " or: %s [irc[6][s]://[nickname[:password]@]irc.example.org[:port][/channel][,channel[...]]"), + exec_name, exec_name); + weechat_iconv_fprintf (stdout, "\n\n"); + weechat_iconv_fprintf (stdout, + _(" -a, --no-connect disable auto-connect to servers at startup\n" + " -c, --config display config file options\n" + " -d, --dir <path> set WeeChat home directory (default: ~/.weechat)\n" + " -f, --key-functions display WeeChat internal functions for keys\n" + " -h, --help this help\n" + " -i, --irc-commands display IRC commands\n" + " -k, --keys display WeeChat default keys\n" + " -l, --license display WeeChat license\n" + " -p, --no-plugin don't load any plugin at startup\n" + " -v, --version display WeeChat version\n" + " -w, --weechat-commands display WeeChat commands\n")); + weechat_iconv_fprintf(stdout, "\n"); +} + +/* + * weechat_display_config_options: display config options + */ + +void +weechat_display_config_options () +{ + int i, j, k; + + weechat_iconv_fprintf (stdout, + _("WeeChat configuration options (<weechat_home>/weechat.rc):\n\n")); + for (i = 0; i < CONFIG_NUMBER_SECTIONS; i++) + { + if (weechat_options[i]) + { + j = 0; + while (weechat_options[i][j].option_name) + { + weechat_iconv_fprintf (stdout, + "* %s:\n", + weechat_options[i][j].option_name); + switch (weechat_options[i][j].option_type) + { + case OPTION_TYPE_BOOLEAN: + weechat_iconv_fprintf (stdout, _(" . type: boolean\n")); + weechat_iconv_fprintf (stdout, _(" . values: 'on' or 'off'\n")); + weechat_iconv_fprintf (stdout, _(" . default value: '%s'\n"), + (weechat_options[i][j].default_int == BOOL_TRUE) ? + "on" : "off"); + break; + case OPTION_TYPE_INT: + weechat_iconv_fprintf (stdout, _(" . type: integer\n")); + weechat_iconv_fprintf (stdout, _(" . values: between %d and %d\n"), + weechat_options[i][j].min, + weechat_options[i][j].max); + weechat_iconv_fprintf (stdout, _(" . default value: %d\n"), + weechat_options[i][j].default_int); + break; + case OPTION_TYPE_INT_WITH_STRING: + weechat_iconv_fprintf (stdout, _(" . type: string\n")); + weechat_iconv_fprintf (stdout, _(" . values: ")); + k = 0; + while (weechat_options[i][j].array_values[k]) + { + weechat_iconv_fprintf (stdout, "'%s'", + weechat_options[i][j].array_values[k]); + if (weechat_options[i][j].array_values[k + 1]) + weechat_iconv_fprintf (stdout, ", "); + k++; + } + weechat_iconv_fprintf (stdout, "\n"); + weechat_iconv_fprintf (stdout, _(" . default value: '%s'\n"), + (weechat_options[i][j].default_string) ? + weechat_options[i][j].default_string : _("empty")); + break; + case OPTION_TYPE_COLOR: + weechat_iconv_fprintf (stdout, _(" . type: color\n")); + weechat_iconv_fprintf (stdout, _(" . values: Curses or Gtk color\n")); + weechat_iconv_fprintf (stdout, _(" . default value: '%s'\n"), + (weechat_options[i][j].default_string) ? + weechat_options[i][j].default_string : _("empty")); + break; + case OPTION_TYPE_STRING: + switch (weechat_options[i][j].max) + { + case 0: + weechat_iconv_fprintf (stdout, _(" . type: string\n")); + weechat_iconv_fprintf (stdout, _(" . values: any string\n")); + break; + case 1: + weechat_iconv_fprintf (stdout, _(" . type: char\n")); + weechat_iconv_fprintf (stdout, _(" . values: any char\n")); + break; + default: + weechat_iconv_fprintf (stdout, _(" . type: string\n")); + weechat_iconv_fprintf (stdout, _(" . values: any string (limit: %d chars)\n"), + weechat_options[i][j].max); + break; + } + weechat_iconv_fprintf (stdout, _(" . default value: '%s'\n"), + (weechat_options[i][j].default_string) ? + weechat_options[i][j].default_string : _("empty")); + break; + } + weechat_iconv_fprintf (stdout, _(" . description: %s\n"), + _(weechat_options[i][j].long_description)); + weechat_iconv_fprintf (stdout, "\n"); + j++; + } + } + } +} + +/* + * weechat_display_commands: display WeeChat and/or IRC commands + */ + +void +weechat_display_commands (int weechat_cmd, int irc_cmd) +{ + int i; + + if (weechat_cmd) + { + weechat_iconv_fprintf (stdout, + _("%s internal commands:\n"), PACKAGE_NAME); + weechat_iconv_fprintf (stdout, "\n"); + for (i = 0; weechat_commands[i].command_name; i++) + { + weechat_iconv_fprintf (stdout, "* %s", weechat_commands[i].command_name); + if (weechat_commands[i].arguments && + weechat_commands[i].arguments[0]) + weechat_iconv_fprintf (stdout, " %s\n\n", _(weechat_commands[i].arguments)); + else + weechat_iconv_fprintf (stdout, "\n\n"); + weechat_iconv_fprintf (stdout, "%s\n\n", _(weechat_commands[i].command_description)); + if (weechat_commands[i].arguments_description && + weechat_commands[i].arguments_description[0]) + weechat_iconv_fprintf (stdout, "%s\n\n", + _(weechat_commands[i].arguments_description)); + } + } + + if (irc_cmd) + { + weechat_iconv_fprintf (stdout, _("IRC commands:\n")); + weechat_iconv_fprintf (stdout, "\n"); + for (i = 0; irc_commands[i].command_name; i++) + { + if (irc_commands[i].cmd_function_args || + irc_commands[i].cmd_function_1arg) + { + weechat_iconv_fprintf (stdout, "* %s", irc_commands[i].command_name); + if (irc_commands[i].arguments && + irc_commands[i].arguments[0]) + weechat_iconv_fprintf (stdout, " %s\n\n", _(irc_commands[i].arguments)); + else + weechat_iconv_fprintf (stdout, "\n\n"); + weechat_iconv_fprintf (stdout, "%s\n\n", _(irc_commands[i].command_description)); + if (irc_commands[i].arguments_description && + irc_commands[i].arguments_description[0]) + weechat_iconv_fprintf (stdout, "%s\n\n", + _(irc_commands[i].arguments_description)); + } + } + } +} + +/* + * weechat_display_key_functions: display WeeChat key functions + */ + +void +weechat_display_key_functions () +{ + int i; + + weechat_iconv_fprintf (stdout, _("Internal key functions:\n")); + weechat_iconv_fprintf (stdout, "\n"); + i = 0; + while (gui_key_functions[i].function_name) + { + weechat_iconv_fprintf (stdout, + "* %s: %s\n", + gui_key_functions[i].function_name, + _(gui_key_functions[i].description)); + i++; + } +} + +/* + * weechat_display_keys: display WeeChat default keys + */ + +void +weechat_display_keys () +{ + t_gui_key *ptr_key; + char *expanded_name; + + weechat_iconv_fprintf (stdout, + _("%s default keys:\n"), PACKAGE_NAME); + weechat_iconv_fprintf (stdout, "\n"); + for (ptr_key = gui_keys; ptr_key; ptr_key = ptr_key->next_key) + { + expanded_name = gui_keyboard_get_expanded_name (ptr_key->key); + weechat_iconv_fprintf (stdout, + "* %s => %s\n", + (expanded_name) ? expanded_name : ptr_key->key, + (ptr_key->function) ? gui_keyboard_function_search_by_ptr (ptr_key->function) : ptr_key->command); + if (expanded_name) + free (expanded_name); + } +} + +/* + * weechat_parse_args: parse command line args + */ + +void +weechat_parse_args (int argc, char *argv[]) +{ + int i; + t_irc_server server_tmp; + + weechat_argv0 = strdup (argv[0]); + weechat_session = NULL; + weechat_home = NULL; + server_cmd_line = 0; + auto_connect = 1; + auto_load_plugins = 1; + + for (i = 1; i < argc; i++) + { + if ((strcmp (argv[i], "-a") == 0) + || (strcmp (argv[i], "--no-connect") == 0)) + auto_connect = 0; + else if ((strcmp (argv[i], "-c") == 0) + || (strcmp (argv[i], "--config") == 0)) + { + weechat_display_config_options (); + weechat_shutdown (EXIT_SUCCESS, 0); + } + else if ((strcmp (argv[i], "-d") == 0) + || (strcmp (argv[i], "--dir") == 0)) + { + if (i + 1 < argc) + weechat_home = strdup (argv[++i]); + else + { + weechat_iconv_fprintf (stderr, + _("%s missing argument for --dir option\n"), + WEECHAT_ERROR); + weechat_shutdown (EXIT_FAILURE, 0); + } + } + else if ((strcmp (argv[i], "-f") == 0) + || (strcmp (argv[i], "--key-functions") == 0)) + { + weechat_display_key_functions (); + weechat_shutdown (EXIT_SUCCESS, 0); + } + else if ((strcmp (argv[i], "-h") == 0) + || (strcmp (argv[i], "--help") == 0)) + { + weechat_display_usage (argv[0]); + weechat_shutdown (EXIT_SUCCESS, 0); + } + else if ((strcmp (argv[i], "-i") == 0) + || (strcmp (argv[i], "--irc-commands") == 0)) + { + weechat_display_commands (0, 1); + weechat_shutdown (EXIT_SUCCESS, 0); + } + else if ((strcmp (argv[i], "-k") == 0) + || (strcmp (argv[i], "--keys") == 0)) + { + weechat_display_keys (); + weechat_shutdown (EXIT_SUCCESS, 0); + } + else if ((strcmp (argv[i], "-l") == 0) + || (strcmp (argv[i], "--license") == 0)) + { + weechat_iconv_fprintf (stdout, "\n%s%s", WEE_LICENSE); + weechat_shutdown (EXIT_SUCCESS, 0); + } + else if ((strcmp (argv[i], "-p") == 0) + || (strcmp (argv[i], "--no-plugin") == 0)) + auto_load_plugins = 0; + else if (strcmp (argv[i], "--session") == 0) + { + if (i + 1 < argc) + weechat_session = strdup (argv[++i]); + else + { + weechat_iconv_fprintf (stderr, + _("%s missing argument for --session option\n"), + WEECHAT_ERROR); + weechat_shutdown (EXIT_FAILURE, 0); + } + } + else if ((strcmp (argv[i], "-v") == 0) + || (strcmp (argv[i], "--version") == 0)) + { + weechat_iconv_fprintf (stdout, PACKAGE_VERSION "\n"); + weechat_shutdown (EXIT_SUCCESS, 0); + } + else if ((strcmp (argv[i], "-w") == 0) + || (strcmp (argv[i], "--weechat-commands") == 0)) + { + weechat_display_commands (1, 0); + weechat_shutdown (EXIT_SUCCESS, 0); + } + else if ((ascii_strncasecmp (argv[i], "irc", 3) == 0)) + { + if (irc_server_init_with_url (argv[i], &server_tmp) < 0) + { + weechat_iconv_fprintf (stderr, + _("%s invalid syntax for IRC server ('%s'), ignored\n"), + WEECHAT_WARNING, argv[i]); + } + else + { + if (!irc_server_new (server_tmp.name, server_tmp.autoconnect, + server_tmp.autoreconnect, + server_tmp.autoreconnect_delay, + 1, server_tmp.address, server_tmp.port, + server_tmp.ipv6, server_tmp.ssl, + server_tmp.password, server_tmp.nick1, + server_tmp.nick2, server_tmp.nick3, + NULL, NULL, NULL, NULL, 0, + server_tmp.autojoin, 1, NULL)) + weechat_iconv_fprintf (stderr, + _("%s unable to create server ('%s'), ignored\n"), + WEECHAT_WARNING, argv[i]); + irc_server_destroy (&server_tmp); + server_cmd_line = 1; + } + } + else + { + weechat_iconv_fprintf (stderr, + _("%s unknown parameter '%s', ignored\n"), + WEECHAT_WARNING, argv[i]); + } + } +} + +/* + * weechat_create_dir: create a directory + * return: 1 if ok (or directory already exists) + * 0 if error + */ + +int +weechat_create_dir (char *directory, int permissions) +{ + if (mkdir (directory, 0755) < 0) + { + /* exit if error (except if directory already exists) */ + if (errno != EEXIST) + { + weechat_iconv_fprintf (stderr, _("%s cannot create directory \"%s\"\n"), + WEECHAT_ERROR, directory); + return 0; + } + return 1; + } + if ((permissions != 0) && (strcmp (directory, getenv ("HOME")) != 0)) + chmod (directory, permissions); + return 1; +} + +/* + * weechat_create_home_dirs: create WeeChat directories + */ + +void +weechat_create_home_dirs () +{ + char *ptr_home; + int dir_length; + struct stat statinfo; + + if (!weechat_home) + { + ptr_home = getenv ("HOME"); + if (!ptr_home) + { + weechat_iconv_fprintf (stderr, _("%s unable to get HOME directory\n"), + WEECHAT_ERROR); + weechat_shutdown (EXIT_FAILURE, 0); + } + dir_length = strlen (ptr_home) + 10; + weechat_home = + (char *) malloc (dir_length * sizeof (char)); + if (!weechat_home) + { + weechat_iconv_fprintf (stderr, _("%s not enough memory for home directory\n"), + WEECHAT_ERROR); + weechat_shutdown (EXIT_FAILURE, 0); + } + snprintf (weechat_home, dir_length, "%s%s.weechat", ptr_home, + DIR_SEPARATOR); + } + + /* if home already exists, it has to be a directory */ + if (stat (weechat_home, &statinfo) == 0) + { + if (!S_ISDIR (statinfo.st_mode)) + { + weechat_iconv_fprintf (stderr, _("%s home (%s) is not a directory\n"), + WEECHAT_ERROR, weechat_home); + weechat_shutdown (EXIT_FAILURE, 0); + } + } + + /* create home directory; error is fatal */ + if (!weechat_create_dir (weechat_home, 0)) + { + weechat_iconv_fprintf (stderr, _("%s unable to create \"%s\" directory\n"), + WEECHAT_ERROR, weechat_home); + weechat_shutdown (EXIT_FAILURE, 0); + } +} + +/* + * weechat_create_config_dirs: create config directories (read from config file) + */ + +void +weechat_create_config_dirs () +{ + char *dir1, *dir2; + + /* create logs directory" */ + dir1 = weechat_strreplace (cfg_log_path, "~", getenv ("HOME")); + dir2 = weechat_strreplace (dir1, "%h", weechat_home); + (void) weechat_create_dir (dir2, 0700); + if (dir1) + free (dir1); + if (dir2) + free (dir2); + + /* create DCC download directory */ + dir1 = weechat_strreplace (cfg_dcc_download_path, "~", getenv ("HOME")); + dir2 = weechat_strreplace (dir1, "%h", weechat_home); + (void) weechat_create_dir (dir2, 0700); + if (dir1) + free (dir1); + if (dir2) + free (dir2); +} + +/* + * weechat_init_vars: initialize some variables + */ + +void +weechat_init_vars () +{ + /* start time, used by /uptime command */ + weechat_start_time = time (NULL); + + /* init gnutls */ +#ifdef HAVE_GNUTLS + gnutls_global_init (); + gnutls_certificate_allocate_credentials (&gnutls_xcred); + gnutls_certificate_set_x509_trust_file (gnutls_xcred, "ca.pem", GNUTLS_X509_FMT_PEM); +#endif +} + +/* + * weechat_config_read: read WeeChat config file + */ + +void +weechat_config_read () +{ + switch (config_read ()) + { + case 0: /* read ok */ + break; + case -1: /* config file not found */ + if (config_create_default () < 0) + exit (EXIT_FAILURE); + if (config_read () != 0) + exit (EXIT_FAILURE); + break; + default: /* other error (fatal) */ + irc_server_free_all (); + exit (EXIT_FAILURE); + } +} + +/* + * weechat_welcome_message: display WeeChat welcome message - yeah! + */ + +void +weechat_welcome_message () +{ + if (cfg_look_startup_logo) + { + gui_printf (NULL, + "%s ___ __ ______________ _____ \n" + "%s __ | / /___________ ____/__ /_______ __ /_\n" + "%s __ | /| / /_ _ \\ _ \\ / __ __ \\ __ `/ __/\n" + "%s __ |/ |/ / / __/ __/ /___ _ / / / /_/ // /_ \n" + "%s ____/|__/ \\___/\\___/\\____/ /_/ /_/\\__,_/ \\__/ \n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK), + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK)); + } + if (cfg_look_weechat_slogan && cfg_look_weechat_slogan[0]) + { + gui_printf (NULL, _("%sWelcome to %s%s%s, %s\n"), + (cfg_look_startup_logo) ? " " : "", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + PACKAGE_NAME, + GUI_NO_COLOR, + cfg_look_weechat_slogan); + } + if (cfg_look_startup_version) + { + gui_printf (NULL, "%s%s%s%s, %s %s %s\n", + (cfg_look_startup_logo) ? " " : "", + GUI_COLOR(GUI_COLOR_WIN_CHAT_CHANNEL), + PACKAGE_STRING, + GUI_NO_COLOR, + _("compiled on"), __DATE__, __TIME__); + } + if (cfg_look_startup_logo || + (cfg_look_weechat_slogan && cfg_look_weechat_slogan[0]) || + cfg_look_startup_version) + gui_printf (NULL, + "%s-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n", + GUI_COLOR(GUI_COLOR_WIN_CHAT_NICK)); + + weechat_log_printf ("%s (%s %s %s)\n", + PACKAGE_STRING, _("compiled on"), __DATE__, __TIME__); +} + +/* + * weechat_shutdown: shutdown WeeChat + */ + +void +weechat_shutdown (int return_code, int crash) +{ + if (weechat_argv0) + free (weechat_argv0); + fifo_remove (); + if (weechat_home) + free (weechat_home); + weechat_log_close (); + if (local_charset) + free (local_charset); + alias_free_all (); + +#ifdef HAVE_GNUTLS + gnutls_certificate_free_credentials (gnutls_xcred); + gnutls_global_deinit(); +#endif + + if (crash) + abort(); + else + exit (return_code); +} + +/* + * weechat_dump: write dump to WeeChat log file + */ + +void +weechat_dump (int crash) +{ + t_irc_server *ptr_server; + t_irc_channel *ptr_channel; + t_irc_nick *ptr_nick; + t_gui_window *ptr_window; + t_gui_buffer *ptr_buffer; +#ifdef PLUGINS + t_weechat_plugin *ptr_plugin; +#endif + + /* prevent reentrance */ + if (sigsegv) + exit (EXIT_FAILURE); + + if (crash) + { + sigsegv = 1; + weechat_log_printf ("Very bad, WeeChat is crashing (SIGSEGV received)...\n"); + } + + weechat_log_printf ("\n"); + if (crash) + { + weechat_log_printf ("****** WeeChat CRASH DUMP ******\n"); + weechat_log_printf ("****** Please send this file to WeeChat developers ******\n"); + weechat_log_printf ("****** and explain when this crash happened ******\n"); + } + else + { + weechat_log_printf ("****** WeeChat dump request ******\n"); + } + + for (ptr_server = irc_servers; ptr_server; ptr_server = ptr_server->next_server) + { + weechat_log_printf ("\n"); + irc_server_print_log (ptr_server); + + for (ptr_channel = ptr_server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + weechat_log_printf ("\n"); + irc_channel_print_log (ptr_channel); + + for (ptr_nick = ptr_channel->nicks; ptr_nick; + ptr_nick = ptr_nick->next_nick) + { + weechat_log_printf ("\n"); + irc_nick_print_log (ptr_nick); + } + + } + } + + irc_dcc_print_log (); + + gui_panel_print_log (); + + weechat_log_printf ("\n"); + weechat_log_printf ("[windows/buffers]\n"); + weechat_log_printf (" => windows:\n"); + for (ptr_window = gui_windows; ptr_window; ptr_window = ptr_window->next_window) + { + weechat_log_printf (" 0x%X\n", ptr_window); + } + weechat_log_printf (" => buffers:\n"); + for (ptr_buffer = gui_buffers; ptr_buffer; ptr_buffer = ptr_buffer->next_buffer) + { + weechat_log_printf (" 0x%X\n", ptr_buffer); + } + weechat_log_printf (" => current window = 0x%X\n", gui_current_window); + + for (ptr_window = gui_windows; ptr_window; ptr_window = ptr_window->next_window) + { + weechat_log_printf ("\n"); + gui_window_print_log (ptr_window); + } + + for (ptr_buffer = gui_buffers; ptr_buffer; + ptr_buffer = ptr_buffer->next_buffer) + { + weechat_log_printf ("\n"); + gui_buffer_print_log (ptr_buffer); + } + + weechat_log_printf ("\n"); + irc_ignore_print_log (); + + weechat_log_printf ("\n"); + hotlist_print_log (); + +#ifdef PLUGINS + for (ptr_plugin = weechat_plugins; ptr_plugin; + ptr_plugin = ptr_plugin->next_plugin) + { + weechat_log_printf ("\n"); + plugin_print_log (ptr_plugin); + } +#endif + + weechat_log_printf ("\n"); + weechat_log_printf ("****** End of dump ******\n"); + weechat_log_printf ("\n"); +} + +/* + * weechat_sigsegv: SIGSEGV handler: save crash log to <weechat_home>/weechat.log and exit + */ + +void +weechat_sigsegv () +{ + weechat_dump (1); + irc_dcc_end (); + irc_server_free_all (); + gui_main_end (); + + weechat_iconv_fprintf (stderr, "\n"); + weechat_iconv_fprintf (stderr, "*** Very bad! WeeChat is crashing (SIGSEGV received)\n"); + if (!weechat_log_crash_rename ()) + weechat_iconv_fprintf (stderr, + "*** Full crash dump was saved to %s/weechat.log file.\n", + weechat_home); + weechat_iconv_fprintf (stderr, "***\n"); + weechat_iconv_fprintf (stderr, "*** Please help WeeChat developers to fix this bug:\n"); + weechat_iconv_fprintf (stderr, "*** 1. If you have a core file, please run: gdb weechat-curses core\n"); + weechat_iconv_fprintf (stderr, "*** then issue \"bt\" command and send result to developers\n"); + weechat_iconv_fprintf (stderr, "*** To enable core files with bash shell: ulimit -c 10000\n"); + weechat_iconv_fprintf (stderr, "*** 2. Otherwise send backtrace (below) and weechat.log\n"); + weechat_iconv_fprintf (stderr, "*** (be careful, private info may be in this file since\n"); + weechat_iconv_fprintf (stderr, "*** part of chats are displayed, so remove lines if needed)\n\n"); + + weechat_backtrace (); + + /* shutdown with error code */ + weechat_shutdown (EXIT_FAILURE, 1); +} + +/* + * main: WeeChat startup + */ + +int +main (int argc, char *argv[]) +{ + setlocale (LC_ALL, ""); /* initialize gettext */ +#ifdef ENABLE_NLS + bindtextdomain (PACKAGE, LOCALEDIR); + bind_textdomain_codeset (PACKAGE, "UTF-8"); + textdomain (PACKAGE); +#endif + +#ifdef HAVE_LANGINFO_CODESET + local_charset = strdup (nl_langinfo (CODESET)); +#else + local_charset = strdup (""); +#endif + utf8_init (); + + signal (SIGINT, SIG_IGN); /* ignore SIGINT signal */ + signal (SIGQUIT, SIG_IGN); /* ignore SIGQUIT signal */ + signal (SIGPIPE, SIG_IGN); /* ignore SIGPIPE signal */ + signal (SIGSEGV, weechat_sigsegv); /* crash dump when SIGSEGV received */ + gui_main_pre_init (&argc, &argv); /* pre-initiliaze interface */ + weechat_init_vars (); /* initialize some variables */ + gui_keyboard_init (); /* init keyb. (default key bindings)*/ + weechat_parse_args (argc, argv); /* parse command line args */ + weechat_create_home_dirs (); /* create WeeChat directories */ + weechat_log_init (); /* init log file */ + command_index_build (); /* build cmd index for completion */ + weechat_config_read (); /* read configuration */ + weechat_create_config_dirs (); /* create config directories */ + gui_main_init (); /* init WeeChat interface */ + fifo_create (); /* FIFO pipe for remote control */ + if (weechat_session) + session_load (weechat_session); /* load previous session if asked */ + weechat_welcome_message (); /* display WeeChat welcome message */ +#ifdef PLUGINS + plugin_init (auto_load_plugins); /* init plugin interface(s) */ +#endif + + irc_server_auto_connect (auto_connect, /* auto-connect to servers */ + server_cmd_line); + + gui_main_loop (); /* WeeChat main loop */ + +#ifdef PLUGINS + plugin_end (); /* end plugin interface(s) */ +#endif + irc_server_disconnect_all (); /* disconnect from all servers */ + if (cfg_look_save_on_exit) + (void) config_write (NULL); /* save config file */ + command_index_free (); /* free commands index */ + irc_dcc_end (); /* remove all DCC */ + irc_server_free_all (); /* free all servers */ + gui_main_end (); /* shut down WeeChat GUI */ + weechat_shutdown (EXIT_SUCCESS, 0); /* quit WeeChat (oh no, why?) */ + + return EXIT_SUCCESS; /* make C compiler happy */ +} diff --git a/src/core/weechat.h b/src/core/weechat.h new file mode 100644 index 000000000..34e95a50e --- /dev/null +++ b/src/core/weechat.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2003-2007 by FlashCode <flashcode@flashtux.org> + * See README for License detail, AUTHORS for developers list. + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef __WEECHAT_H +#define __WEECHAT_H 1 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <sys/time.h> + +#ifdef HAVE_GNUTLS +#include <gnutls/gnutls.h> +#endif + +#include <locale.h> + +#if defined(ENABLE_NLS) && !defined(_) + #ifdef HAVE_LIBINTL_H + #include <libintl.h> + #else + #include "../../intl/libintl.h" + #endif + #define _(string) gettext(string) + #define NG_(single,plural,number) ngettext(single,plural,number) + #ifdef gettext_noop + #define N_(string) gettext_noop(string) + #else + #define N_(string) (string) + #endif +#endif +#if !defined(_) + #define _(string) (string) + #define NG_(single,plural,number) (plural) + #define N_(string) (string) +#endif + + +#define WEECHAT_COPYRIGHT_DATE "(c) 2003-2007" +#define WEECHAT_WEBSITE "http://weechat.flashtux.org" + +#define WEECHAT_ERROR _("WeeChat Error:") +#define WEECHAT_WARNING _("WeeChat Warning:") + +/* log file */ + +#define WEECHAT_LOG_NAME "weechat.log" + +/* license */ + +#define WEE_LICENSE \ + PACKAGE_STRING " (c) Copyright 2003-2007, compiled on " __DATE__ " " __TIME__ \ + "\nDeveloped by FlashCode <flashcode@flashtux.org> - " WEECHAT_WEBSITE "\n\n" \ + "This program is free software; you can redistribute it and/or modify\n" \ + "it under the terms of the GNU General Public License as published by\n" \ + "the Free Software Foundation; either version 3 of the License, or\n" \ + "(at your option) any later version.\n" \ + "\n", \ + \ + "This program is distributed in the hope that it will be useful,\n" \ + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \ + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" \ + "GNU General Public License for more details.\n" \ + "\n" \ + "You should have received a copy of the GNU General Public License\n" \ + "along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n" + +/* directory separator, depending on OS */ + +#ifdef _WIN32 + #define DIR_SEPARATOR "\\" + #define DIR_SEPARATOR_CHAR '\\' +#else + #define DIR_SEPARATOR "/" + #define DIR_SEPARATOR_CHAR '/' +#endif + +/* some systems (like GNU/Hurd) doesn't define PATH_MAX */ + +#ifndef PATH_MAX + #define PATH_MAX 4096 +#endif + +/* internal charset */ + +#define WEECHAT_INTERNAL_CHARSET "UTF-8" + +/* global variables and functions */ + +extern char *weechat_argv0; +extern time_t weechat_start_time; +extern int quit_weechat; +extern char *weechat_home; +extern char *local_charset; + +#ifdef HAVE_GNUTLS +extern gnutls_certificate_credentials gnutls_xcred; +#endif + +extern void weechat_dump (int); +extern void weechat_shutdown (int, int); + +#endif /* weechat.h */ |