diff options
Diffstat (limited to 'src/plugins/script')
-rw-r--r-- | src/plugins/script/CMakeLists.txt | 43 | ||||
-rw-r--r-- | src/plugins/script/Makefile.am | 46 | ||||
-rw-r--r-- | src/plugins/script/script-action.c | 710 | ||||
-rw-r--r-- | src/plugins/script/script-action.h | 29 | ||||
-rw-r--r-- | src/plugins/script/script-buffer.c | 797 | ||||
-rw-r--r-- | src/plugins/script/script-buffer.h | 44 | ||||
-rw-r--r-- | src/plugins/script/script-command.c | 270 | ||||
-rw-r--r-- | src/plugins/script/script-command.h | 25 | ||||
-rw-r--r-- | src/plugins/script/script-completion.c | 192 | ||||
-rw-r--r-- | src/plugins/script/script-completion.h | 25 | ||||
-rw-r--r-- | src/plugins/script/script-config.c | 587 | ||||
-rw-r--r-- | src/plugins/script/script-config.h | 71 | ||||
-rw-r--r-- | src/plugins/script/script-info.c | 110 | ||||
-rw-r--r-- | src/plugins/script/script-info.h | 25 | ||||
-rw-r--r-- | src/plugins/script/script-repo.c | 1369 | ||||
-rw-r--r-- | src/plugins/script/script-repo.h | 83 | ||||
-rw-r--r-- | src/plugins/script/script.c | 292 | ||||
-rw-r--r-- | src/plugins/script/script.h | 37 |
18 files changed, 4755 insertions, 0 deletions
diff --git a/src/plugins/script/CMakeLists.txt b/src/plugins/script/CMakeLists.txt new file mode 100644 index 000000000..e1d8b5570 --- /dev/null +++ b/src/plugins/script/CMakeLists.txt @@ -0,0 +1,43 @@ +# +# Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> +# +# This file is part of WeeChat, the extensible chat client. +# +# WeeChat 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. +# +# WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. +# + +ADD_LIBRARY(script MODULE +script.c script.h +script-action.c script-action.h +script-buffer.c script-buffer.h +script-command.c script-command.h +script-completion.c script-completion.h +script-config.c script-config.h +script-info.c script-info.h +script-repo.c script-repo.h) +SET_TARGET_PROPERTIES(script PROPERTIES PREFIX "") + +SET (LINK_LIBS) + +IF(ZLIB_FOUND) + LIST(APPEND LINK_LIBS ${ZLIB_LIBRARY}) +ENDIF(ZLIB_FOUND) + +IF(GCRYPT_FOUND) + LIST(APPEND LINK_LIBS gcrypt) +ENDIF(GCRYPT_FOUND) + +TARGET_LINK_LIBRARIES(script ${LINK_LIBS}) + +INSTALL(TARGETS script LIBRARY DESTINATION ${LIBDIR}/plugins) diff --git a/src/plugins/script/Makefile.am b/src/plugins/script/Makefile.am new file mode 100644 index 000000000..0f07f3dfe --- /dev/null +++ b/src/plugins/script/Makefile.am @@ -0,0 +1,46 @@ +# +# Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> +# +# This file is part of WeeChat, the extensible chat client. +# +# WeeChat 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. +# +# WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. +# + +INCLUDES = -DLOCALEDIR=\"$(datadir)/locale\" $(ZLIB_CFLAGS) $(GCRYPT_CFLAGS) + +libdir = ${weechat_libdir}/plugins + +lib_LTLIBRARIES = script.la + +script_la_SOURCES = script.c \ + script.h \ + script-action.c \ + script-action.h \ + script-buffer.c \ + script-buffer.h \ + script-command.c \ + script-command.h \ + script-completion.c \ + script-completion.h \ + script-config.c \ + script-config.h \ + script-info.c \ + script-info.h \ + script-repo.c \ + script-repo.h + +script_la_LDFLAGS = -module +script_la_LIBADD = $(SCRIPT_LFLAGS) $(ZLIB_LFLAGS) $(GCRYPT_LFLAGS) + +EXTRA_DIST = CMakeLists.txt diff --git a/src/plugins/script/script-action.c b/src/plugins/script/script-action.c new file mode 100644 index 000000000..aabb8e504 --- /dev/null +++ b/src/plugins/script/script-action.c @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * script-action.c: actions on scripts (load/unload, install/remove, hold, ...) + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <libgen.h> + +#include "../weechat-plugin.h" +#include "script.h" +#include "script-action.h" +#include "script-buffer.h" +#include "script-config.h" +#include "script-repo.h" + + +char *script_actions = NULL; + + +void script_action_install (); + + +/* + * script_action_list: list loaded scripts (all languages) + */ + +void +script_action_list () +{ + int i, scripts_loaded; + char hdata_name[128]; + const char *ptr_name; + struct t_hdata *hdata; + void *ptr_script; + + weechat_printf (NULL, ""); + weechat_printf (NULL, _("Scripts loaded:")); + + scripts_loaded = 0; + + for (i = 0; script_language[i]; i++) + { + snprintf (hdata_name, sizeof (hdata_name), + "%s_script", script_language[i]); + hdata = weechat_hdata_get (hdata_name); + ptr_script = weechat_hdata_get_list (hdata, "scripts"); + while (ptr_script) + { + ptr_name = weechat_hdata_string (hdata, ptr_script, "name"); + weechat_printf (NULL, " %s %s%s%s.%s %s%s %s(%s%s%s)", + script_repo_get_status_for_display (script_repo_search_by_name (ptr_name), + "*?HN", 0), + weechat_color (weechat_config_string (script_config_color_text_name)), + ptr_name, + weechat_color (weechat_config_string (script_config_color_text_extension)), + script_extension[i], + weechat_color (weechat_config_string (script_config_color_text_version)), + weechat_hdata_string (hdata, ptr_script, "version"), + weechat_color ("chat_delimiters"), + weechat_color (weechat_config_string (script_config_color_text_description)), + weechat_hdata_string (hdata, ptr_script, "description"), + weechat_color ("chat_delimiters")); + scripts_loaded++; + ptr_script = weechat_hdata_move (hdata, ptr_script, 1); + } + } + + if (scripts_loaded == 0) + { + weechat_printf (NULL, _(" (none)")); + } +} + +/* + * script_action_load: load a script + */ + +void +script_action_load (const char *name) +{ + char *pos, str_command[1024]; + int language; + + language = -1; + pos = strrchr (name, '.'); + if (pos) + language = script_language_search_by_extension (pos + 1); + if (language < 0) + { + weechat_printf (NULL, + _("%s: unknown language for script \"%s\""), + SCRIPT_PLUGIN_NAME, name); + return; + } + + /* execute command (for example: "/perl load iset.pl") */ + snprintf (str_command, sizeof (str_command), + "/%s load %s", + script_language[language], + name); + weechat_command (NULL, str_command); +} + +/* + * script_action_unload: unload a script + */ + +void +script_action_unload (const char *name) +{ + char *pos, hdata_name[128], *filename, *ptr_base_name, str_command[1024]; + const char *ptr_filename, *ptr_registered_name; + int language, found, i; + struct t_hdata *hdata; + void *ptr_script; + + language = -1; + pos = strrchr (name, '.'); + if (pos) + { + /* unload script by using name + extension (example: "iset.pl") */ + language = script_language_search_by_extension (pos + 1); + if (language < 0) + { + weechat_printf (NULL, + _("%s: unknown language for script \"%s\""), + SCRIPT_PLUGIN_NAME, name); + return; + } + /* + * search registered name of script using name with extension, + * for example with "iset.pl" we should find "iset" + */ + snprintf (hdata_name, sizeof (hdata_name), + "%s_script", script_language[language]); + hdata = weechat_hdata_get (hdata_name); + ptr_script = weechat_hdata_get_list (hdata, "scripts"); + while (ptr_script) + { + found = 0; + ptr_filename = weechat_hdata_string (hdata, ptr_script, "filename"); + if (ptr_filename) + { + filename = strdup (ptr_filename); + if (filename) + { + ptr_base_name = basename (filename); + if (strcmp (ptr_base_name, name) == 0) + found = 1; + free (filename); + } + } + if (found) + { + ptr_registered_name = weechat_hdata_string (hdata, ptr_script, + "name"); + if (ptr_registered_name) + { + snprintf (str_command, sizeof (str_command), + "/%s unload %s", + script_language[language], + ptr_registered_name); + weechat_command (NULL, str_command); + } + return; + } + ptr_script = weechat_hdata_move (hdata, ptr_script, 1); + } + } + else + { + /* unload script by using name (example: "iset") */ + for (i = 0; script_language[i]; i++) + { + snprintf (hdata_name, sizeof (hdata_name), + "%s_script", script_language[i]); + hdata = weechat_hdata_get (hdata_name); + ptr_script = weechat_hdata_get_list (hdata, "scripts"); + while (ptr_script) + { + ptr_registered_name = weechat_hdata_string (hdata, ptr_script, + "name"); + if (strcmp (ptr_registered_name, name) == 0) + { + snprintf (str_command, sizeof (str_command), + "/%s unload %s", + script_language[i], + name); + weechat_command (NULL, str_command); + return; + } + ptr_script = weechat_hdata_move (hdata, ptr_script, 1); + } + } + } +} + +/* + * script_action_installnext_timer_cb: callback called to install next script + */ + +int +script_action_installnext_timer_cb (void *data, int remaining_calls) +{ + /* make C compiler happy */ + (void) data; + (void) remaining_calls; + + script_action_install (); + + return WEECHAT_RC_OK; +} + +/* + * script_action_install_process_cb: callback called when script is downloaded + */ + +int +script_action_install_process_cb (void *data, const char *command, + int return_code, const char *out, + const char *err) +{ + char *pos, *filename, str_signal[256]; + struct t_repo_script *ptr_script; + + /* make C compiler happy */ + (void) data; + + if (return_code >= 0) + { + pos = strrchr (command, '/'); + + if ((err && err[0]) || (out && (strncmp (out, "error:", 6) == 0))) + { + weechat_printf (NULL, + _("%s%s: error downloading script \"%s\": %s"), + weechat_prefix ("error"), + SCRIPT_PLUGIN_NAME, + (pos) ? pos + 1 : "?", + (err && err[0]) ? err : out + 6); + return WEECHAT_RC_OK; + } + + if (pos) + { + ptr_script = script_repo_search_by_name_ext (pos + 1); + if (ptr_script) + { + filename = script_config_get_script_download_filename (ptr_script); + if (filename) + { + snprintf (str_signal, sizeof (str_signal), + "%s_script_install", + script_language[ptr_script->language]); + weechat_hook_signal_send (str_signal, + WEECHAT_HOOK_SIGNAL_STRING, + filename); + free (filename); + } + + /* schedule install of next script */ + weechat_hook_timer (10, 0, 1, + &script_action_installnext_timer_cb, NULL); + } + } + } + + return WEECHAT_RC_OK; +} + +/* + * script_action_install: install script(s) marked for install + */ + +void +script_action_install () +{ + struct t_repo_script *ptr_script, *ptr_script_to_install; + char *filename, *url; + int length; + struct t_hashtable *options; + + ptr_script_to_install = NULL; + + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + if (ptr_script->install_order > 0) + { + if (ptr_script->install_order == 1) + ptr_script_to_install = ptr_script; + ptr_script->install_order--; + } + } + + if (ptr_script_to_install) + { + filename = script_config_get_script_download_filename (ptr_script_to_install); + if (filename) + { + options = weechat_hashtable_new (8, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, + NULL); + if (options) + { + length = 4 + strlen (ptr_script_to_install->url) + 1; + url = malloc (length); + if (url) + { + weechat_printf (NULL, + _("%s: downloading script \"%s\"..."), + SCRIPT_PLUGIN_NAME, + ptr_script_to_install->name_with_extension); + + snprintf (url, length, "url:%s", + ptr_script_to_install->url); + weechat_hashtable_set (options, "file_out", filename); + weechat_hook_process_hashtable (url, options, 30000, + &script_action_install_process_cb, + NULL); + free (url); + } + weechat_hashtable_free (options); + } + free (filename); + } + } +} + +/* + * script_action_remove: remove a script + */ + +void +script_action_remove (const char *name) +{ + struct t_repo_script *ptr_script; + char str_signal[256]; + + ptr_script = script_repo_search_by_name_ext (name); + if (ptr_script) + { + if (!(ptr_script->status & SCRIPT_STATUS_INSTALLED)) + { + weechat_printf (NULL, + _("%s: script \"%s\" is not installed"), + SCRIPT_PLUGIN_NAME, name); + } + else if (ptr_script->status & SCRIPT_STATUS_HELD) + { + weechat_printf (NULL, + _("%s: script \"%s\" is held"), + SCRIPT_PLUGIN_NAME, name); + } + else + { + snprintf (str_signal, sizeof (str_signal), + "%s_script_remove", + script_language[ptr_script->language]); + weechat_hook_signal_send (str_signal, + WEECHAT_HOOK_SIGNAL_STRING, + ptr_script->name_with_extension); + } + } + else + { + weechat_printf (NULL, + _("%s: script \"%s\" not found"), + SCRIPT_PLUGIN_NAME, name); + } +} + +/* + * script_action_hold: (un)hold a script + * return 1 if ok, 0 if error + */ + +int +script_action_hold (const char *name, int quiet) +{ + struct t_repo_script *ptr_script; + + ptr_script = script_repo_search_by_name_ext (name); + if (ptr_script) + { + if (ptr_script->status & SCRIPT_STATUS_HELD) + { + script_config_unhold (ptr_script->name_with_extension); + if (!quiet) + { + weechat_printf (NULL, + _("%s: script \"%s\" not " + "held any more"), + SCRIPT_PLUGIN_NAME, name); + } + } + else + { + script_config_hold (ptr_script->name_with_extension); + if (!quiet) + { + weechat_printf (NULL, + _("%s: script \"%s\" held"), + SCRIPT_PLUGIN_NAME, name); + } + } + script_repo_update_status (ptr_script); + return 1; + } + else + { + if (!quiet) + { + weechat_printf (NULL, + _("%s: script \"%s\" not found"), + SCRIPT_PLUGIN_NAME, name); + } + } + + return 0; +} + +/* + * script_action_show: show detailed info on a script + */ + +void +script_action_show (const char *name, int quiet) +{ + struct t_repo_script *ptr_script; + + if (name) + { + ptr_script = script_repo_search_by_name_ext (name); + if (ptr_script) + { + script_buffer_show_detail_script (ptr_script); + } + else + { + if (!quiet) + { + weechat_printf (NULL, + _("%s: script \"%s\" not found"), + SCRIPT_PLUGIN_NAME, name); + } + } + } + else + script_buffer_show_detail_script (NULL); +} + +/* + * script_action_run: run planned actions + * return 1 if an action was executed, otherwise 0 + */ + +int +script_action_run () +{ + char **actions, **argv, **argv_eol, *ptr_action; + int num_actions, argc, i, j, quiet, script_found; + struct t_repo_script *ptr_script; + + if (!script_actions) + return 0; + + actions = weechat_string_split (script_actions, "\n", 0, 0, &num_actions); + if (actions) + { + for (i = 0; i < num_actions; i++) + { + quiet = 0; + ptr_action = actions[i]; + if (ptr_action[0] == '-') + { + /* + * if action starts with options (like "-q"), + * read and skip them + */ + ptr_action++; + while (ptr_action[0] && (ptr_action[0] != ' ')) + { + switch (ptr_action[0]) + { + case 'q': /* quiet */ + quiet = 1; + break; + } + ptr_action++; + } + while (ptr_action[0] == ' ') + { + ptr_action++; + } + } + argv = weechat_string_split (ptr_action, " ", 0, 0, &argc); + argv_eol = weechat_string_split (ptr_action, " ", 1, 0, &argc); + if (argv && argv_eol) + { + if (weechat_strcasecmp (argv[0], "buffer") == 0) + { + /* open buffer with list of scripts */ + if (!script_buffer) + { + script_buffer_open (); + script_buffer_refresh (1); + } + weechat_buffer_set (script_buffer, "display", "1"); + } + else if (weechat_strcasecmp (argv[0], "list") == 0) + { + script_action_list (); + } + else if (weechat_strcasecmp (argv[0], "load") == 0) + { + for (j = 1; j < argc; j++) + { + script_action_load (argv[j]); + } + } + else if (weechat_strcasecmp (argv[0], "unload") == 0) + { + for (j = 1; j < argc; j++) + { + script_action_unload (argv[j]); + } + } + else if (weechat_strcasecmp (argv[0], "install") == 0) + { + script_found = 0; + for (j = 1; j < argc; j++) + { + ptr_script = script_repo_search_by_name_ext (argv[j]); + if (ptr_script) + { + if (ptr_script->status & SCRIPT_STATUS_HELD) + { + weechat_printf (NULL, + _("%s: script \"%s\" is held"), + SCRIPT_PLUGIN_NAME, argv[j]); + } + else if ((ptr_script->status & SCRIPT_STATUS_INSTALLED) + && !(ptr_script->status & SCRIPT_STATUS_NEW_VERSION)) + { + weechat_printf (NULL, + _("%s: script \"%s\" is already " + "installed and up-to-date"), + SCRIPT_PLUGIN_NAME, argv[j]); + } + else + { + script_found++; + ptr_script->install_order = script_found; + } + } + else + { + weechat_printf (NULL, + _("%s: script \"%s\" not found"), + SCRIPT_PLUGIN_NAME, argv[j]); + } + } + if (script_found) + script_action_install (); + } + else if (weechat_strcasecmp (argv[0], "remove") == 0) + { + for (j = 1; j < argc; j++) + { + script_action_remove (argv[j]); + } + } + else if (weechat_strcasecmp (argv[0], "hold") == 0) + { + script_found = 0; + for (j = 1; j < argc; j++) + { + if (script_action_hold (argv[j], quiet)) + script_found = 1; + } + if (script_found) + script_buffer_refresh (0); + } + else if (weechat_strcasecmp (argv[0], "show") == 0) + { + if (!script_buffer) + script_buffer_open (); + script_action_show ((argc >= 2) ? argv[1] : NULL, + quiet); + weechat_buffer_set (script_buffer, "display", "1"); + } + else if (weechat_strcasecmp (argv[0], "upgrade") == 0) + { + script_found = 0; + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + /* + * if script is intalled, with new version available, + * and not held, then upgrade it + */ + if ((ptr_script->status & SCRIPT_STATUS_INSTALLED) + && (ptr_script->status & SCRIPT_STATUS_NEW_VERSION) + && !(ptr_script->status & SCRIPT_STATUS_HELD)) + { + script_found++; + ptr_script->install_order = script_found; + } + } + if (script_found) + script_action_install (); + else + { + weechat_printf (NULL, + _("%s: all scripts are up-to-date"), + SCRIPT_PLUGIN_NAME); + } + } + } + if (argv) + weechat_string_free_split (argv); + if (argv_eol) + weechat_string_free_split (argv_eol); + } + weechat_string_free_split (actions); + } + + free (script_actions); + script_actions = NULL; + + return 1; +} + + +/* + * script_action_add: add an action to list of actions + */ + +void +script_action_add (const char *action) +{ + char *new_actions; + + if (!action) + return; + + if (script_actions) + { + new_actions = realloc (script_actions, + strlen (script_actions) + 1 + strlen (action) + 1); + if (!new_actions) + return; + script_actions = new_actions; + strcat (script_actions, "\n"); + strcat (script_actions, action); + } + else + { + script_actions = strdup (action); + } +} + +/* + * script_action_schedule: schedule action + * if "need_repository" is 1, then the action will be + * executed only when the repository file is up-to-date + */ + +void +script_action_schedule (const char *action, int need_repository, int quiet) +{ + script_action_add (action); + + if (need_repository) + { + if (script_repo_file_is_uptodate ()) + { + if (!repo_scripts) + script_repo_file_read (quiet); + script_action_run (); + } + else + script_repo_file_update (quiet); + } + else + script_action_run (); +} diff --git a/src/plugins/script/script-action.h b/src/plugins/script/script-action.h new file mode 100644 index 000000000..42af8c639 --- /dev/null +++ b/src/plugins/script/script-action.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_SCRIPT_ACTION_H +#define __WEECHAT_SCRIPT_ACTION_H 1 + +extern char *script_actions; + +extern int script_action_run (); +extern void script_action_schedule (const char *action, int need_repository, + int quiet); + +#endif /* __WEECHAT_SCRIPT_ACTION_H */ diff --git a/src/plugins/script/script-buffer.c b/src/plugins/script/script-buffer.c new file mode 100644 index 000000000..bf040010d --- /dev/null +++ b/src/plugins/script/script-buffer.c @@ -0,0 +1,797 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * script-buffer.c: display scripts on script buffer + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "../weechat-plugin.h" +#include "script.h" +#include "script-buffer.h" +#include "script-config.h" +#include "script-repo.h" + + +struct t_gui_buffer *script_buffer = NULL; +int script_buffer_selected_line = 0; +struct t_repo_script *script_buffer_detail_script = NULL; + + +/* + * script_buffer_display_line_script: display a line with script + */ + +void +script_buffer_display_line_script (int line, struct t_repo_script *script) +{ + char str_line[16384], str_item[1024], str_color_name[256], str_color[32]; + char str_format[256], str_date[64], str_key[2], utf_char[16], *tags; + const char *columns, *ptr_col; + int char_size, *ptr_max_length, max_length, num_spaces, unknown; + struct tm *tm; + + snprintf (str_color_name, sizeof (str_color_name), + "%s,%s", + (line == script_buffer_selected_line) ? + weechat_config_string (script_config_color_text_selected) : + weechat_config_string (script_config_color_text), + (line == script_buffer_selected_line) ? + weechat_config_string (script_config_color_text_bg_selected) : + weechat_config_string (script_config_color_text_bg)); + snprintf (str_color, sizeof (str_color), + "%s", weechat_color (str_color_name)); + + columns = weechat_config_string (script_config_look_columns); + ptr_col = columns; + + str_line[0] = '\0'; + while (ptr_col[0]) + { + unknown = 0; + str_item[0] = '\0'; + num_spaces = 0; + char_size = weechat_utf8_char_size (ptr_col); + memcpy (utf_char, ptr_col, char_size); + utf_char[char_size] = '\0'; + if (utf_char[0] == '%') + { + ptr_col += char_size; + char_size = weechat_utf8_char_size (ptr_col); + memcpy (utf_char, ptr_col, char_size); + utf_char[char_size] = '\0'; + + str_key[0] = ptr_col[0]; + str_key[1] = '\0'; + ptr_max_length = weechat_hashtable_get (script_repo_max_length_field, + str_key); + max_length = (ptr_max_length) ? *ptr_max_length : 0; + num_spaces = max_length; + + switch (utf_char[0]) + { + case 'a': /* author */ + if (script->author) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script->author); + snprintf (str_item, sizeof (str_item), + "%s", script->author); + } + break; + case 'd': /* description */ + if (script->description) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script->description); + snprintf (str_item, sizeof (str_item), + "%s%s", + weechat_color ((line == script_buffer_selected_line) ? "white" : "249"), + script->description); + } + break; + case 'D': /* date added */ + if (script->date_added > 0) + { + tm = localtime (&script->date_added); + strftime (str_date, sizeof (str_date), + "%Y-%m-%d", tm); + snprintf (str_item, sizeof (str_item), + "%s%s", + weechat_color ( + weechat_config_string ( + (line == script_buffer_selected_line) ? + script_config_color_text_date_selected : + script_config_color_text_date)), + str_date); + } + else + num_spaces = 10; + break; + case 'e': /* file extension */ + if (script->language >= 0) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script_extension[script->language]); + snprintf (str_item, sizeof (str_item), + "%s%s", + weechat_color ( + weechat_config_string ( + (line == script_buffer_selected_line) ? + script_config_color_text_extension_selected : + script_config_color_text_extension)), + script_extension[script->language]); + } + break; + case 'l': /* language */ + if (script->language >= 0) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script_language[script->language]); + snprintf (str_item, sizeof (str_item), + "%s", script_language[script->language]); + } + break; + case 'L': /* license */ + if (script->license) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script->license); + snprintf (str_item, sizeof (str_item), + "%s", script->license); + } + break; + case 'n': /* name + extension */ + if (script->name_with_extension) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script->name_with_extension); + snprintf (str_item, sizeof (str_item), + "%s%s%s.%s", + weechat_color ( + weechat_config_string ( + (line == script_buffer_selected_line) ? + script_config_color_text_name_selected : + script_config_color_text_name)), + script->name, + weechat_color ( + weechat_config_string ( + (line == script_buffer_selected_line) ? + script_config_color_text_extension_selected : + script_config_color_text_extension)), + script_extension[script->language]); + } + break; + case 'N': /* name (without extension) */ + if (script->name) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script->name); + snprintf (str_item, sizeof (str_item), + "%s%s", + weechat_color ( + weechat_config_string ( + (line == script_buffer_selected_line) ? + script_config_color_text_name_selected : + script_config_color_text_name)), + script->name); + } + break; + case 'r': /* requirements */ + if (script->requirements) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script->requirements); + snprintf (str_item, sizeof (str_item), + "%s", script->requirements); + } + break; + case 's': /* status */ + snprintf (str_item, sizeof (str_item), + script_repo_get_status_for_display (script, + "*iHrN", 0)); + break; + case 't': /* tags */ + if (script->tags) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script->tags); + tags = weechat_string_replace (script->tags, ",", " "); + if (tags) + { + snprintf (str_item, sizeof (str_item), + "%s%s", + weechat_color ( + weechat_config_string ( + (line == script_buffer_selected_line) ? + script_config_color_text_tags_selected : + script_config_color_text_tags)), + tags); + free (tags); + } + } + break; + case 'u': /* date updated */ + if (script->date_updated > 0) + { + tm = localtime (&script->date_updated); + strftime (str_date, sizeof (str_date), + "%Y-%m-%d", tm); + snprintf (str_item, sizeof (str_item), + "%s%s", + weechat_color ( + weechat_config_string ( + (line == script_buffer_selected_line) ? + script_config_color_text_date_selected : + script_config_color_text_date)), + str_date); + } + else + num_spaces = 10; + break; + case 'v': /* version */ + if (script->version) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script->version); + snprintf (str_item, sizeof (str_item), + "%s%s", + weechat_color ( + weechat_config_string ( + (line == script_buffer_selected_line) ? + script_config_color_text_version_selected : + script_config_color_text_version)), + script->version); + } + break; + case 'V': /* version loaded */ + if (script->version_loaded) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script->version_loaded); + snprintf (str_item, sizeof (str_item), + "%s%s", + weechat_color ( + weechat_config_string ( + (line == script_buffer_selected_line) ? + script_config_color_text_version_loaded_selected : + script_config_color_text_version_loaded)), + script->version_loaded); + } + break; + case 'w': /* min_weechat */ + if (script->min_weechat) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script->min_weechat); + snprintf (str_item, sizeof (str_item), + "%s", script->min_weechat); + } + break; + case 'W': /* max_weechat */ + if (script->max_weechat) + { + num_spaces = max_length - weechat_utf8_strlen_screen (script->max_weechat); + snprintf (str_item, sizeof (str_item), + "%s", script->max_weechat); + } + break; + case '%': /* "%%" will display a single "%" */ + snprintf (str_item, sizeof (str_item), + "%s%%", + weechat_color (weechat_config_string (script_config_color_text_delimiters))); + break; + default: + unknown = 1; + break; + } + } + else + { + snprintf (str_item, sizeof (str_item), + "%s%s", + weechat_color (weechat_config_string (script_config_color_text_delimiters)), + utf_char); + } + if (!unknown) + { + if (str_item[0]) + { + strcat (str_line, str_color); + strcat (str_line, str_item); + } + if (num_spaces > 0) + { + snprintf (str_format, sizeof (str_format), + "%%-%ds", + num_spaces); + snprintf (str_item, sizeof (str_item), + str_format, " "); + strcat (str_line, str_item); + } + } + ptr_col += char_size; + } + + weechat_printf_y (script_buffer, line, "%s", str_line); +} + +/* + * script_buffer_detail_label: get header of a line for detail of script + * The returned string is aligned on the right + */ + +const char * +script_buffer_detail_label (const char *text, int max_length) +{ + char str_format[16]; + static char result[1024]; + int num_spaces; + + num_spaces = max_length - weechat_utf8_strlen_screen (text); + snprintf (str_format, sizeof (str_format), + "%%-%ds%%s", num_spaces); + snprintf (result, sizeof (result), + str_format, + (num_spaces > 0) ? " " : "", + text); + + return result; +} + +/* + * script_buffer_display_detail_script: display detail on a script + */ + +void +script_buffer_display_detail_script (struct t_repo_script *script) +{ + struct tm *tm; + char str_time[1024]; + char *labels[] = { N_("Script"), N_("Version"), N_("Author"), + N_("License"), N_("Description"), N_("Tags"), + N_("Status"), N_("Date added"), N_("Date updated"), + N_("URL"), N_("MD5"), N_("Requires"), N_("Min WeeChat"), + N_("Max WeeChat"), NULL }; + int i, length, max_length, line; + + max_length = 0; + for (i = 0; labels[i]; i++) + { + length = weechat_utf8_strlen_screen (_(labels[i])); + if (length > max_length) + max_length = length; + } + + line = 0; + + weechat_printf_y (script_buffer, line + 1, + "%s: %s%s%s.%s", + script_buffer_detail_label (_(labels[line]), max_length), + weechat_color (weechat_config_string (script_config_color_text_name)), + script->name, + weechat_color (weechat_config_string (script_config_color_text_extension)), + script_extension[script->language]); + line++; + weechat_printf_y (script_buffer, line + 1, "%s: %s", + script_buffer_detail_label (_(labels[line]), max_length), + script->version); + line++; + weechat_printf_y (script_buffer, line + 1, + "%s: %s <%s>", + script_buffer_detail_label (_(labels[line]), max_length), + script->author, + script->mail); + line++; + weechat_printf_y (script_buffer, line + 1, + "%s: %s", + script_buffer_detail_label (_(labels[line]), max_length), + script->license); + line++; + weechat_printf_y (script_buffer, line + 1, + "%s: %s", + script_buffer_detail_label (_(labels[line]), max_length), + script->description); + line++; + weechat_printf_y (script_buffer, line + 1, + "%s: %s", + script_buffer_detail_label (_(labels[line]), max_length), + script->tags); + line++; + if ((script->popularity == 0) && (script->status == 0)) + { + weechat_printf_y (script_buffer, line + 1, + "%s: -", + script_buffer_detail_label (_(labels[line]), max_length)); + } + else + { + weechat_printf_y (script_buffer, line + 1, + "%s: %s%s (%s%s%s%s%s%s%s%s%s%s )", + script_buffer_detail_label (_(labels[line]), max_length), + script_repo_get_status_for_display (script, "*iHrN", 1), + weechat_color ("chat"), + (script->popularity > 0) ? " " : "", + (script->popularity > 0) ? _("popular") : "", + (script->status & SCRIPT_STATUS_INSTALLED) ? " " : "", + (script->status & SCRIPT_STATUS_INSTALLED) ? _("installed") : "", + (script->status & SCRIPT_STATUS_HELD) ? " " : "", + (script->status & SCRIPT_STATUS_HELD) ? _("held") : "", + (script->status & SCRIPT_STATUS_RUNNING) ? " " : "", + (script->status & SCRIPT_STATUS_RUNNING) ? _("running") : "", + (script->status & SCRIPT_STATUS_NEW_VERSION) ? " " : "", + (script->status & SCRIPT_STATUS_NEW_VERSION) ? _("obsolete") : ""); + } + line++; + tm = localtime (&script->date_added); + strftime (str_time, sizeof (str_time), "%Y-%m-%d %H:%M:%S", tm); + weechat_printf_y (script_buffer, line + 1, + "%s: %s", + script_buffer_detail_label (_(labels[line]), max_length), + str_time); + line++; + tm = localtime (&script->date_updated); + strftime (str_time, sizeof (str_time), "%Y-%m-%d %H:%M:%S", tm); + weechat_printf_y (script_buffer, line + 1, + "%s: %s", + script_buffer_detail_label (_(labels[line]), max_length), + str_time); + line++; + weechat_printf_y (script_buffer, line + 1, + "%s: %s", + script_buffer_detail_label (_(labels[line]), max_length), + script->url); + line++; + weechat_printf_y (script_buffer, line + 1, + "%s: %s", + script_buffer_detail_label (_(labels[line]), max_length), + script->md5sum); + line++; + weechat_printf_y (script_buffer, line + 1, + "%s: %s", + script_buffer_detail_label (_(labels[line]), max_length), + (script->requirements) ? script->requirements : "-"); + line++; + weechat_printf_y (script_buffer, line + 1, + "%s: %s", + script_buffer_detail_label (_(labels[line]), max_length), + (script->min_weechat) ? script->min_weechat : "-"); + line++; + weechat_printf_y (script_buffer, line + 1, + "%s: %s", + script_buffer_detail_label (_(labels[line]), max_length), + (script->max_weechat) ? script->max_weechat : "-"); +} + +/* + * script_buffer_refresh: update list of scripts in script buffer + */ + +void +script_buffer_refresh (int clear) +{ + struct t_repo_script *ptr_script; + int line; + char str_title[1024]; + + if (!script_buffer) + return; + + if (clear) + { + weechat_buffer_clear (script_buffer); + script_buffer_selected_line = (script_repo_count_displayed > 0) ? 0 : -1; + } + + if (script_buffer_detail_script) + { + snprintf (str_title, sizeof (str_title), + _("alt+d=back to list")); + } + else + { + snprintf (str_title, sizeof (str_title), + _("%d/%d scripts (filter: %s) | Sort: %s | " + "alt+i=install r=remove l=load u=unload h=(un)hold " + "d=show detail | Input: 'q'=close 'r'=refresh 's:x,y'=sort " + "'words'=filter '*'=reset filter"), + script_repo_count_displayed, + script_repo_count, + (script_repo_filter) ? script_repo_filter : "*", + weechat_config_string (script_config_look_sort)); + } + weechat_buffer_set (script_buffer, "title", str_title); + + if (script_buffer_detail_script) + { + /* detail on a script */ + script_buffer_display_detail_script (script_buffer_detail_script); + } + else + { + /* list of scripts */ + line = 0; + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + if (ptr_script->displayed) + { + script_buffer_display_line_script (line, ptr_script); + line++; + } + } + } +} + +/* + * script_buffer_set_current_line: set current selected line in script buffer + */ + +void +script_buffer_set_current_line (int line) +{ + int old_line; + + old_line = script_buffer_selected_line; + script_buffer_selected_line = line; + + script_buffer_display_line_script (old_line, + script_repo_search_displayed_by_number (old_line)); + script_buffer_display_line_script (script_buffer_selected_line, + script_repo_search_displayed_by_number (script_buffer_selected_line)); +} + +/* + * script_buffer_show_detail_script: show detailed info on a script + */ + +void +script_buffer_show_detail_script (struct t_repo_script *script) +{ + if (!script_buffer) + return; + + if (script_buffer_detail_script == script) + script_buffer_detail_script = NULL; + else + script_buffer_detail_script = script; + + weechat_buffer_clear (script_buffer); + script_buffer_refresh (0); + + if (!script_buffer_detail_script) + script_buffer_check_line_outside_window (); +} + +/* + * script_buffer_get_window_info: get infos about window + */ + +void +script_buffer_get_window_info (struct t_gui_window *window, + int *start_line_y, int *chat_height) +{ + struct t_hdata *hdata_window, *hdata_window_scroll, *hdata_line; + struct t_hdata *hdata_line_data; + void *window_scroll, *start_line, *line_data; + + hdata_window = weechat_hdata_get ("window"); + hdata_window_scroll = weechat_hdata_get ("window_scroll"); + hdata_line = weechat_hdata_get ("line"); + hdata_line_data = weechat_hdata_get ("line_data"); + *start_line_y = 0; + window_scroll = weechat_hdata_pointer (hdata_window, window, "scroll"); + if (window_scroll) + { + start_line = weechat_hdata_pointer (hdata_window_scroll, window_scroll, + "start_line"); + if (start_line) + { + line_data = weechat_hdata_pointer (hdata_line, start_line, "data"); + if (line_data) + { + *start_line_y = weechat_hdata_integer (hdata_line_data, + line_data, "y"); + } + } + } + *chat_height = weechat_hdata_integer (hdata_window, window, + "win_chat_height"); +} + +/* + * script_buffer_check_line_outside_window: check if current line is outside + * window + */ + +void +script_buffer_check_line_outside_window () +{ + struct t_gui_window *window; + int start_line_y, chat_height; + char str_command[256]; + + window = weechat_window_search_with_buffer (script_buffer); + if (!window) + return; + + script_buffer_get_window_info (window, &start_line_y, &chat_height); + if ((start_line_y > script_buffer_selected_line) + || (start_line_y <= script_buffer_selected_line - chat_height)) + { + snprintf (str_command, sizeof (str_command), + "/window scroll -window %d %s%d", + weechat_window_get_integer (window, "number"), + (start_line_y > script_buffer_selected_line) ? "-" : "+", + (start_line_y > script_buffer_selected_line) ? + start_line_y - script_buffer_selected_line : + script_buffer_selected_line - start_line_y - chat_height + 1); + weechat_command (script_buffer, str_command); + } +} + +/* + * script_buffer_window_scrolled_cb: called when signal "window_scrolled" is + * received + */ + +int +script_buffer_window_scrolled_cb (void *data, const char *signal, + const char *type_data, + void *signal_data) +{ + int start_line_y, chat_height, line; + + /* make C compiler happy */ + (void) data; + (void) signal; + (void) type_data; + + /* scrolled another window/buffer? then just ignore */ + if (weechat_window_get_pointer (signal_data, "buffer") != script_buffer) + return WEECHAT_RC_OK; + + /* ignore if detail of a script is displayed */ + if (script_buffer_detail_script) + return WEECHAT_RC_OK; + + script_buffer_get_window_info (signal_data, &start_line_y, &chat_height); + + line = script_buffer_selected_line; + while (line < start_line_y) + { + line += chat_height; + } + while (line >= start_line_y + chat_height) + { + line -= chat_height; + } + if (line < start_line_y) + line = start_line_y; + if (line >= script_repo_count_displayed) + line = script_repo_count_displayed - 1; + script_buffer_set_current_line (line); + + return WEECHAT_RC_OK; +} + +/* + * script_buffer_input_cb: callback called when user send data to script list + * buffer + */ + +int +script_buffer_input_cb (void *data, struct t_gui_buffer *buffer, + const char *input_data) +{ + /* make C compiler happy */ + (void) data; + (void) buffer; + + if (strcmp (input_data, "q") == 0) + { + weechat_buffer_close (buffer); + return WEECHAT_RC_OK; + } + + if (strncmp (input_data, "s:", 2) == 0) + { + if (input_data[2]) + weechat_config_option_set (script_config_look_sort, input_data + 2, 1); + else + weechat_config_option_reset (script_config_look_sort, 1); + return WEECHAT_RC_OK; + } + + if (strcmp (input_data, "r") == 0) + { + script_get_loaded_scripts (); + script_repo_remove_all (); + script_repo_file_read (1); + script_buffer_refresh (1); + return WEECHAT_RC_OK; + } + + script_repo_filter_scripts (input_data); + + return WEECHAT_RC_OK; +} + +/* + * script_buffer_close_cb: callback called when script buffer is closed + */ + +int +script_buffer_close_cb (void *data, struct t_gui_buffer *buffer) +{ + /* make C compiler happy */ + (void) data; + (void) buffer; + + script_buffer = NULL; + script_buffer_selected_line = 0; + script_buffer_detail_script = NULL; + + return WEECHAT_RC_OK; +} + +/* + * script_buffer_set_callbacks: restore buffers callbacks (input and close) for + * buffer created by script plugin + */ + +void +script_buffer_set_callbacks () +{ + struct t_gui_buffer *ptr_buffer; + + ptr_buffer = weechat_buffer_search (SCRIPT_PLUGIN_NAME, SCRIPT_BUFFER_NAME); + if (ptr_buffer) + { + script_buffer = ptr_buffer; + weechat_buffer_set_pointer (script_buffer, "close_callback", &script_buffer_close_cb); + weechat_buffer_set_pointer (script_buffer, "input_callback", &script_buffer_input_cb); + } +} + +/* + * script_buffer_open: open script buffer (to display list of scripts) + */ + +void +script_buffer_open () +{ + if (!script_buffer) + { + script_buffer = weechat_buffer_new (SCRIPT_BUFFER_NAME, + &script_buffer_input_cb, NULL, + &script_buffer_close_cb, NULL); + + /* failed to create buffer ? then exit */ + if (!script_buffer) + return; + + weechat_buffer_set (script_buffer, "type", "free"); + weechat_buffer_set (script_buffer, "title", _("Scripts")); + weechat_buffer_set (script_buffer, "key_bind_meta2-A", "/script up"); + weechat_buffer_set (script_buffer, "key_bind_meta2-B", "/script down"); + weechat_buffer_set (script_buffer, "key_bind_meta-l", "/script load"); + weechat_buffer_set (script_buffer, "key_bind_meta-u", "/script unload"); + weechat_buffer_set (script_buffer, "key_bind_meta-i", "/script install"); + weechat_buffer_set (script_buffer, "key_bind_meta-r", "/script remove"); + weechat_buffer_set (script_buffer, "key_bind_meta-h", "/script hold"); + weechat_buffer_set (script_buffer, "key_bind_meta-d", "/script show"); + weechat_buffer_set (script_buffer, "localvar_set_type", "script"); + + script_buffer_selected_line = 0; + script_buffer_detail_script = NULL; + } +} diff --git a/src/plugins/script/script-buffer.h b/src/plugins/script/script-buffer.h new file mode 100644 index 000000000..a9a29d326 --- /dev/null +++ b/src/plugins/script/script-buffer.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_SCRIPT_BUFFER_H +#define __WEECHAT_SCRIPT_BUFFER_H 1 + +#define SCRIPT_BUFFER_NAME "scripts" + +struct t_repo_script; + +extern struct t_gui_buffer *script_buffer; +extern int script_buffer_selected_line; +extern struct t_repo_script *script_buffer_detail_script; + +extern void script_buffer_refresh (int clear); +extern void script_buffer_set_current_line (int line); +extern void script_buffer_show_detail_script (struct t_repo_script *script); +extern void script_buffer_check_line_outside_window (); +extern int script_buffer_window_scrolled_cb (void *data, const char *signal, + const char *type_data, + void *signal_data); +extern int script_buffer_input_cb (void *data, struct t_gui_buffer *buffer, + const char *input_data); +extern int script_buffer_close_cb (void *data, struct t_gui_buffer *buffer); +extern void script_buffer_set_callbacks (); +extern void script_buffer_open (); + +#endif /* __WEECHAT_SCRIPT_BUFFER_H */ diff --git a/src/plugins/script/script-command.c b/src/plugins/script/script-command.c new file mode 100644 index 000000000..4ba65cb84 --- /dev/null +++ b/src/plugins/script/script-command.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * script-command.c: script commands + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "../weechat-plugin.h" +#include "script.h" +#include "script-command.h" +#include "script-action.h" +#include "script-buffer.h" +#include "script-config.h" +#include "script-repo.h" + + +/* + * script_command_action: run action + */ + +void +script_command_action (struct t_gui_buffer *buffer, const char *action, + const char *action_with_args, int need_repository) +{ + struct t_repo_script *ptr_script; + char str_action[4096]; + + if (action_with_args) + { + /* action with arguments on command line */ + script_action_schedule (action_with_args, need_repository, 0); + } + else if (script_buffer && (buffer == script_buffer)) + { + /* action on current line of script buffer */ + if ((weechat_strcasecmp (action, "show") == 0) + && script_buffer_detail_script) + { + /* if detail on script is displayed, back to list */ + snprintf (str_action, sizeof (str_action), + "-q %s", + action); + script_action_schedule (str_action, need_repository, 1); + } + else + { + /* if list is displayed, execute action on script */ + if (!script_buffer_detail_script) + { + ptr_script = script_repo_search_displayed_by_number (script_buffer_selected_line); + if (ptr_script) + { + snprintf (str_action, sizeof (str_action), + "-q %s %s", + action, + ptr_script->name_with_extension); + script_action_schedule (str_action, need_repository, 1); + } + } + } + } +} + +/* + * script_command_script: command to manage scripts + */ + +int +script_command_script (void *data, struct t_gui_buffer *buffer, int argc, + char **argv, char **argv_eol) +{ + char *error; + long value; + int line; + + /* make C compiler happy */ + (void) data; + + if (argc == 1) + { + script_action_schedule ("buffer", 1, 0); + return WEECHAT_RC_OK; + } + + if (weechat_strcasecmp (argv[1], "list") == 0) + { + script_action_schedule ("list", 1, 0); + return WEECHAT_RC_OK; + } + + if ((weechat_strcasecmp (argv[1], "load") == 0) + || (weechat_strcasecmp (argv[1], "unload") == 0)) + { + script_command_action (buffer, + argv[1], + (argc > 2) ? argv_eol[1] : NULL, + 0); + return WEECHAT_RC_OK; + } + + if ((weechat_strcasecmp (argv[1], "install") == 0) + || (weechat_strcasecmp (argv[1], "remove") == 0) + || (weechat_strcasecmp (argv[1], "hold") == 0) + || (weechat_strcasecmp (argv[1], "show") == 0)) + { + script_command_action (buffer, + argv[1], + (argc > 2) ? argv_eol[1] : NULL, + 1); + return WEECHAT_RC_OK; + } + + if (weechat_strcasecmp (argv[1], "upgrade") == 0) + { + script_action_schedule ("upgrade", 1, 0); + return WEECHAT_RC_OK; + } + + if (weechat_strcasecmp (argv[1], "update") == 0) + { + script_repo_file_update (0); + return WEECHAT_RC_OK; + } + + if (!script_buffer) + script_buffer_open (); + + if (script_buffer) + { + weechat_buffer_set (script_buffer, "display", "1"); + + if (argc > 1) + { + if (!script_buffer_detail_script + && (script_buffer_selected_line >= 0) + && (script_repo_count_displayed > 0)) + { + if (strcmp (argv[1], "up") == 0) + { + value = 1; + if (argc > 2) + { + error = NULL; + value = strtol (argv[2], &error, 10); + if (!error || error[0]) + value = 1; + } + line = script_buffer_selected_line - value; + if (line < 0) + line = 0; + if (line != script_buffer_selected_line) + { + script_buffer_set_current_line (line); + script_buffer_check_line_outside_window (); + } + return WEECHAT_RC_OK; + } + else if (strcmp (argv[1], "down") == 0) + { + value = 1; + if (argc > 2) + { + error = NULL; + value = strtol (argv[2], &error, 10); + if (!error || error[0]) + value = 1; + } + line = script_buffer_selected_line + value; + if (line >= script_repo_count_displayed) + line = script_repo_count_displayed - 1; + if (line != script_buffer_selected_line) + { + script_buffer_set_current_line (line); + script_buffer_check_line_outside_window (); + } + return WEECHAT_RC_OK; + } + } + } + } + + script_buffer_refresh (0); + + return WEECHAT_RC_OK; +} + +/* + * scrit_command_init: init script commands (create hooks) + */ + +void +script_command_init () +{ + weechat_hook_command ("script", + N_("WeeChat scripts manager"), + N_("list || show <script>" + " || load|unload <script> [<script>...]" + " || install|remove|hold <script> [<script>...]" + " || upgrade || update"), + N_(" list: list loaded scripts (all languages)\n" + " show: show detailed info about a script\n" + " load: load script(s)\n" + " unload: unload script(s)\n" + " install: install/upgrade script(s)\n" + " remove: remove script(s)\n" + " hold: hold/unhold script(s) (a script held " + "will not be upgraded any more and cannot be " + "removed)\n" + " upgrade: upgrade all installed scripts which " + "are obsolete (new version available)\n" + " update: update local scripts cache\n\n" + "Without argument, this command opens a buffer " + "with list of scripts.\n\n" + "On script buffer, the possible status for each " + "script are:\n" + " * i H r N\n" + " | | | | |\n" + " | | | | obsolete (new version available)\n" + " | | | running (loaded)\n" + " | | held\n" + " | installed\n" + " popular script\n\n" + "Keys on script buffer:\n" + " alt+i install script\n" + " alt+r remove script\n" + " alt+l load script\n" + " alt+u unload script\n" + " alt+h (un)hold script\n\n" + "Input allowed on script buffer:\n" + " q close buffer\n" + " r refresh buffer\n" + " s:x,y sort buffer using keys x and y (see /help " + "script.look.sort)\n" + " s: reset sort (use default sort)\n" + " word(s) filter scripts: search word(s) in " + "scripts (description, tags, ...)\n" + " * remove filter"), + "list" + " || show %(script_scripts)" + " || load %(script_files)" + " || unload %(python_script)|%(perl_script)|" + "%(ruby_script)|%(tcl_script)|%(lua_script)|" + "%(guile_script)" + " || install %(script_scripts)|%*" + " || remove %(script_scripts_installed)|%*" + " || hold %(script_scripts)" + " || update" + " || upgrade", + &script_command_script, NULL); +} diff --git a/src/plugins/script/script-command.h b/src/plugins/script/script-command.h new file mode 100644 index 000000000..0af7d2098 --- /dev/null +++ b/src/plugins/script/script-command.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_SCRIPT_COMMAND_H +#define __WEECHAT_SCRIPT_COMMAND_H 1 + +extern void script_command_init (); + +#endif /* __WEECHAT_SCRIPT_COMMAND_H */ diff --git a/src/plugins/script/script-completion.c b/src/plugins/script/script-completion.c new file mode 100644 index 000000000..e9d6fbc99 --- /dev/null +++ b/src/plugins/script/script-completion.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * script-completion.c: completion for script commands + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <libgen.h> + +#include "../weechat-plugin.h" +#include "script.h" +#include "script-repo.h" + + +/* + * script_completion_scripts_cb: callback for completion with scripts in + * repository + */ + +int +script_completion_scripts_cb (void *data, const char *completion_item, + struct t_gui_buffer *buffer, + struct t_gui_completion *completion) +{ + struct t_repo_script *ptr_script; + + /* make C compiler happy */ + (void) data; + (void) completion_item; + (void) buffer; + + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + weechat_hook_completion_list_add (completion, + ptr_script->name_with_extension, + 0, WEECHAT_LIST_POS_SORT); + } + + return WEECHAT_RC_OK; +} + +/* + * script_completion_scripts_installed_cb: callback for completion with scripts + * installed + */ + +int +script_completion_scripts_installed_cb (void *data, const char *completion_item, + struct t_gui_buffer *buffer, + struct t_gui_completion *completion) +{ + struct t_repo_script *ptr_script; + + /* make C compiler happy */ + (void) data; + (void) completion_item; + (void) buffer; + + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + if (ptr_script->status & SCRIPT_STATUS_INSTALLED) + { + weechat_hook_completion_list_add (completion, + ptr_script->name_with_extension, + 0, WEECHAT_LIST_POS_SORT); + } + } + + return WEECHAT_RC_OK; +} + +/* + * script_completion_exec_file_cb: callback called for each file in script + * directories + */ + +void +script_completion_exec_file_cb (void *data, const char *filename) +{ + struct t_gui_completion *completion; + const char *extension; + char *pos, *filename2, *ptr_base_name; + + completion = (struct t_gui_completion *)(((void **)data)[0]); + extension = (const char *)(((void **)data)[1]); + + pos = strrchr (filename, '.'); + if (!pos) + return; + + /* ignore scripts that do not ends with expected extension */ + if (strcmp (pos + 1, extension) != 0) + return; + + filename2 = strdup (filename); + if (filename2) + { + ptr_base_name = basename (filename2); + weechat_hook_completion_list_add (completion, + ptr_base_name, + 0, WEECHAT_LIST_POS_SORT); + free (filename2); + } +} + +/* + * script_completion_scripts_files_cb: callback for completion with files in + * script directories + */ + +int +script_completion_scripts_files_cb (void *data, const char *completion_item, + struct t_gui_buffer *buffer, + struct t_gui_completion *completion) +{ + const char *weechat_home; + char *directory; + int length, i; + void *pointers[2]; + + /* make C compiler happy */ + (void) data; + (void) completion_item; + (void) buffer; + + weechat_home = weechat_info_get ("weechat_dir", NULL); + + length = strlen (weechat_home) + 128 + 1; + directory = malloc (length); + if (directory) + { + for (i = 0; script_language[i]; i++) + { + pointers[0] = completion; + pointers[1] = script_extension[i]; + + /* look for files in "~/.weechat/<language>/" */ + snprintf (directory, length, + "%s/%s", weechat_home, script_language[i]); + weechat_exec_on_files (directory, 0, + pointers, &script_completion_exec_file_cb); + + /* look for files in "~/.weechat/<language>/autoload/" */ + snprintf (directory, length, + "%s/%s/autoload", weechat_home, script_language[i]); + weechat_exec_on_files (directory, 0, + pointers, &script_completion_exec_file_cb); + } + free (directory); + } + + return WEECHAT_RC_OK; +} + +/* + * script_completion_init: init completion for script plugin + */ + +void +script_completion_init () +{ + weechat_hook_completion ("script_scripts", + N_("list of scripts in repository"), + &script_completion_scripts_cb, NULL); + weechat_hook_completion ("script_scripts_installed", + N_("list of scripts installed (from repository)"), + &script_completion_scripts_installed_cb, NULL); + weechat_hook_completion ("script_files", + N_("files in script directories"), + &script_completion_scripts_files_cb, NULL); +} diff --git a/src/plugins/script/script-completion.h b/src/plugins/script/script-completion.h new file mode 100644 index 000000000..a14e2a0ba --- /dev/null +++ b/src/plugins/script/script-completion.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_SCRIPT_COMPLETION_H +#define __WEECHAT_SCRIPT_COMPLETION_H 1 + +extern void script_completion_init (); + +#endif /* __WEECHAT_SCRIPT_COMPLETION_H */ diff --git a/src/plugins/script/script-config.c b/src/plugins/script/script-config.c new file mode 100644 index 000000000..0123b7c8c --- /dev/null +++ b/src/plugins/script/script-config.c @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * script-config.c: script configuration options (file script.conf) + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "../weechat-plugin.h" +#include "script.h" +#include "script-config.h" +#include "script-buffer.h" +#include "script-repo.h" + + +struct t_config_file *script_config_file = NULL; +struct t_config_section *script_config_section_look = NULL; +struct t_config_section *script_config_section_color = NULL; +struct t_config_section *script_config_section_scripts = NULL; + +/* script config, look section */ + +struct t_config_option *script_config_look_columns; +struct t_config_option *script_config_look_sort; + +/* script config, color section */ + +struct t_config_option *script_config_color_status_popular; +struct t_config_option *script_config_color_status_installed; +struct t_config_option *script_config_color_status_held; +struct t_config_option *script_config_color_status_running; +struct t_config_option *script_config_color_status_obsolete; +struct t_config_option *script_config_color_status_unknown; +struct t_config_option *script_config_color_text; +struct t_config_option *script_config_color_text_date; +struct t_config_option *script_config_color_text_delimiters; +struct t_config_option *script_config_color_text_description; +struct t_config_option *script_config_color_text_extension; +struct t_config_option *script_config_color_text_name; +struct t_config_option *script_config_color_text_tags; +struct t_config_option *script_config_color_text_version; +struct t_config_option *script_config_color_text_version_loaded; +struct t_config_option *script_config_color_text_bg; +struct t_config_option *script_config_color_text_selected; +struct t_config_option *script_config_color_text_date_selected; +struct t_config_option *script_config_color_text_description_selected; +struct t_config_option *script_config_color_text_extension_selected; +struct t_config_option *script_config_color_text_name_selected; +struct t_config_option *script_config_color_text_tags_selected; +struct t_config_option *script_config_color_text_version_selected; +struct t_config_option *script_config_color_text_version_loaded_selected; +struct t_config_option *script_config_color_text_bg_selected; + +/* script config, scripts section */ + +struct t_config_option *script_config_scripts_cache_expire; +struct t_config_option *script_config_scripts_dir; +struct t_config_option *script_config_scripts_hold; +struct t_config_option *script_config_scripts_url; + + +/* + * script_config_get_dir: get local directory for script + * Note: result must be freed after use + */ + +char * +script_config_get_dir () +{ + const char *weechat_home; + char *path, *path2; + + weechat_home = weechat_info_get ("weechat_dir", NULL); + + path = weechat_string_expand_home (weechat_config_string (script_config_scripts_dir)); + path2 = weechat_string_replace ((path) ? + path : weechat_config_string (script_config_scripts_dir), + "%h", weechat_home); + + if (path && path2) + { + free (path); + path = NULL; + } + + if (path2) + return path2; + if (path) + return path; + return strdup (weechat_config_string (script_config_scripts_dir)); +} + +/* + * script_config_get_xml_filename: get filename with scripts (by default + * "/home/xxx/.weechat/script/plugins.xml.gz") + * Note: result must be freed after use + */ + +char * +script_config_get_xml_filename () +{ + char *path, *filename; + int length; + + path = script_config_get_dir (); + length = strlen (path) + 64; + filename = malloc (length); + if (filename) + snprintf (filename, length, "%s/plugins.xml.gz", path); + free (path); + return filename; +} + +/* + * script_config_get_script_download_filename: get filename for a script to + * download, for eample: + * "/home/xxx/.weechat/script/iset.pl" + * Note: result must be freed after + * use + */ + +char * +script_config_get_script_download_filename (struct t_repo_script *script) +{ + char *path, *filename; + int length; + + path = script_config_get_dir (); + length = strlen (path) + 1 + strlen (script->name_with_extension) + 1; + filename = malloc (length); + if (filename) + snprintf (filename, length, "%s/%s", path, script->name_with_extension); + free (path); + return filename; +} + +/* + * script_config_refresh_cb: callback called when user changes xfer option that + * needs a refresh of script list + */ + +void +script_config_refresh_cb (void *data, struct t_config_option *option) +{ + /* make C compiler happy */ + (void) data; + (void) option; + + if (script_buffer) + script_buffer_refresh (0); +} + +/* + * script_config_change_sort_cb: callback called when default sort keys are + * changed + */ + +void +script_config_change_sort_cb (void *data, struct t_config_option *option) +{ + /* make C compiler happy */ + (void) data; + (void) option; + + if (repo_scripts) + { + script_repo_remove_all (); + script_repo_file_read (1); + script_buffer_refresh (1); + } +} + +/* + * script_config_change_hold_cb: callback called when list of scripts to "hold" + * is changed + */ + +void +script_config_change_hold_cb (void *data, struct t_config_option *option) +{ + /* make C compiler happy */ + (void) data; + (void) option; + + script_repo_update_status_all (); + if (script_buffer) + script_buffer_refresh (0); +} + +/* + * script_config_hold: hold a script + * Note: the option is changed, but the status "held" + * in script is NOT updated by this function + */ + +void +script_config_hold (const char *name_with_extension) +{ + char **items, *hold; + int num_items, i, length; + + length = strlen (weechat_config_string (script_config_scripts_hold)) + + 1 + strlen (name_with_extension) + 1; + hold = malloc (length); + if (hold) + { + hold[0] = '\0'; + items = weechat_string_split (weechat_config_string (script_config_scripts_hold), + ",", 0, 0, &num_items); + if (items) + { + for (i = 0; i < num_items; i++) + { + if (strcmp (items[i], name_with_extension) != 0) + { + if (hold[0]) + strcat (hold, ","); + strcat (hold, items[i]); + } + } + weechat_string_free_split (items); + } + if (hold[0]) + strcat (hold, ","); + strcat (hold, name_with_extension); + + weechat_config_option_set (script_config_scripts_hold, hold, 0); + + free (hold); + } +} + +/* + * script_config_unhold: unhold a script + * Note: the option is changed, but the status "held" + * in script is NOT updated by this function + */ + +void +script_config_unhold (const char *name_with_extension) +{ + char **items, *hold; + int num_items, i, length; + + length = strlen (weechat_config_string (script_config_scripts_hold)) + 1; + hold = malloc (length); + if (hold) + { + hold[0] = '\0'; + items = weechat_string_split (weechat_config_string (script_config_scripts_hold), + ",", 0, 0, &num_items); + if (items) + { + for (i = 0; i < num_items; i++) + { + if (strcmp (items[i], name_with_extension) != 0) + { + if (hold[0]) + strcat (hold, ","); + strcat (hold, items[i]); + } + } + weechat_string_free_split (items); + } + + weechat_config_option_set (script_config_scripts_hold, hold, 0); + + free (hold); + } +} + +/* + * script_config_reaload: reload script configuration file + */ + +int +script_config_reload (void *data, struct t_config_file *config_file) +{ + /* make C compiler happy */ + (void) data; + + return weechat_config_reload (config_file); +} + +/* + * script_config_init: init script configuration file + * return: 1 if ok, 0 if error + */ + +int +script_config_init () +{ + struct t_config_section *ptr_section; + + script_config_file = weechat_config_new (SCRIPT_CONFIG_NAME, + &script_config_reload, NULL); + if (!script_config_file) + return 0; + + /* look */ + ptr_section = weechat_config_new_section (script_config_file, "look", + 0, 0, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL); + if (!ptr_section) + { + weechat_config_free (script_config_file); + return 0; + } + + script_config_look_columns = weechat_config_new_option ( + script_config_file, ptr_section, + "columns", "string", + N_("format of columns displayed in script buffer: following column " + "identifiers are replaced by their value: %a=author, %d=description, " + "%D=date added, %e=extension, %l=language, %L=license, %n=name with " + "extension, %N=name, %r=requirements, %s=status, %t=tags, " + "%u=date updated, %v=version, %V=version loaded, %w=min_weechat, " + "%W=max_weechat)"), + NULL, 0, 0, "%s %n %V %v %u | %d | %t", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_look_sort = weechat_config_new_option ( + script_config_file, ptr_section, + "sort", "string", + N_("default sort keys for scripts: comma-separated list of identifiers: " + "a=author, d=date added, e=extension, i=installed, l=language, " + "n=name, o=obsolete, p=popularity, r=running, u=date updated; char " + "\"-\" can be used before identifier to reverse order; example: " + "\"i,u\": installed scripts first, sorted by update date"), + NULL, 0, 0, "p,n", NULL, 0, + NULL, NULL, &script_config_change_sort_cb, NULL, NULL, NULL); + + /* color */ + ptr_section = weechat_config_new_section (script_config_file, "color", + 0, 0, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL); + if (!ptr_section) + { + weechat_config_free (script_config_file); + return 0; + } + + script_config_color_status_popular = weechat_config_new_option ( + script_config_file, ptr_section, + "status_popular", "color", + N_("color for status \"popular\" (\"*\")"), + NULL, 0, 0, "yellow", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_status_installed = weechat_config_new_option ( + script_config_file, ptr_section, + "status_installed", "color", + N_("color for status \"installed\" (\"i\")"), + NULL, 0, 0, "lightcyan", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_status_held = weechat_config_new_option ( + script_config_file, ptr_section, + "status_held", "color", + N_("color for status \"held\" (\"H\")"), + NULL, 0, 0, "white", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_status_running = weechat_config_new_option ( + script_config_file, ptr_section, + "status_running", "color", + N_("color for status \"running\" (\"r\")"), + NULL, 0, 0, "lightgreen", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_status_obsolete = weechat_config_new_option ( + script_config_file, ptr_section, + "status_obsolete", "color", + N_("color for status \"obsolete\" (\"N\")"), + NULL, 0, 0, "lightmagenta", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_status_unknown = weechat_config_new_option ( + script_config_file, ptr_section, + "status_unknown", "color", + N_("color for status \"unknown\" (\"?\")"), + NULL, 0, 0, "lightred", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text = weechat_config_new_option ( + script_config_file, ptr_section, + "text", "color", + N_("text color in script buffer"), + NULL, 0, 0, "default", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_date = weechat_config_new_option ( + script_config_file, ptr_section, + "text_date", "color", + N_("text color of dates in script buffer"), + NULL, 0, 0, "default", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_delimiters = weechat_config_new_option ( + script_config_file, ptr_section, + "text_delimiters", "color", + N_("text color of delimiters in script buffer"), + NULL, 0, 0, "darkgray", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_description = weechat_config_new_option ( + script_config_file, ptr_section, + "text_description", "color", + N_("text color of description in script buffer"), + NULL, 0, 0, "default", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_extension = weechat_config_new_option ( + script_config_file, ptr_section, + "text_extension", "color", + N_("text color of extension in script buffer"), + NULL, 0, 0, "default", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_name = weechat_config_new_option ( + script_config_file, ptr_section, + "text_name", "color", + N_("text color of script name in script buffer"), + NULL, 0, 0, "cyan", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_tags = weechat_config_new_option ( + script_config_file, ptr_section, + "text_tags", "color", + N_("text color of tags in script buffer"), + NULL, 0, 0, "brown", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_version = weechat_config_new_option ( + script_config_file, ptr_section, + "text_version", "color", + N_("text color of version in script buffer"), + NULL, 0, 0, "magenta", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_version_loaded = weechat_config_new_option ( + script_config_file, ptr_section, + "text_version_loaded", "color", + N_("text color of version loaded in script buffer"), + NULL, 0, 0, "default", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_bg = weechat_config_new_option ( + script_config_file, ptr_section, + "text_bg", "color", + N_("background color in script buffer"), + NULL, 0, 0, "default", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_selected = weechat_config_new_option ( + script_config_file, ptr_section, + "text_selected", "color", + N_("text color for selected line in script buffer"), + NULL, 0, 0, "white", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_date_selected = weechat_config_new_option ( + script_config_file, ptr_section, + "text_date_selected", "color", + N_("text color of dates for selected line in script buffer"), + NULL, 0, 0, "white", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_description_selected = weechat_config_new_option ( + script_config_file, ptr_section, + "text_description_selected", "color", + N_("text color of description for selected line in script buffer"), + NULL, 0, 0, "white", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_extension_selected = weechat_config_new_option ( + script_config_file, ptr_section, + "text_extension_selected", "color", + N_("text color of extension for selected line in script buffer"), + NULL, 0, 0, "white", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_name_selected = weechat_config_new_option ( + script_config_file, ptr_section, + "text_name_selected", "color", + N_("text color of script name for selected line in script buffer"), + NULL, 0, 0, "lightcyan", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_tags_selected = weechat_config_new_option ( + script_config_file, ptr_section, + "text_tags_selected", "color", + N_("text color of tags for selected line in script buffer"), + NULL, 0, 0, "yellow", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_version_selected = weechat_config_new_option ( + script_config_file, ptr_section, + "text_version_selected", "color", + N_("text color of version for selected line in script buffer"), + NULL, 0, 0, "lightmagenta", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_version_loaded_selected = weechat_config_new_option ( + script_config_file, ptr_section, + "text_version_loaded_selected", "color", + N_("text color of version loaded for selected line in script buffer"), + NULL, 0, 0, "white", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + script_config_color_text_bg_selected = weechat_config_new_option ( + script_config_file, ptr_section, + "text_bg_selected", "color", + N_("background color for selected line in script buffer"), + NULL, 0, 0, "red", NULL, 0, + NULL, NULL, &script_config_refresh_cb, NULL, NULL, NULL); + + /* scripts */ + ptr_section = weechat_config_new_section (script_config_file, "scripts", + 0, 0, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL); + if (!ptr_section) + { + weechat_config_free (script_config_file); + return 0; + } + + script_config_scripts_cache_expire = weechat_config_new_option ( + script_config_file, ptr_section, + "cache_expire", "integer", + N_("local cache expiration time, in minutes (-1 = never expires, " + "0 = always expire)"), + NULL, -1, 525600, "60", NULL, 0, NULL, NULL, + NULL, NULL, NULL, NULL); + script_config_scripts_dir = weechat_config_new_option ( + script_config_file, ptr_section, + "dir", "string", + N_("local cache directory for scripts"), + NULL, 0, 0, "%h/script", NULL, 0, NULL, NULL, + NULL, NULL, NULL, NULL); + script_config_scripts_hold = weechat_config_new_option ( + script_config_file, ptr_section, + "hold", "string", + N_("scripts to \"hold\": comma-separated list of scripts which will " + "never been upgraded and can not be removed, for example: " + "\"buffers.pl,iset.pl\""), + NULL, 0, 0, "", NULL, 0, NULL, NULL, + &script_config_change_hold_cb, NULL, NULL, NULL); + script_config_scripts_url = weechat_config_new_option ( + script_config_file, ptr_section, + "url", "string", + N_("URL for file with list of scripts"), + NULL, 0, 0, "http://www.weechat.org/files/plugins.xml.gz", NULL, 0, NULL, NULL, + NULL, NULL, NULL, NULL); + + return 1; +} + +/* + * script_config_read: read script configuration file + */ + +int +script_config_read () +{ + return weechat_config_read (script_config_file); +} + +/* + * script_config_write: write script configuration file + */ + +int +script_config_write () +{ + return weechat_config_write (script_config_file); +} + +/* + * script_config_free: free script configuration file + */ + +void +script_config_free () +{ + weechat_config_free (script_config_file); +} diff --git a/src/plugins/script/script-config.h b/src/plugins/script/script-config.h new file mode 100644 index 000000000..31ec84bb5 --- /dev/null +++ b/src/plugins/script/script-config.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_SCRIPT_CONFIG_H +#define __WEECHAT_SCRIPT_CONFIG_H 1 + +#define SCRIPT_CONFIG_NAME "script" + +struct t_repo_script; + +extern struct t_config_option *script_config_look_columns; +extern struct t_config_option *script_config_look_sort; + +extern struct t_config_option *script_config_color_status_popular; +extern struct t_config_option *script_config_color_status_installed; +extern struct t_config_option *script_config_color_status_held; +extern struct t_config_option *script_config_color_status_running; +extern struct t_config_option *script_config_color_status_obsolete; +extern struct t_config_option *script_config_color_status_unknown; +extern struct t_config_option *script_config_color_text; +extern struct t_config_option *script_config_color_text_date; +extern struct t_config_option *script_config_color_text_delimiters; +extern struct t_config_option *script_config_color_text_description; +extern struct t_config_option *script_config_color_text_extension; +extern struct t_config_option *script_config_color_text_name; +extern struct t_config_option *script_config_color_text_tags; +extern struct t_config_option *script_config_color_text_version; +extern struct t_config_option *script_config_color_text_version_loaded; +extern struct t_config_option *script_config_color_text_bg; +extern struct t_config_option *script_config_color_text_selected; +extern struct t_config_option *script_config_color_text_date_selected; +extern struct t_config_option *script_config_color_text_description_selected; +extern struct t_config_option *script_config_color_text_extension_selected; +extern struct t_config_option *script_config_color_text_name_selected; +extern struct t_config_option *script_config_color_text_tags_selected; +extern struct t_config_option *script_config_color_text_version_selected; +extern struct t_config_option *script_config_color_text_version_loaded_selected; +extern struct t_config_option *script_config_color_text_bg_selected; + +extern struct t_config_option *script_config_scripts_cache_expire; +extern struct t_config_option *script_config_scripts_dir; +extern struct t_config_option *script_config_scripts_hold; +extern struct t_config_option *script_config_scripts_url; + +extern char *script_config_get_dir (); +extern char *script_config_get_xml_filename (); +extern char *script_config_get_script_download_filename (struct t_repo_script *script); +extern void script_config_hold (const char *name_with_extension); +extern void script_config_unhold (const char *name_with_extension); +extern int script_config_init (); +extern int script_config_read (); +extern int script_config_write (); +extern void script_config_free (); + +#endif /* __WEECHAT_SCRIPT_CONFIG_H */ diff --git a/src/plugins/script/script-info.c b/src/plugins/script/script-info.c new file mode 100644 index 000000000..c04959223 --- /dev/null +++ b/src/plugins/script/script-info.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * script-info.c: info, infolist and hdata hooks for script plugin + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "../weechat-plugin.h" +#include "script.h" +#include "script-repo.h" + + +/* + * script_info_get_infolist_cb: callback called when script infolist is asked + */ + +struct t_infolist * +script_info_get_infolist_cb (void *data, const char *infolist_name, + void *pointer, const char *arguments) +{ + struct t_infolist *ptr_infolist; + struct t_repo_script *ptr_script; + + /* make C compiler happy */ + (void) data; + + if (!infolist_name || !infolist_name[0]) + return NULL; + + if (weechat_strcasecmp (infolist_name, "script_script") == 0) + { + if (pointer && !script_repo_script_valid (pointer)) + return NULL; + + ptr_infolist = weechat_infolist_new (); + if (ptr_infolist) + { + if (pointer) + { + /* build list with only one script */ + if (!script_repo_add_to_infolist (ptr_infolist, pointer)) + { + weechat_infolist_free (ptr_infolist); + return NULL; + } + return ptr_infolist; + } + else + { + /* build list with all scripts matching arguments */ + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + if (!arguments || !arguments[0] + || weechat_string_match (ptr_script->name_with_extension, + arguments, 0)) + { + if (!script_repo_add_to_infolist (ptr_infolist, ptr_script)) + { + weechat_infolist_free (ptr_infolist); + return NULL; + } + } + } + return ptr_infolist; + } + } + } + + return NULL; +} + +/* + * script_info_init: initialize info, infolist and hdata hooks for script plugin + */ + +void +script_info_init () +{ + /* infolist hooks */ + weechat_hook_infolist ("script_script", + N_("list of scripts"), + N_("script pointer (optional)"), + N_("script name with extension (can start or end with \"*\" as wildcard) (optional)"), + &script_info_get_infolist_cb, NULL); + + /* hdata hooks */ + weechat_hook_hdata ("script_script", N_("scripts from repository"), + &script_repo_hdata_script_cb, NULL); +} diff --git a/src/plugins/script/script-info.h b/src/plugins/script/script-info.h new file mode 100644 index 000000000..2e2811d90 --- /dev/null +++ b/src/plugins/script/script-info.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_SCRIPT_INFO_H +#define __WEECHAT_SCRIPT_INFO_H 1 + +extern void script_info_init (); + +#endif /* __WEECHAT_SCRIPT_INFO_H */ diff --git a/src/plugins/script/script-repo.c b/src/plugins/script/script-repo.c new file mode 100644 index 000000000..12a326c6e --- /dev/null +++ b/src/plugins/script/script-repo.c @@ -0,0 +1,1369 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * script-repo.c: download and read repository file (plugins.xml.gz) + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <stdio.h> +#include <time.h> +#include <zlib.h> +#include <gcrypt.h> + +#include "../weechat-plugin.h" +#include "script.h" +#include "script-repo.h" +#include "script-action.h" +#include "script-buffer.h" +#include "script-config.h" + + +struct t_repo_script *repo_scripts = NULL; +struct t_repo_script *last_repo_script = NULL; +int script_repo_count = 0; +int script_repo_count_displayed = 0; +struct t_hashtable *script_repo_max_length_field = NULL; +char *script_repo_filter = NULL; + + +/* + * script_repo_script_valid: check if a script pointer exists + * return 1 if script exists + * 0 if script is not found + */ + +int +script_repo_script_valid (struct t_repo_script *script) +{ + struct t_repo_script *ptr_script; + + if (!script) + return 0; + + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + if (ptr_script == script) + return 1; + } + + /* script not found */ + return 0; +} + +/* + * script_repo_search_displayed_by_number: search a script displayed by number + * (first script displayed is 0) + */ + +struct t_repo_script * +script_repo_search_displayed_by_number (int number) +{ + struct t_repo_script *ptr_script; + int i; + + if (number < 0) + return NULL; + + i = 0; + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + if (ptr_script->displayed) + { + if (i == number) + return ptr_script; + i++; + } + } + + /* script not found */ + return NULL; +} + +/* + * script_repo_search_by_name: search a script by name + * (example: "iset") + */ + +struct t_repo_script * +script_repo_search_by_name (const char *name) +{ + struct t_repo_script *ptr_script; + + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + if (strcmp (ptr_script->name, name) == 0) + return ptr_script; + } + + /* script not found */ + return NULL; +} + +/* + * script_repo_search_by_name_ext: search a script by name/extension + * (example: "iset.pl") + */ + +struct t_repo_script * +script_repo_search_by_name_ext (const char *name_with_extension) +{ + struct t_repo_script *ptr_script; + + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + if (strcmp (ptr_script->name_with_extension, name_with_extension) == 0) + return ptr_script; + } + + /* script not found */ + return NULL; +} + +/* + * script_repo_get_status_for_display: get status for display + * list is the codes of status to display + * (exemple: "*iHrN" for all status) + */ + +const char * +script_repo_get_status_for_display (struct t_repo_script *script, + const char *list, + int collapse) +{ + static char str_status[128]; + const char *ptr_list; + char str_space[2]; + + str_space[0] = (collapse) ? '\0' : ' '; + str_space[1] = '\0'; + + str_status[0] = '\0'; + + for (ptr_list = list; ptr_list[0]; ptr_list++) + { + switch (ptr_list[0]) + { + case '*': + strcat (str_status, weechat_color (weechat_config_string (script_config_color_status_popular))); + strcat (str_status, (script && (script->popularity > 0)) ? "*" : str_space); + break; + case 'i': + strcat (str_status, weechat_color (weechat_config_string (script_config_color_status_installed))); + strcat (str_status, (script && (script->status & SCRIPT_STATUS_INSTALLED)) ? "i" : str_space); + break; + case '?': + strcat (str_status, weechat_color (weechat_config_string (script_config_color_status_unknown))); + strcat (str_status, (script) ? str_space : "?"); + break; + case 'H': + strcat (str_status, weechat_color (weechat_config_string (script_config_color_status_held))); + strcat (str_status, (script && (script->status & SCRIPT_STATUS_HELD)) ? "H" : str_space); + break; + case 'r': + strcat (str_status, weechat_color (weechat_config_string (script_config_color_status_running))); + strcat (str_status, (script && (script->status & SCRIPT_STATUS_RUNNING)) ? "r" : str_space); + break; + case 'N': + strcat (str_status, weechat_color (weechat_config_string (script_config_color_status_obsolete))); + strcat (str_status, (script && (script->status & SCRIPT_STATUS_NEW_VERSION)) ? "N" : str_space); + break; + } + } + + return str_status; +} + +/* + * script_repo_alloc: allocate a script structure + */ + +struct t_repo_script * +script_repo_alloc () +{ + struct t_repo_script *new_script; + + new_script = malloc (sizeof (*new_script)); + if (new_script) + { + new_script->name = NULL; + new_script->name_with_extension = NULL; + new_script->language = -1; + new_script->author = NULL; + new_script->mail = NULL; + new_script->version = NULL; + new_script->license = NULL; + new_script->description = NULL; + new_script->tags = NULL; + new_script->requirements = NULL; + new_script->min_weechat = NULL; + new_script->max_weechat = NULL; + new_script->md5sum = NULL; + new_script->url = NULL; + new_script->popularity = 0; + new_script->date_added = 0; + new_script->date_updated = 0; + new_script->status = 0; + new_script->version_loaded = NULL; + new_script->displayed = 1; + new_script->install_order = 0; + new_script->prev_script = NULL; + new_script->next_script = NULL; + } + + return new_script; +} + +/* + * script_repo_compare_scripts: compare two scripts using sort key(s) + * (from option script.look.sort) + */ + +int +script_repo_compare_scripts (struct t_repo_script *script1, + struct t_repo_script *script2) +{ + const char *ptr_sort; + int cmp, reverse; + + reverse = 1; + ptr_sort = weechat_config_string (script_config_look_sort); + while (ptr_sort[0]) + { + cmp = 0; + switch (ptr_sort[0]) + { + case '-': /* reverse order */ + reverse = -1; + break; + case 'a': /* author */ + cmp = strcmp (script1->author, script2->author); + break; + case 'd': /* date added */ + if (script1->date_added > script2->date_added) + cmp = -1; + else if (script1->date_added < script2->date_added) + cmp = 1; + break; + case 'e': /* extension */ + cmp = strcmp (script_extension[script1->language], + script_extension[script2->language]); + break; + case 'i': /* status "installed" */ + if ((script1->status & SCRIPT_STATUS_INSTALLED) + && !(script2->status & SCRIPT_STATUS_INSTALLED)) + cmp = -1; + else if (!(script1->status & SCRIPT_STATUS_INSTALLED) + && (script2->status & SCRIPT_STATUS_INSTALLED)) + cmp = 1; + break; + case 'l': /* language */ + cmp = strcmp (script_language[script1->language], + script_language[script2->language]); + break; + case 'n': /* name */ + cmp = strcmp (script1->name, script2->name); + break; + case 'o': /* status "new version" (script obsolete) */ + if ((script1->status & SCRIPT_STATUS_NEW_VERSION) + && !(script2->status & SCRIPT_STATUS_NEW_VERSION)) + cmp = -1; + else if (!(script1->status & SCRIPT_STATUS_NEW_VERSION) + && (script2->status & SCRIPT_STATUS_NEW_VERSION)) + cmp = 1; + break; + case 'p': /* popularity */ + if (script1->popularity > script2->popularity) + cmp = -1; + else if (script1->popularity < script2->popularity) + cmp = 1; + break; + case 'r': /* status "running" */ + if ((script1->status & SCRIPT_STATUS_RUNNING) + && !(script2->status & SCRIPT_STATUS_RUNNING)) + cmp = -1; + else if (!(script1->status & SCRIPT_STATUS_RUNNING) + && (script2->status & SCRIPT_STATUS_RUNNING)) + cmp = 1; + break; + case 'u': /* date updated */ + if (script1->date_updated > script2->date_updated) + cmp = -1; + else if (script1->date_updated < script2->date_updated) + cmp = 1; + break; + default: + reverse = 1; + break; + } + if (cmp != 0) + return cmp * reverse; + ptr_sort++; + } + + return 0; +} + +/* + * script_repo_find_pos: find position for script in list + */ + +struct t_repo_script * +script_repo_find_pos (struct t_repo_script *script) +{ + struct t_repo_script *ptr_script; + + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + if (script_repo_compare_scripts (ptr_script, script) > 0) + return ptr_script; + } + + /* position not found, add to the end */ + return NULL; +} + +/* + * script_repo_set_max_length_field: set max length for a field in hashtable + * "script_repo_max_length_field" + */ + +void +script_repo_set_max_length_field (const char *field, int length) +{ + int *value; + + value = weechat_hashtable_get (script_repo_max_length_field, field); + if (!value || (length > *value)) + weechat_hashtable_set (script_repo_max_length_field, field, &length); +} + +/* + * script_repo_add: add script to list of scripts + */ + +void +script_repo_add (struct t_repo_script *script) +{ + struct t_repo_script *ptr_script; + + ptr_script = script_repo_find_pos (script); + if (ptr_script) + { + /* insert script before script found */ + script->prev_script = ptr_script->prev_script; + script->next_script = ptr_script; + if (ptr_script->prev_script) + (ptr_script->prev_script)->next_script = script; + else + repo_scripts = script; + ptr_script->prev_script = script; + } + else + { + /* add script to the end */ + script->prev_script = last_repo_script; + script->next_script = NULL; + if (repo_scripts) + last_repo_script->next_script = script; + else + repo_scripts = script; + last_repo_script = script; + } + + /* set max length for fields */ + if (script->name) + script_repo_set_max_length_field ("N", weechat_utf8_strlen_screen (script->name)); + if (script->name_with_extension) + script_repo_set_max_length_field ("n", weechat_utf8_strlen_screen (script->name_with_extension)); + if (script->language >= 0) + { + script_repo_set_max_length_field ("l", weechat_utf8_strlen_screen (script_language[script->language])); + script_repo_set_max_length_field ("e", weechat_utf8_strlen_screen (script_extension[script->language])); + } + if (script->author) + script_repo_set_max_length_field ("a", weechat_utf8_strlen_screen (script->author)); + if (script->version) + script_repo_set_max_length_field ("v", weechat_utf8_strlen_screen (script->version)); + if (script->version_loaded) + script_repo_set_max_length_field ("V", weechat_utf8_strlen_screen (script->version_loaded)); + if (script->license) + script_repo_set_max_length_field ("L", weechat_utf8_strlen_screen (script->license)); + if (script->description) + script_repo_set_max_length_field ("d", weechat_utf8_strlen_screen (script->description)); + if (script->tags) + script_repo_set_max_length_field ("t", weechat_utf8_strlen_screen (script->tags)); + if (script->requirements) + script_repo_set_max_length_field ("r", weechat_utf8_strlen_screen (script->requirements)); + if (script->min_weechat) + script_repo_set_max_length_field ("w", weechat_utf8_strlen_screen (script->min_weechat)); + if (script->max_weechat) + script_repo_set_max_length_field ("W", weechat_utf8_strlen_screen (script->max_weechat)); + + script_repo_count++; + if (script->displayed) + script_repo_count_displayed++; +} + +/* + * script_repo_free: free data in script + */ + +void +script_repo_free (struct t_repo_script *script) +{ + if (script->name) + free (script->name); + if (script->name_with_extension) + free (script->name_with_extension); + if (script->author) + free (script->author); + if (script->mail) + free (script->mail); + if (script->version) + free (script->version); + if (script->license) + free (script->license); + if (script->description) + free (script->description); + if (script->tags) + free (script->tags); + if (script->requirements) + free (script->requirements); + if (script->min_weechat) + free (script->min_weechat); + if (script->max_weechat) + free (script->max_weechat); + if (script->md5sum) + free (script->md5sum); + if (script->url) + free (script->url); + if (script->version_loaded) + free (script->version_loaded); + + free (script); + } + +/* + * script_repo_remove: remove a script from list + */ + +void +script_repo_remove (struct t_repo_script *script) +{ + struct t_repo_script *new_repo_scripts; + + /* remove script from list */ + if (last_repo_script == script) + last_repo_script = script->prev_script; + if (script->prev_script) + { + (script->prev_script)->next_script = script->next_script; + new_repo_scripts = repo_scripts; + } + else + new_repo_scripts = script->next_script; + if (script->next_script) + (script->next_script)->prev_script = script->prev_script; + + /* free data */ + if (script->displayed) + script_repo_count_displayed--; + script_repo_free (script); + + repo_scripts = new_repo_scripts; + + script_repo_count--; + + if (script_buffer_selected_line >= script_repo_count_displayed) + { + script_buffer_selected_line = (script_repo_count_displayed == 0) ? + 0 : script_repo_count_displayed - 1; + } +} + +/* + * script_repo_remove_all: remove all scripts from list + */ + +void +script_repo_remove_all () +{ + while (repo_scripts) + { + script_repo_remove (repo_scripts); + } + if (script_repo_max_length_field) + { + weechat_hashtable_free (script_repo_max_length_field); + script_repo_max_length_field = NULL; + } +} + +/* + * script_repo_script_is_held: return 1 if script is held, 0 otherwise + */ + +int +script_repo_script_is_held (struct t_repo_script *script) +{ + const char *hold; + char *pos; + int length; + + hold = weechat_config_string (script_config_scripts_hold); + length = strlen (script->name_with_extension); + pos = strstr (hold, script->name_with_extension); + while (pos) + { + if (((pos == hold) || (*(pos - 1) == ',')) + && ((pos[length] == ',') || !pos[length])) + { + /* script held */ + return 1; + } + pos = strstr (pos + 1, script->name_with_extension); + } + + /* script not held */ + return 0; +} + +/* + * script_repo_md5sum_file: return MD5 checksum for content of a file + * Note: result has to be free() after use + */ + +char * +script_repo_md5sum_file (const char *filename) +{ + struct stat st; + FILE *file; + char md5sum[512]; + const char *hexa = "0123456789abcdef"; + unsigned char *data, *result; + gcry_md_hd_t hd; + int mdlen, i; + + md5sum[0] = '\0'; + + if (stat (filename, &st) == -1) + return NULL; + + data = malloc (st.st_size); + if (!data) + return NULL; + + file = fopen (filename, "r"); + if ((int)fread (data, 1, st.st_size, file) < st.st_size) + { + free (data); + return NULL; + } + fclose (file); + + gcry_md_open (&hd, GCRY_MD_MD5, 0); + mdlen = gcry_md_get_algo_dlen (GCRY_MD_MD5); + gcry_md_write (hd, data, st.st_size); + result = gcry_md_read (hd, GCRY_MD_MD5); + for (i = 0; i < mdlen; i++) + { + md5sum[i * 2] = hexa[(result[i] & 0xFF) / 16]; + md5sum[(i * 2) + 1] = hexa[(result[i] & 0xFF) % 16]; + } + md5sum[((mdlen - 1) * 2) + 2] = '\0'; + gcry_md_close (hd); + + free (data); + + return strdup (md5sum); +} + +/* + * script_repo_update_status: update status of a script, which are: + * - script installed? + * - script running? + * - new version available? + */ + +void +script_repo_update_status (struct t_repo_script *script) +{ + const char *weechat_home, *version; + char *filename, *md5sum; + struct stat st; + int length; + struct t_repo_script *ptr_script; + + script->status = 0; + md5sum = NULL; + + /* check if script is installed (file found on disk) */ + weechat_home = weechat_info_get ("weechat_dir", NULL); + length = strlen (weechat_home) + strlen (script->name_with_extension) + 64; + filename = malloc (length); + if (filename) + { + snprintf (filename, length, "%s/%s/autoload/%s", + weechat_home, + script_language[script->language], + script->name_with_extension); + if (stat (filename, &st) == 0) + { + script->status |= SCRIPT_STATUS_INSTALLED; + md5sum = script_repo_md5sum_file (filename); + } + else + { + snprintf (filename, length, "%s/%s/%s", + weechat_home, + script_language[script->language], + script->name_with_extension); + if (stat (filename, &st) == 0) + { + script->status |= SCRIPT_STATUS_INSTALLED; + md5sum = script_repo_md5sum_file (filename); + } + } + free (filename); + } + + /* check if script is held */ + if (script_repo_script_is_held (script)) + script->status |= SCRIPT_STATUS_HELD; + + /* check if script is running (loaded) */ + version = weechat_hashtable_get (script_loaded, script->name_with_extension); + if (version) + { + script->status |= SCRIPT_STATUS_RUNNING; + if (script->version_loaded) + free (script->version_loaded); + script->version_loaded = strdup (version); + } + else + { + if (script->version_loaded) + { + free (script->version_loaded); + script->version_loaded = NULL; + } + } + + /* check if script has new version (script is obsolete) */ + if (md5sum && script->md5sum && (strcmp (script->md5sum, md5sum) != 0)) + script->status |= SCRIPT_STATUS_NEW_VERSION; + + /* recompute max length for version loaded (for display) */ + if (script_repo_max_length_field) + { + length = 0; + weechat_hashtable_set (script_repo_max_length_field, "V", &length); + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + if (ptr_script->version_loaded) + script_repo_set_max_length_field ("V", weechat_utf8_strlen_screen (ptr_script->version_loaded)); + } + } + + if (md5sum) + free (md5sum); +} + +/* + * script_repo_update_status_all: update status of all scripts + */ + +void +script_repo_update_status_all () +{ + struct t_repo_script *ptr_script; + + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + script_repo_update_status (ptr_script); + } +} + +/* + * script_repo_file_exists: return 1 if repository file (plugins.xml.gz) exists + * otherwise 0 + */ + +int +script_repo_file_exists () +{ + char *filename; + int rc; + struct stat st; + + filename = script_config_get_xml_filename(); + if (!filename) + return 0; + + rc = 0; + if (stat (filename, &st) == 0) + rc = 1; + + free (filename); + + return rc; +} + +/* + * script_repo_file_is_uptodate: return 1 if repository file (plugins.xml.gz) + * is up-to-date (file exists and is not outdated) + * otherwise 0 (file has to be downloaded) + */ + +int +script_repo_file_is_uptodate () +{ + char *filename; + struct stat st; + int cache_expire; + time_t current_time; + + cache_expire = weechat_config_integer (script_config_scripts_cache_expire); + + /* cache always expires? => NOT up-to-date */ + if (cache_expire == 0) + return 0; + + filename = script_config_get_xml_filename (); + + /* filename not found? => NOT up-to-date */ + if (!filename) + return 0; + + /* file does not exist? => NOT up-to-date */ + if (stat (filename, &st) == -1) + { + free (filename); + return 0; + } + + /* cache never expires? => OK, up-to-date! */ + if (cache_expire < 0) + { + free (filename); + return 1; + } + + current_time = time (NULL); + + /* cache has expired? => NOT up-to-date */ + if (current_time > st.st_mtime + (cache_expire * 60)) + { + free (filename); + return 0; + } + + /* OK, up-to-date! */ + free (filename); + return 1; +} + +/* + * script_repo_file_read: read scripts in repository file (plugins.xml.gz) + * return 1 if ok, 0 if error + */ + +int +script_repo_file_read (int quiet) +{ + char *filename, *ptr_line, line[4096], *pos, *pos2, *pos3; + char *name, *value1, *value2, *value3, *value, *error; + const char *version; + gzFile file; + struct t_repo_script *script; + int version_number, version_ok, script_ok, length; + struct tm tm_script; + + script_get_loaded_scripts (); + + script_repo_remove_all (); + + if (!script_repo_max_length_field) + { + script_repo_max_length_field = weechat_hashtable_new (16, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_INTEGER, + NULL, + NULL); + } + else + weechat_hashtable_remove_all (script_repo_max_length_field); + + if (script_repo_filter) + { + free (script_repo_filter); + script_repo_filter = NULL; + } + + version = weechat_info_get ("version", NULL); + version_number = weechat_util_version_number (version); + + filename = script_config_get_xml_filename (); + if (!filename) + { + weechat_printf (NULL, _("%s%s: error reading list of scripts"), + weechat_prefix ("error"), + SCRIPT_PLUGIN_NAME); + return 0; + } + + script = NULL; + file = gzopen (filename, "r"); + free (filename); + if (!file) + { + weechat_printf (NULL, _("%s%s: error reading list of scripts"), + weechat_prefix ("error"), + SCRIPT_PLUGIN_NAME); + return 0; + } + + while (!gzeof (file)) + { + ptr_line = gzgets (file, line, sizeof (line) - 1); + if (ptr_line) + { + if (strstr (ptr_line, "<plugin id=")) + { + script = script_repo_alloc (); + } + else if (strstr (ptr_line, "</plugin>")) + { + if (script) + { + script_ok = 0; + if (script->name && (script->language >= 0)) + { + version_ok = 1; + if (script->min_weechat) + { + if (weechat_util_version_number (script->min_weechat) > version_number) + version_ok = 0; + } + if (version_ok && script->max_weechat) + { + if (weechat_util_version_number (script->max_weechat) < version_number) + version_ok = 0; + } + if (version_ok) + { + length = strlen (script->name) + 1 + + strlen (script_extension[script->language]) + 1; + script->name_with_extension = malloc (length); + if (script->name_with_extension) + { + snprintf (script->name_with_extension, + length, + "%s.%s", + script->name, + script_extension[script->language]); + } + script_repo_update_status (script); + script_repo_add (script); + script_ok = 1; + } + } + if (!script_ok) + { + script_repo_free (script); + } + script = NULL; + } + } + else if (script) + { + pos = strchr (ptr_line, '<'); + if (pos) + { + pos2 = strchr (pos + 1, '>'); + if (pos2 && (pos2 > pos + 1)) + { + pos3 = strstr (pos2 + 1, "</"); + if (pos3 && (pos3 > pos2 + 1)) + { + name = weechat_strndup (pos + 1, pos2 - pos - 1); + value1 = weechat_strndup (pos2 + 1, pos3 - pos2 - 1); + value2 = weechat_string_replace (value1, "&", "&"); + value3 = weechat_string_replace (value2, ">", ">"); + value = weechat_string_replace (value3, "<", "<"); + if (name && value) + { + if (strcmp (name, "name") == 0) + script->name = strdup (value); + else if (strcmp (name, "language") == 0) + script->language = script_language_search (value); + else if (strcmp (name, "author") == 0) + script->author = strdup (value); + else if (strcmp (name, "mail") == 0) + script->mail = strdup (value); + else if (strcmp (name, "version") == 0) + script->version = strdup (value); + else if (strcmp (name, "license") == 0) + script->license = strdup (value); + else if (strcmp (name, "desc_en") == 0) + script->description = strdup (value); + else if (strcmp (name, "tags") == 0) + script->tags = strdup (value); + else if (strcmp (name, "requirements") == 0) + script->requirements = strdup (value); + else if (strcmp (name, "min_weechat") == 0) + script->min_weechat = strdup (value); + else if (strcmp (name, "max_weechat") == 0) + script->max_weechat = strdup (value); + else if (strcmp (name, "md5sum") == 0) + script->md5sum = strdup (value); + else if (strcmp (name, "url") == 0) + script->url = strdup (value); + else if (strcmp (name, "popularity") == 0) + { + error = NULL; + script->popularity = (int)strtol (value, + &error, + 10); + if (!error || error[0]) + script->popularity = 0; + } + else if (strcmp (name, "added") == 0) + { + /* + * initialize structure, because strptime + * does not do it + */ + memset (&tm_script, 0, sizeof (tm_script)); + error = strptime (value, + "%Y-%m-%d %H:%M:%S", + &tm_script); + if (error && !error[0]) + script->date_added = mktime (&tm_script); + } + else if (strcmp (name, "updated") == 0) + { + /* + * initialize structure, because strptime + * does not do it + */ + memset (&tm_script, 0, sizeof (tm_script)); + error = strptime (value, + "%Y-%m-%d %H:%M:%S", + &tm_script); + if (error && !error[0]) + script->date_updated = mktime (&tm_script); + } + } + if (name) + free (name); + if (value1) + free (value1); + if (value2) + free (value2); + if (value3) + free (value3); + if (value) + free (value); + } + } + } + } + } + } + + gzclose (file); + + if (repo_scripts && !quiet) + { + weechat_printf (NULL, + _("%s: %d scripts for WeeChat %s"), + SCRIPT_PLUGIN_NAME, script_repo_count, + weechat_info_get ("version", NULL)); + } + + if (!repo_scripts) + { + weechat_printf (NULL, + _("%s%s: list of scripts is empty (repository file " + "is broken, or download has failed)"), + weechat_prefix ("error"), + SCRIPT_PLUGIN_NAME); + } + + return 1; +} + +/* + * script_repo_file_update_process_cb: callback called when list of scripts is + * downloaded + */ + +int +script_repo_file_update_process_cb (void *data, const char *command, + int return_code, const char *out, + const char *err) +{ + int quiet; + + /* make C compiler happy */ + (void) command; + + quiet = (data == 0) ? 0 : 1; + + if (return_code >= 0) + { + if ((err && err[0]) || (out && (strncmp (out, "error:", 6) == 0))) + { + weechat_printf (NULL, + _("%s%s: error downloading list of scripts: %s"), + weechat_prefix ("error"), + SCRIPT_PLUGIN_NAME, + (err && err[0]) ? err : out + 6); + return WEECHAT_RC_OK; + } + + if (script_repo_file_read (quiet) && repo_scripts) + { + if (!script_action_run ()) + script_buffer_refresh (1); + } + else + script_buffer_refresh (1); + } + + return WEECHAT_RC_OK; +} + +/* + * script_repo_file_update: update repository file, and read it + */ + +void +script_repo_file_update (int quiet) +{ + char *filename, *url; + int length; + struct t_hashtable *options; + + script_repo_remove_all (); + + filename = script_config_get_xml_filename (); + if (!filename) + return; + + options = weechat_hashtable_new (8, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, + NULL); + if (options) + { + length = 4 + strlen (weechat_config_string (script_config_scripts_url)) + 1; + url = malloc (length); + if (url) + { + if (!quiet) + { + weechat_printf (NULL, + _("%s: downloading list of scripts..."), + SCRIPT_PLUGIN_NAME); + } + + snprintf (url, length, "url:%s", + weechat_config_string (script_config_scripts_url)); + weechat_hashtable_set (options, "file_out", filename); + weechat_hook_process_hashtable (url, options, 30000, + &script_repo_file_update_process_cb, + (quiet) ? (void *)1 : (void *)0); + free (url); + } + weechat_hashtable_free (options); + } + + free (filename); +} + +/* + * script_repo_match_search: return 1 if script is matching search string, + * otherwise 0 + */ + +int +script_repo_match_search (struct t_repo_script *script, const char *search) +{ + char **words, **tags; + int num_words, num_tags, has_tag, match, i, j; + + if (strcmp (search, "*") == 0) + return 1; + + words = weechat_string_split (search, " ", 0, 0, &num_words); + tags = weechat_string_split ((script->tags) ? script->tags : "", ",", 0, 0, + &num_tags); + if (words) + { + for (i = 0; i < num_words; i++) + { + has_tag = 0; + if (tags) + { + for (j = 0; j < num_tags; j++) + { + if (weechat_strcasecmp (tags[j], words[i]) == 0) + { + has_tag = 1; + break; + } + } + } + if (!has_tag) + { + match = 0; + if (script->name_with_extension + && weechat_strcasestr (script->name_with_extension, words[i])) + match = 1; + + if (!match && script->description + && weechat_strcasestr (script->description, words[i])) + match = 1; + + if (!match && script->license + && weechat_strcasestr (script->license, words[i])) + match = 1; + + if (!match && script->author + && weechat_strcasestr (script->author, words[i])) + match = 1; + + if (!match) + { + weechat_string_free_split (words); + weechat_string_free_split (tags); + return 0; + } + } + } + } + + if (words) + weechat_string_free_split (words); + if (tags) + weechat_string_free_split (tags); + + return 1; +} + +/* + * script_repo_filter_scripts: filter scripts (search string in + * name/description/tags) and mark scripts found as + * "displayed" (0 in displayed for non-matching + * scripts) + */ + +void +script_repo_filter_scripts (const char *search) +{ + struct t_repo_script *ptr_script; + + if (script_repo_filter) + free (script_repo_filter); + script_repo_filter = strdup (search); + + script_repo_count_displayed = 0; + + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + ptr_script->displayed = (script_repo_match_search (ptr_script, search)); + if (ptr_script->displayed) + script_repo_count_displayed++; + } + + script_buffer_refresh (1); +} + +/* + * script_repo_hdata_script_cb: return hdata for script + */ + +struct t_hdata * +script_repo_hdata_script_cb (void *data, const char *hdata_name) +{ + struct t_hdata *hdata; + + /* make C compiler happy */ + (void) data; + + hdata = weechat_hdata_new (hdata_name, "prev_script", "next_script"); + if (hdata) + { + WEECHAT_HDATA_VAR(struct t_repo_script, name, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, name_with_extension, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, language, INTEGER, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, author, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, mail, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, version, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, license, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, description, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, tags, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, requirements, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, min_weechat, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, max_weechat, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, md5sum, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, url, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, popularity, INTEGER, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, date_added, TIME, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, date_updated, TIME, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, status, INTEGER, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, version_loaded, STRING, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, displayed, INTEGER, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, install_order, INTEGER, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_repo_script, prev_script, POINTER, NULL, hdata_name); + WEECHAT_HDATA_VAR(struct t_repo_script, next_script, POINTER, NULL, hdata_name); + WEECHAT_HDATA_LIST(repo_scripts); + WEECHAT_HDATA_LIST(last_repo_script); + } + return hdata; +} + +/* + * script_repo_add_to_infolist: add a script in an infolist + * return 1 if ok, 0 if error + */ + +int +script_repo_add_to_infolist (struct t_infolist *infolist, + struct t_repo_script *script) +{ + struct t_infolist_item *ptr_item; + + if (!infolist || !script) + return 0; + + ptr_item = weechat_infolist_new_item (infolist); + if (!ptr_item) + return 0; + + if (!weechat_infolist_new_var_string (ptr_item, "name", script->name)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "name_with_extension", script->name_with_extension)) + return 0; + if (!weechat_infolist_new_var_integer (ptr_item, "language", script->language)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "author", script->author)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "mail", script->mail)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "version", script->version)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "license", script->license)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "description", script->description)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "tags", script->tags)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "requirements", script->requirements)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "min_weechat", script->min_weechat)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "max_weechat", script->max_weechat)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "md5sum", script->md5sum)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "url", script->url)) + return 0; + if (!weechat_infolist_new_var_integer (ptr_item, "popularity", script->popularity)) + return 0; + if (!weechat_infolist_new_var_time (ptr_item, "date_added", script->date_added)) + return 0; + if (!weechat_infolist_new_var_time (ptr_item, "date_updated", script->date_updated)) + return 0; + if (!weechat_infolist_new_var_integer (ptr_item, "status", script->status)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "version_loaded", script->version_loaded)) + return 0; + if (!weechat_infolist_new_var_integer (ptr_item, "displayed", script->displayed)) + return 0; + if (!weechat_infolist_new_var_integer (ptr_item, "install_order", script->install_order)) + return 0; + + return 1; +} + +/* + * script_repo_print_log: print script infos in log (usually for crash dump) + */ + +void +script_repo_print_log () +{ + struct t_repo_script *ptr_script; + + for (ptr_script = repo_scripts; ptr_script; + ptr_script = ptr_script->next_script) + { + weechat_log_printf (""); + weechat_log_printf ("[script (addr:0x%lx)]", ptr_script); + weechat_log_printf (" name. . . . . . . . . : '%s'", ptr_script->name); + weechat_log_printf (" name_with_extension . : '%s'", ptr_script->name_with_extension); + weechat_log_printf (" language. . . . . . . : %d", ptr_script->language); + weechat_log_printf (" author. . . . . . . . : '%s'", ptr_script->author); + weechat_log_printf (" mail. . . . . . . . . : '%s'", ptr_script->mail); + weechat_log_printf (" version . . . . . . . : '%s'", ptr_script->version); + weechat_log_printf (" license . . . . . . . : '%s'", ptr_script->license); + weechat_log_printf (" description . . . . . : '%s'", ptr_script->description); + weechat_log_printf (" tags. . . . . . . . . : '%s'", ptr_script->tags); + weechat_log_printf (" requirements. . . . . : '%s'", ptr_script->requirements); + weechat_log_printf (" min_weechat . . . . . : '%s'", ptr_script->min_weechat); + weechat_log_printf (" max_weechat . . . . . : '%s'", ptr_script->max_weechat); + weechat_log_printf (" md5sum. . . . . . . . : '%s'", ptr_script->md5sum); + weechat_log_printf (" url . . . . . . . . . : '%s'", ptr_script->url); + weechat_log_printf (" popularity. . . . . . : %d", ptr_script->popularity); + weechat_log_printf (" date_added. . . . . . : %ld", ptr_script->date_added); + weechat_log_printf (" date_updated. . . . . : %ld", ptr_script->date_updated); + weechat_log_printf (" status. . . . . . . . : %d (%s%s%s%s )", + ptr_script->status, + (ptr_script->status & SCRIPT_STATUS_INSTALLED) ? " installed": "", + (ptr_script->status & SCRIPT_STATUS_HELD) ? " held": "", + (ptr_script->status & SCRIPT_STATUS_RUNNING) ? " running": "", + (ptr_script->status & SCRIPT_STATUS_NEW_VERSION) ? " new_version": ""); + weechat_log_printf (" version_loaded. . . . : '%s'", ptr_script->version_loaded); + weechat_log_printf (" displayed . . . . . . : %d", ptr_script->displayed); + weechat_log_printf (" install_order . . . . : %d", ptr_script->install_order); + weechat_log_printf (" prev_script . . . . . : 0x%lx", ptr_script->prev_script); + weechat_log_printf (" next_script . . . . . : 0x%lx", ptr_script->next_script); + } +} diff --git a/src/plugins/script/script-repo.h b/src/plugins/script/script-repo.h new file mode 100644 index 000000000..9bd05e6c7 --- /dev/null +++ b/src/plugins/script/script-repo.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_SCRIPT_REPO_H +#define __WEECHAT_SCRIPT_REPO_H 1 + +/* status for script */ +#define SCRIPT_STATUS_INSTALLED 1 +#define SCRIPT_STATUS_HELD 2 +#define SCRIPT_STATUS_RUNNING 4 +#define SCRIPT_STATUS_NEW_VERSION 8 + +struct t_repo_script +{ + char *name; /* script name */ + char *name_with_extension; /* script name with extension */ + int language; /* language index */ + char *author; /* author */ + char *mail; /* author's mail */ + char *version; /* plugin version */ + char *license; /* license */ + char *description; /* description */ + char *tags; /* comma-separated list of tags */ + char *requirements; /* requirements */ + char *min_weechat; /* min WeeChat version */ + char *max_weechat; /* max WeeChat version */ + char *md5sum; /* md5sum of script content */ + char *url; /* URL to download script */ + int popularity; /* >0 for popular scripts only */ + time_t date_added; /* date added */ + time_t date_updated; /* date updated */ + int status; /* installed/running/new version */ + char *version_loaded; /* version of script loaded */ + int displayed; /* script displayed? */ + int install_order; /* order for install script (if >0)*/ + struct t_repo_script *prev_script; /* link to previous script */ + struct t_repo_script *next_script; /* link to next script */ +}; + +extern struct t_repo_script *repo_scripts; +extern struct t_repo_script *last_repo_script; +extern int script_repo_count, script_repo_count_displayed; +struct t_hashtable *script_repo_max_length_field; +extern char *script_repo_filter; + +extern int script_repo_script_valid (struct t_repo_script *script); +extern struct t_repo_script *script_repo_search_displayed_by_number (int number); +extern struct t_repo_script *script_repo_search_by_name (const char *name); +extern struct t_repo_script *script_repo_search_by_name_ext (const char *name_with_extension); +extern const char *script_repo_get_status_for_display (struct t_repo_script *script, + const char *list, + int collapse); +extern void script_repo_remove_all (); +extern void script_repo_update_status (struct t_repo_script *script); +extern void script_repo_update_status_all (); +extern int script_repo_file_exists (); +extern int script_repo_file_is_uptodate (); +extern int script_repo_file_read (int quiet); +extern void script_repo_file_update (int quiet); +extern void script_repo_filter_scripts (const char *search); +extern struct t_hdata *script_repo_hdata_script_cb (void *data, + const char *hdata_name); +extern int script_repo_add_to_infolist (struct t_infolist *infolist, + struct t_repo_script *script); +extern void script_repo_print_log (); + +#endif /* __WEECHAT_SCRIPT_REPO_H */ diff --git a/src/plugins/script/script.c b/src/plugins/script/script.c new file mode 100644 index 000000000..a52f9d670 --- /dev/null +++ b/src/plugins/script/script.c @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * script.c: scripts manager for WeeChat + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <libgen.h> + +#include "../weechat-plugin.h" +#include "script.h" +#include "script-buffer.h" +#include "script-command.h" +#include "script-completion.h" +#include "script-config.h" +#include "script-info.h" +#include "script-repo.h" + + +WEECHAT_PLUGIN_NAME(SCRIPT_PLUGIN_NAME); +WEECHAT_PLUGIN_DESCRIPTION(N_("Scripts manager")); +WEECHAT_PLUGIN_AUTHOR("Sebastien Helleu <flashcode@flashtux.org>"); +WEECHAT_PLUGIN_VERSION(WEECHAT_VERSION); +WEECHAT_PLUGIN_LICENSE(WEECHAT_LICENSE); + +struct t_weechat_plugin *weechat_script_plugin = NULL; + +char *script_language[] = { "guile", "lua", "perl", "python", "ruby", "tcl", NULL }; +char *script_extension[] = { "scm", "lua", "pl", "py", "rb", "tcl", NULL }; + +struct t_hashtable *script_loaded = NULL; +struct t_hook *script_timer_refresh = NULL; + + +/* + * script_language_search: search language and return index + * return -1 if not found + */ + +int +script_language_search (const char *language) +{ + int i; + + for (i = 0; script_language[i]; i++) + { + if (strcmp (script_language[i], language) == 0) + return i; + } + + /* language not found */ + return -1; +} + +/* + * script_language_search_by_extension: search language by extension and return + * index + * return -1 if not found + */ + +int +script_language_search_by_extension (const char *extension) +{ + int i; + + for (i = 0; script_extension[i]; i++) + { + if (strcmp (script_extension[i], extension) == 0) + return i; + } + + /* extension not found */ + return -1; +} + +/* + * script_get_loaded_scripts: get loaded scripts (in hashtable) + */ + +void +script_get_loaded_scripts () +{ + int i; + char hdata_name[128], *filename, *ptr_base_name; + const char *ptr_filename; + struct t_hdata *hdata; + void *ptr_script; + + if (!script_loaded) + { + script_loaded = weechat_hashtable_new (16, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, + NULL); + } + else + weechat_hashtable_remove_all (script_loaded); + + for (i = 0; script_language[i]; i++) + { + snprintf (hdata_name, sizeof (hdata_name), + "%s_script", script_language[i]); + hdata = weechat_hdata_get (hdata_name); + ptr_script = weechat_hdata_get_list (hdata, "scripts"); + while (ptr_script) + { + ptr_filename = weechat_hdata_string (hdata, ptr_script, "filename"); + if (ptr_filename) + { + filename = strdup (ptr_filename); + if (filename) + { + ptr_base_name = basename (filename); + weechat_hashtable_set (script_loaded, + ptr_base_name, + weechat_hdata_string (hdata, ptr_script, + "version")); + free (filename); + } + } + ptr_script = weechat_hdata_move (hdata, ptr_script, 1); + } + } +} + +/* + * script_debug_dump_cb: callback for "debug_dump" signal + */ + +int +script_debug_dump_cb (void *data, const char *signal, const char *type_data, + void *signal_data) +{ + /* make C compiler happy */ + (void) data; + (void) signal; + (void) type_data; + + if (!signal_data + || (weechat_strcasecmp ((char *)signal_data, SCRIPT_PLUGIN_NAME) == 0)) + { + weechat_log_printf (""); + weechat_log_printf ("***** \"%s\" plugin dump *****", + weechat_plugin->name); + + script_repo_print_log (); + + weechat_log_printf (""); + weechat_log_printf ("***** End of \"%s\" plugin dump *****", + weechat_plugin->name); + } + + return WEECHAT_RC_OK; +} + +/* + * script_timer_refresh_cb: callback for timer used to refresh list of scripts + */ + +int +script_timer_refresh_cb (void *data, int remaining_calls) +{ + /* make C compiler happy */ + (void) data; + + script_get_loaded_scripts (); + script_repo_update_status_all (); + script_buffer_refresh (0); + + if (remaining_calls == 0) + script_timer_refresh = NULL; + + return WEECHAT_RC_OK; +} + +/* + * script_signal_script_cb: callback for signals "xxx_script_yyy" + * (example: "python_script_loaded") + */ + +int +script_signal_script_cb (void *data, const char *signal, const char *type_data, + void *signal_data) +{ + /* make C compiler happy */ + (void) data; + (void) signal; + (void) type_data; + (void) signal_data; + + if (weechat_script_plugin->debug >= 2) + weechat_printf (NULL, "signal: %s, data: %s", signal, (char *)signal_data); + + if (!script_timer_refresh) + { + script_timer_refresh = weechat_hook_timer (50, 0, 1, + &script_timer_refresh_cb, NULL); + } + + return WEECHAT_RC_OK; +} + +/* + * weechat_plugin_init: initialize script plugin + */ + +int +weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[]) +{ + /* make C compiler happy */ + (void) argc; + (void) argv; + + weechat_plugin = plugin; + + script_buffer_set_callbacks (); + + if (!script_config_init ()) + return WEECHAT_RC_ERROR; + + if (script_config_read () < 0) + return WEECHAT_RC_ERROR; + + weechat_mkdir_home (SCRIPT_PLUGIN_NAME, 0755); + + script_command_init (); + script_completion_init (); + script_info_init (); + + weechat_hook_signal ("debug_dump", &script_debug_dump_cb, NULL); + weechat_hook_signal ("window_scrolled", &script_buffer_window_scrolled_cb, NULL); + weechat_hook_signal ("*_script_*", &script_signal_script_cb, NULL); + + if (script_repo_file_exists ()) + { + if (!script_repo_file_is_uptodate ()) + script_repo_file_update (0); + else + script_repo_file_read (0); + } + + if (script_buffer) + script_buffer_refresh (1); + + return WEECHAT_RC_OK; +} + +/* + * weechat_plugin_end: end script plugin + */ + +int +weechat_plugin_end (struct t_weechat_plugin *plugin) +{ + /* make C compiler happy */ + (void) plugin; + + script_config_write (); + + script_repo_remove_all (); + + if (script_repo_filter) + free (script_repo_filter); + + if (script_loaded) + weechat_hashtable_free (script_loaded); + + script_config_free (); + + return WEECHAT_RC_OK; +} diff --git a/src/plugins/script/script.h b/src/plugins/script/script.h new file mode 100644 index 000000000..e922c3011 --- /dev/null +++ b/src/plugins/script/script.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __WEECHAT_SCRIPT_H +#define __WEECHAT_SCRIPT_H 1 + +#define weechat_plugin weechat_script_plugin +#define SCRIPT_PLUGIN_NAME "script" + +extern struct t_weechat_plugin *weechat_script_plugin; + +extern char *script_language[]; +extern char *script_extension[]; +extern struct t_hashtable *script_loaded; + +extern int script_language_search (const char *language); +extern int script_language_search_by_extension (const char *extension); +extern void script_actions_add (const char *action); +extern void script_get_loaded_scripts (); + +#endif /* __WEECHAT_SCRIPT_H */ |