/* * plugin-script.c - common functions used by script plugins * * Copyright (C) 2003-2015 Sébastien Helleu * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include "weechat-plugin.h" #include "plugin-script.h" #include "plugin-script-callback.h" #define SCRIPT_OPTION_CHECK_LICENSE "check_license" int script_option_check_license = 0; /* * Reads script configuration. */ void plugin_script_config_read (struct t_weechat_plugin *weechat_plugin) { const char *string; string = weechat_config_get_plugin (SCRIPT_OPTION_CHECK_LICENSE); if (!string) { weechat_config_set_plugin (SCRIPT_OPTION_CHECK_LICENSE, "off"); string = weechat_config_get_plugin (SCRIPT_OPTION_CHECK_LICENSE); } if (string && (weechat_config_string_to_boolean (string) > 0)) script_option_check_license = 1; else script_option_check_license = 0; } /* * Callback for changes on configuration option. */ int plugin_script_config_cb (void *data, const char *option, const char *value) { /* make C compiler happy */ (void) option; (void) value; plugin_script_config_read (data); return WEECHAT_RC_OK; } /* * Creates directories for plugin in WeeChat home: * - ~/.weechat/XXX/ * - ~/.weechat/XXX/autoload/ */ void plugin_script_create_dirs (struct t_weechat_plugin *weechat_plugin) { char *string; int length; weechat_mkdir_home (weechat_plugin->name, 0755); length = strlen (weechat_plugin->name) + strlen ("/autoload") + 1; string = malloc (length); if (string) { snprintf (string, length, "%s/autoload", weechat_plugin->name); weechat_mkdir_home (string, 0755); free (string); } } /* * Initializes script plugin: * - reads configuration * - hooks config * - creates directories in WeeChat home * - hooks command, completion, hdata, infolist, signals * - parses arguments * - auto-loads scripts. */ void plugin_script_init (struct t_weechat_plugin *weechat_plugin, int argc, char *argv[], struct t_plugin_script_init *init) { char *string, *completion, signal_name[128]; char *action_signals[] = { "install", "remove", "autoload", NULL }; int length, i, auto_load_scripts; /* read script configuration */ plugin_script_config_read (weechat_plugin); /* add hook for configuration option */ length = strlen (weechat_plugin->name) + 64; string = malloc (length); if (string) { snprintf (string, length, "plugins.var.%s.%s", weechat_plugin->name, SCRIPT_OPTION_CHECK_LICENSE); weechat_hook_config (string, &plugin_script_config_cb, weechat_plugin); free (string); } /* create directories in WeeChat home */ plugin_script_create_dirs (weechat_plugin); /* add command */ completion = NULL; length = strlen (weechat_plugin->name) + 16; string = malloc (length); if (string) { snprintf (string, length, "%%(%s_script)", weechat_plugin->name); completion = weechat_string_replace ("list %s" " || listfull %s" " || load %(filename)" " || autoload" " || reload %s" " || unload %s", "%s", string); } weechat_hook_command ( weechat_plugin->name, N_("list/load/unload scripts"), N_("list|listfull []" " || load [-q] " " || autoload" " || reload|unload [-q] []"), N_(" list: list loaded scripts\n" "listfull: list loaded scripts (verbose)\n" " load: load a script\n" "autoload: load all scripts in \"autoload\" directory\n" " reload: reload a script (if no name given, unload all scripts, " "then load all scripts in \"autoload\" directory)\n" " unload: unload a script (if no name given, unload all scripts)\n" "filename: script (file) to load\n" " name: a script name (name used in call to \"register\" " "function)\n" " -q: quiet mode: do not display messages\n" "\n" "Without argument, this command lists all loaded scripts."), completion, init->callback_command, NULL); if (string) free (string); if (completion) free (completion); /* add completion, hdata and infolist */ length = strlen (weechat_plugin->name) + 64; string = malloc (length); if (string) { snprintf (string, length, "%s_script", weechat_plugin->name); weechat_hook_completion (string, N_("list of scripts"), init->callback_completion, NULL); weechat_hook_hdata (string, N_("list of scripts"), init->callback_hdata, weechat_plugin); weechat_hook_infolist (string, N_("list of scripts"), N_("script pointer (optional)"), N_("script name (wildcard \"*\" is allowed) (optional)"), init->callback_infolist, NULL); snprintf (string, length, "%s_callback", weechat_plugin->name); weechat_hook_hdata (string, N_("callback of a script"), &plugin_script_callback_hdata_callback_cb, weechat_plugin); free (string); } /* add signal for "debug_dump" */ weechat_hook_signal ("debug_dump", init->callback_signal_debug_dump, NULL); /* add signal for "debug_libs" */ weechat_hook_signal ("debug_libs", init->callback_signal_debug_libs, NULL); /* add signal for "buffer_closed" */ weechat_hook_signal ("buffer_closed", init->callback_signal_buffer_closed, NULL); /* add signals for script actions (install/remove/autoload) */ for (i = 0; action_signals[i]; i++) { snprintf (signal_name, sizeof (signal_name), "%s_script_%s", weechat_plugin->name, action_signals[i]); weechat_hook_signal (signal_name, init->callback_signal_script_action, NULL); } /* parse arguments */ auto_load_scripts = 1; for (i = 0; i < argc; i++) { if ((strcmp (argv[i], "-s") == 0) || (strcmp (argv[i], "--no-script") == 0)) { auto_load_scripts = 0; } } /* autoload scripts */ if (auto_load_scripts) { plugin_script_auto_load (weechat_plugin, init->callback_load_file); } } /* * Checks if a script pointer is valid. * * Returns: * 1: script exists * 0: script is not found */ int plugin_script_valid (struct t_plugin_script *scripts, struct t_plugin_script *script) { struct t_plugin_script *ptr_script; if (!scripts || !script) return 0; for (ptr_script = scripts; ptr_script; ptr_script = ptr_script->next_script) { if (ptr_script == script) return 1; } /* script not found */ return 0; } /* * Converts a pointer to a string for usage in a script. * * Returns string with format "0x12345678". * * Note: result must be freed after use. */ char * plugin_script_ptr2str (void *pointer) { char pointer_str[128]; if (!pointer) return strdup (""); snprintf (pointer_str, sizeof (pointer_str), "0x%lx", (long unsigned int)pointer); return strdup (pointer_str); } /* * Converts a string to pointer for usage outside a script. * * Format of "str_pointer" is "0x12345678". */ void * plugin_script_str2ptr (struct t_weechat_plugin *weechat_plugin, const char *script_name, const char *function_name, const char *str_pointer) { long unsigned int value; int rc; struct t_gui_buffer *ptr_buffer; if (!str_pointer || !str_pointer[0]) return NULL; if ((str_pointer[0] != '0') || (str_pointer[1] != 'x')) goto invalid; rc = sscanf (str_pointer + 2, "%lx", &value); if ((rc != EOF) && (rc >= 1)) return (void *)value; invalid: if ((weechat_plugin->debug >= 1) && script_name && function_name) { ptr_buffer = weechat_buffer_search_main (); if (ptr_buffer) { weechat_buffer_set (ptr_buffer, "print_hooks_enabled", "0"); weechat_printf (NULL, _("%s%s: warning, invalid pointer (\"%s\") for " "function \"%s\" (script: %s)"), weechat_prefix ("error"), weechat_plugin->name, str_pointer, function_name, script_name); weechat_buffer_set (ptr_buffer, "print_hooks_enabled", "1"); } } return NULL; } /* * Auto-loads all scripts in a directory. */ void plugin_script_auto_load (struct t_weechat_plugin *weechat_plugin, void (*callback)(void *data, const char *filename)) { const char *dir_home; char *dir_name; int dir_length; /* build directory, adding WeeChat home */ dir_home = weechat_info_get ("weechat_dir", ""); if (!dir_home) return; dir_length = strlen (dir_home) + strlen (weechat_plugin->name) + 16; dir_name = malloc (dir_length); if (!dir_name) return; snprintf (dir_name, dir_length, "%s/%s/autoload", dir_home, weechat_plugin->name); weechat_exec_on_files (dir_name, 0, NULL, callback); free (dir_name); } /* * Searches for a script by registered name (example: "iset"). * * Returns pointer to script, NULL if not found. */ struct t_plugin_script * plugin_script_search (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script *scripts, const char *name) { struct t_plugin_script *ptr_script; for (ptr_script = scripts; ptr_script; ptr_script = ptr_script->next_script) { if (weechat_strcasecmp (ptr_script->name, name) == 0) return ptr_script; } /* script not found */ return NULL; } /* * Searches for a script by full name (example: "iset.pl"). * * Returns pointer to script, NULL if not found. */ struct t_plugin_script * plugin_script_search_by_full_name (struct t_plugin_script *scripts, const char *full_name) { char *base_name; struct t_plugin_script *ptr_script; for (ptr_script = scripts; ptr_script; ptr_script = ptr_script->next_script) { base_name = basename (ptr_script->filename); if (strcmp (base_name, full_name) == 0) return ptr_script; } /* script not found */ return NULL; } /* * Searches for path name of a script. * * Note: result must be freed after use. */ char * plugin_script_search_path (struct t_weechat_plugin *weechat_plugin, const char *filename) { char *final_name; const char *dir_home, *dir_system; int length; struct stat st; if (filename[0] == '~') return weechat_string_expand_home (filename); dir_home = weechat_info_get ("weechat_dir", ""); if (dir_home) { /* try WeeChat user's autoload dir */ length = strlen (dir_home) + strlen (weechat_plugin->name) + 8 + strlen (filename) + 16; final_name = malloc (length); if (final_name) { snprintf (final_name, length, "%s/%s/autoload/%s", dir_home, weechat_plugin->name, filename); if ((stat (final_name, &st) == 0) && (st.st_size > 0)) return final_name; free (final_name); } /* try WeeChat language user's dir */ length = strlen (dir_home) + strlen (weechat_plugin->name) + strlen (filename) + 16; final_name = malloc (length); if (final_name) { snprintf (final_name, length, "%s/%s/%s", dir_home, weechat_plugin->name, filename); if ((stat (final_name, &st) == 0) && (st.st_size > 0)) return final_name; free (final_name); } /* try WeeChat user's dir */ length = strlen (dir_home) + strlen (filename) + 16; final_name = malloc (length); if (final_name) { snprintf (final_name, length, "%s/%s", dir_home, filename); if ((stat (final_name, &st) == 0) && (st.st_size > 0)) return final_name; free (final_name); } } /* try WeeChat system dir */ dir_system = weechat_info_get ("weechat_sharedir", ""); if (dir_system) { length = strlen (dir_system) + strlen (weechat_plugin->name) + strlen (filename) + 16; final_name = malloc (length); if (final_name) { snprintf (final_name,length, "%s/%s/%s", dir_system, weechat_plugin->name, filename); if ((stat (final_name, &st) == 0) && (st.st_size > 0)) return final_name; free (final_name); } } return strdup (filename); } /* * Searches for position of script in list (to keep list sorted on name). */ struct t_plugin_script * plugin_script_find_pos (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script *scripts, struct t_plugin_script *script) { struct t_plugin_script *ptr_script; for (ptr_script = scripts; ptr_script; ptr_script = ptr_script->next_script) { if (weechat_strcasecmp (script->name, ptr_script->name) < 0) return ptr_script; } return NULL; } /* * Inserts a script in list (keeping list sorted on name). */ void plugin_script_insert_sorted (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script **scripts, struct t_plugin_script **last_script, struct t_plugin_script *script) { struct t_plugin_script *pos_script; if (*scripts) { pos_script = plugin_script_find_pos (weechat_plugin, *scripts, script); if (pos_script) { /* insert script into the list (before script found) */ script->prev_script = pos_script->prev_script; script->next_script = pos_script; if (pos_script->prev_script) (pos_script->prev_script)->next_script = script; else *scripts = script; pos_script->prev_script = script; } else { /* add script to the end */ script->prev_script = *last_script; script->next_script = NULL; (*last_script)->next_script = script; *last_script = script; } } else { /* first script in list */ script->prev_script = NULL; script->next_script = NULL; *scripts = script; *last_script = script; } } /* * Adds a script to list of scripts. * * Returns pointer to new script, NULL if error. */ struct t_plugin_script * plugin_script_add (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script **scripts, struct t_plugin_script **last_script, const char *filename, const char *name, const char *author, const char *version, const char *license, const char *description, const char *shutdown_func, const char *charset) { struct t_plugin_script *new_script; if (!name[0] || strchr (name, ' ')) { weechat_printf (NULL, _("%s: error loading script \"%s\" (spaces or empty name " "not allowed)"), weechat_plugin->name, name); return NULL; } if (script_option_check_license && (weechat_strcmp_ignore_chars (weechat_plugin->license, license, "0123456789-.,/\\()[]{}", 0) != 0)) { weechat_printf (NULL, _("%s%s: warning, license \"%s\" for script \"%s\" " "differs from plugin license (\"%s\")"), weechat_prefix ("error"), weechat_plugin->name, license, name, weechat_plugin->license); } new_script = malloc (sizeof (*new_script)); if (new_script) { new_script->filename = strdup (filename); new_script->interpreter = NULL; new_script->name = strdup (name); new_script->author = strdup (author); new_script->version = strdup (version); new_script->license = strdup (license); new_script->description = strdup (description); new_script->shutdown_func = (shutdown_func) ? strdup (shutdown_func) : NULL; new_script->charset = (charset) ? strdup (charset) : NULL; new_script->callbacks = NULL; new_script->unloading = 0; plugin_script_insert_sorted (weechat_plugin, scripts, last_script, new_script); return new_script; } weechat_printf (NULL, _("%s: error loading script \"%s\" (not enough memory)"), weechat_plugin->name, name); return NULL; } /* * Restores buffers callbacks (input and close) for buffers created by script * plugin. */ void plugin_script_set_buffer_callbacks (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script *scripts, struct t_plugin_script *script, int (*callback_buffer_input) (void *data, struct t_gui_buffer *buffer, const char *input_data), int (*callback_buffer_close) (void *data, struct t_gui_buffer *buffer)) { struct t_infolist *infolist; struct t_gui_buffer *ptr_buffer; const char *script_name, *str_script_input_cb, *str_script_input_cb_data; const char *str_script_close_cb, *str_script_close_cb_data; struct t_plugin_script *ptr_script; struct t_plugin_script_cb *script_cb_input; struct t_plugin_script_cb *script_cb_close; infolist = weechat_infolist_get ("buffer", NULL, NULL); if (infolist) { while (weechat_infolist_next (infolist)) { if (weechat_infolist_pointer (infolist, "plugin") == weechat_plugin) { ptr_buffer = weechat_infolist_pointer (infolist, "pointer"); script_name = weechat_buffer_get_string (ptr_buffer, "localvar_script_name"); if (script_name && script_name[0]) { ptr_script = plugin_script_search (weechat_plugin, scripts, script_name); if (ptr_script && (ptr_script == script)) { str_script_input_cb = weechat_buffer_get_string (ptr_buffer, "localvar_script_input_cb"); str_script_input_cb_data = weechat_buffer_get_string (ptr_buffer, "localvar_script_input_cb_data"); str_script_close_cb = weechat_buffer_get_string (ptr_buffer, "localvar_script_close_cb"); str_script_close_cb_data = weechat_buffer_get_string (ptr_buffer, "localvar_script_close_cb_data"); if (str_script_input_cb && str_script_input_cb[0]) { script_cb_input = plugin_script_callback_add (ptr_script, str_script_input_cb, str_script_input_cb_data); if (script_cb_input) { script_cb_input->buffer = ptr_buffer; weechat_buffer_set_pointer (ptr_buffer, "input_callback", callback_buffer_input); weechat_buffer_set_pointer (ptr_buffer, "input_callback_data", script_cb_input); } } if (str_script_close_cb && str_script_close_cb[0]) { script_cb_close = plugin_script_callback_add (ptr_script, str_script_close_cb, str_script_close_cb_data); if (script_cb_close) { script_cb_close->buffer = ptr_buffer; weechat_buffer_set_pointer (ptr_buffer, "close_callback", callback_buffer_close); weechat_buffer_set_pointer (ptr_buffer, "close_callback_data", script_cb_close); } } } } } } weechat_infolist_free (infolist); } } /* * Removes callbacks for a buffer (called when a buffer is closed by user). */ void plugin_script_remove_buffer_callbacks (struct t_plugin_script *scripts, struct t_gui_buffer *buffer) { struct t_plugin_script *ptr_script; struct t_plugin_script_cb *ptr_script_cb, *next_script_cb; for (ptr_script = scripts; ptr_script; ptr_script = ptr_script->next_script) { /* * do not remove buffer callbacks if script is being unloaded * (because all callbacks will be removed anyway) */ if (!ptr_script->unloading) { ptr_script_cb = ptr_script->callbacks; while (ptr_script_cb) { next_script_cb = ptr_script_cb->next_callback; if (ptr_script_cb->buffer == buffer) plugin_script_callback_remove (ptr_script, ptr_script_cb); ptr_script_cb = next_script_cb; } } } } /* * Removes a script from list of scripts. */ void plugin_script_remove (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script **scripts, struct t_plugin_script **last_script, struct t_plugin_script *script) { struct t_plugin_script_cb *ptr_script_cb, *ptr_script_cb2; script->unloading = 1; for (ptr_script_cb = script->callbacks; ptr_script_cb; ptr_script_cb = ptr_script_cb->next_callback) { /* free configuration file */ if (ptr_script_cb->config_file) { if (weechat_config_boolean (weechat_config_get ("weechat.plugin.save_config_on_unload"))) weechat_config_write (ptr_script_cb->config_file); weechat_config_free (ptr_script_cb->config_file); } /* unhook */ if (ptr_script_cb->hook) weechat_unhook (ptr_script_cb->hook); /* close buffer */ if (ptr_script_cb->buffer) weechat_buffer_close (ptr_script_cb->buffer); /* remove bar item */ if (ptr_script_cb->bar_item) weechat_bar_item_remove (ptr_script_cb->bar_item); /* * remove same pointers in other callbacks * (to not free 2 times with same pointer!) */ for (ptr_script_cb2 = ptr_script_cb->next_callback; ptr_script_cb2; ptr_script_cb2 = ptr_script_cb2->next_callback) { if (ptr_script_cb2->config_file == ptr_script_cb->config_file) ptr_script_cb2->config_file = NULL; if (ptr_script_cb2->config_section == ptr_script_cb->config_section) ptr_script_cb2->config_section = NULL; if (ptr_script_cb2->config_option == ptr_script_cb->config_option) ptr_script_cb2->config_option = NULL; if (ptr_script_cb2->hook == ptr_script_cb->hook) ptr_script_cb2->hook = NULL; if (ptr_script_cb2->buffer == ptr_script_cb->buffer) ptr_script_cb2->buffer = NULL; if (ptr_script_cb2->bar_item == ptr_script_cb->bar_item) ptr_script_cb2->bar_item = NULL; if (ptr_script_cb2->upgrade_file == ptr_script_cb->upgrade_file) ptr_script_cb2->upgrade_file = NULL; } } /* remove all callbacks created by this script */ plugin_script_callback_remove_all (script); /* free data */ if (script->filename) free (script->filename); if (script->name) free (script->name); if (script->author) free (script->author); if (script->version) free (script->version); if (script->license) free (script->license); if (script->description) free (script->description); if (script->shutdown_func) free (script->shutdown_func); if (script->charset) free (script->charset); /* remove script from list */ if (script->prev_script) (script->prev_script)->next_script = script->next_script; if (script->next_script) (script->next_script)->prev_script = script->prev_script; if (*scripts == script) *scripts = script->next_script; if (*last_script == script) *last_script = script->prev_script; /* free script */ free (script); } /* * Adds list of scripts to completion list. */ void plugin_script_completion (struct t_weechat_plugin *weechat_plugin, struct t_gui_completion *completion, struct t_plugin_script *scripts) { struct t_plugin_script *ptr_script; for (ptr_script = scripts; ptr_script; ptr_script = ptr_script->next_script) { weechat_hook_completion_list_add (completion, ptr_script->name, 0, WEECHAT_LIST_POS_SORT); } } /* * Adds script name for a plugin action. */ void plugin_script_action_add (char **action_list, const char *name) { int length; char *action_list2; length = strlen (name); if (!(*action_list)) { *action_list = malloc (length + 1); if (*action_list) strcpy (*action_list, name); } else { action_list2 = realloc (*action_list, strlen (*action_list) + 1 + length + 1); if (!action_list2) { free (*action_list); *action_list = NULL; return; } *action_list = action_list2; strcat (*action_list, ","); strcat (*action_list, name); } } /* * Removes script file(s) from disk. */ void plugin_script_remove_file (struct t_weechat_plugin *weechat_plugin, const char *name, int quiet, int display_error_if_no_script_removed) { int num_found, i; char *path_script; num_found = 0; i = 0; while (i < 2) { path_script = plugin_script_search_path (weechat_plugin, name); /* * script not found? (if path_script == name, that means the function * above did not find the script) */ if (!path_script || (strcmp (path_script, name) == 0)) { if (path_script) free (path_script); break; } num_found++; if (unlink (path_script) == 0) { if (!quiet) { weechat_printf (NULL, _("%s: script removed: %s"), weechat_plugin->name, path_script); } } else { weechat_printf (NULL, _("%s%s: failed to remove script: %s " "(%s)"), weechat_prefix ("error"), weechat_plugin->name, path_script, strerror (errno)); break; } free (path_script); i++; } if ((num_found == 0) && display_error_if_no_script_removed) { weechat_printf (NULL, _("%s: script \"%s\" not found, nothing " "was removed"), weechat_plugin->name, name); } } /* * Installs some scripts (using comma separated list). * * This function does following tasks: * 1. unloads script (if script is loaded) * 2. removes script file(s) * 3. moves script file from "install" dir to language dir * 4. makes link in autoload dir (if option "-a" is given) * 5. loads script. */ void plugin_script_action_install (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script *scripts, void (*script_unload)(struct t_plugin_script *script), int (*script_load)(const char *filename), int *quiet, char **list) { char **argv, *name, *ptr_base_name, *base_name, *new_path, *autoload_path; char *symlink_path, str_signal[128], *ptr_list; const char *dir_home, *dir_separator; int argc, i, length, rc, autoload; struct t_plugin_script *ptr_script; if (!*list) return; /* create again directories, just in case they have been removed */ plugin_script_create_dirs (weechat_plugin); ptr_list = *list; autoload = 0; *quiet = 0; while ((ptr_list[0] == ' ') || (ptr_list[0] == '-')) { if (ptr_list[0] == ' ') ptr_list++; else { switch (ptr_list[1]) { case 'a': /* no autoload */ autoload = 1; break; case 'q': /* quiet mode */ *quiet = 1; break; } ptr_list += 2; } } argv = weechat_string_split (ptr_list, ",", 0, 0, &argc); if (argv) { for (i = 0; i < argc; i++) { name = strdup (argv[i]); if (name) { ptr_base_name = basename (name); base_name = strdup (ptr_base_name); if (base_name) { /* unload script, if script is loaded */ ptr_script = plugin_script_search_by_full_name (scripts, base_name); if (ptr_script) (*script_unload) (ptr_script); /* remove script file(s) */ plugin_script_remove_file (weechat_plugin, base_name, *quiet, 0); /* move file from install dir to language dir */ dir_home = weechat_info_get ("weechat_dir", ""); length = strlen (dir_home) + strlen (weechat_plugin->name) + strlen (base_name) + 16; new_path = malloc (length); if (new_path) { snprintf (new_path, length, "%s/%s/%s", dir_home, weechat_plugin->name, base_name); if (rename (name, new_path) == 0) { /* make link in autoload dir */ if (autoload) { length = strlen (dir_home) + strlen (weechat_plugin->name) + 8 + strlen (base_name) + 16; autoload_path = malloc (length); if (autoload_path) { snprintf (autoload_path, length, "%s/%s/autoload/%s", dir_home, weechat_plugin->name, base_name); dir_separator = weechat_info_get ("dir_separator", ""); length = 2 + strlen (dir_separator) + strlen (base_name) + 1; symlink_path = malloc (length); if (symlink_path) { snprintf (symlink_path, length, "..%s%s", dir_separator, base_name); rc = symlink (symlink_path, autoload_path); (void) rc; free (symlink_path); } free (autoload_path); } } /* load script */ (*script_load) (new_path); } else { weechat_printf (NULL, _("%s%s: failed to move script %s " "to %s (%s)"), weechat_prefix ("error"), weechat_plugin->name, name, new_path, strerror (errno)); } free (new_path); } free (base_name); } free (name); } } weechat_string_free_split (argv); } *quiet = 0; snprintf (str_signal, sizeof (str_signal), "%s_script_installed", weechat_plugin->name); (void) weechat_hook_signal_send (str_signal, WEECHAT_HOOK_SIGNAL_STRING, ptr_list); free (*list); *list = NULL; } /* * Removes some scripts (using comma separated list). * * This function does following tasks: * 1. unloads script (if script is loaded) * 2. removes script file(s). */ void plugin_script_action_remove (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script *scripts, void (*script_unload)(struct t_plugin_script *script), int *quiet, char **list) { char **argv, str_signal[128], *ptr_list; int argc, i; struct t_plugin_script *ptr_script; if (!*list) return; /* create again directories, just in case they have been removed */ plugin_script_create_dirs (weechat_plugin); ptr_list = *list; *quiet = 0; if (strncmp (ptr_list, "-q ", 3) == 0) { *quiet = 1; ptr_list += 3; } argv = weechat_string_split (ptr_list, ",", 0, 0, &argc); if (argv) { for (i = 0; i < argc; i++) { /* unload script, if script is loaded */ ptr_script = plugin_script_search_by_full_name (scripts, argv[i]); if (ptr_script) (*script_unload) (ptr_script); /* remove script file(s) */ plugin_script_remove_file (weechat_plugin, argv[i], *quiet, 1); } weechat_string_free_split (argv); } *quiet = 0; snprintf (str_signal, sizeof (str_signal), "%s_script_removed", weechat_plugin->name); (void) weechat_hook_signal_send (str_signal, WEECHAT_HOOK_SIGNAL_STRING, ptr_list); free (*list); *list = NULL; } /* * Enables/disables autoload for some scripts (using comma separated list). */ void plugin_script_action_autoload (struct t_weechat_plugin *weechat_plugin, int *quiet, char **list) { char **argv, *name, *ptr_base_name, *base_name, *autoload_path; char *symlink_path, *ptr_list; const char *dir_home, *dir_separator; int argc, i, length, rc, autoload; if (!*list) return; /* create again directories, just in case they have been removed */ plugin_script_create_dirs (weechat_plugin); ptr_list = *list; autoload = 0; *quiet = 0; while ((ptr_list[0] == ' ') || (ptr_list[0] == '-')) { if (ptr_list[0] == ' ') ptr_list++; else { switch (ptr_list[1]) { case 'a': /* no autoload */ autoload = 1; break; case 'q': /* quiet mode */ *quiet = 1; break; } ptr_list += 2; } } argv = weechat_string_split (ptr_list, ",", 0, 0, &argc); if (argv) { for (i = 0; i < argc; i++) { name = strdup (argv[i]); if (name) { ptr_base_name = basename (name); base_name = strdup (ptr_base_name); if (base_name) { dir_home = weechat_info_get ("weechat_dir", ""); length = strlen (dir_home) + strlen (weechat_plugin->name) + 8 + strlen (base_name) + 16; autoload_path = malloc (length); if (autoload_path) { snprintf (autoload_path, length, "%s/%s/autoload/%s", dir_home, weechat_plugin->name, base_name); if (autoload) { dir_separator = weechat_info_get ("dir_separator", ""); length = 2 + strlen (dir_separator) + strlen (base_name) + 1; symlink_path = malloc (length); if (symlink_path) { snprintf (symlink_path, length, "..%s%s", dir_separator, base_name); rc = symlink (symlink_path, autoload_path); (void) rc; free (symlink_path); } } else { unlink (autoload_path); } free (autoload_path); } free (base_name); } free (name); } } weechat_string_free_split (argv); } *quiet = 0; free (*list); *list = NULL; } /* * Displays list of scripts. */ void plugin_script_display_list (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script *scripts, const char *name, int full) { struct t_plugin_script *ptr_script; weechat_printf (NULL, ""); weechat_printf (NULL, /* TRANSLATORS: "%s" is language (for example "perl") */ _("%s scripts loaded:"), weechat_plugin->name); if (scripts) { for (ptr_script = scripts; ptr_script; ptr_script = ptr_script->next_script) { if (!name || (weechat_strcasestr (ptr_script->name, name))) { weechat_printf (NULL, " %s%s%s v%s - %s", weechat_color ("chat_buffer"), ptr_script->name, weechat_color ("chat"), ptr_script->version, ptr_script->description); if (full) { weechat_printf (NULL, _(" file: %s"), ptr_script->filename); weechat_printf (NULL, _(" written by \"%s\", license: %s"), ptr_script->author, ptr_script->license); } } } } else weechat_printf (NULL, _(" (none)")); } /* * Displays list of scripts on one line. */ void plugin_script_display_short_list (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script *scripts) { const char *scripts_loaded; char *buf; int length; struct t_plugin_script *ptr_script; if (scripts) { /* TRANSLATORS: "%s" is language (for example "perl") */ scripts_loaded = _("%s scripts loaded:"); length = strlen (scripts_loaded) + strlen (weechat_plugin->name) + 1; for (ptr_script = scripts; ptr_script; ptr_script = ptr_script->next_script) { length += strlen (ptr_script->name) + 2; } length++; buf = malloc (length); if (buf) { snprintf (buf, length, scripts_loaded, weechat_plugin->name); strcat (buf, " "); for (ptr_script = scripts; ptr_script; ptr_script = ptr_script->next_script) { strcat (buf, ptr_script->name); if (ptr_script->next_script) strcat (buf, ", "); } weechat_printf (NULL, "%s", buf); free (buf); } } } /* * Gets hdata for script. */ struct t_hdata * plugin_script_hdata_script (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script **scripts, struct t_plugin_script **last_script, const char *hdata_name) { struct t_hdata *hdata; char str_hdata_callback[128]; hdata = weechat_hdata_new (hdata_name, "prev_script", "next_script", 0, 0, NULL, NULL); if (hdata) { snprintf (str_hdata_callback, sizeof (str_hdata_callback), "%s_callback", weechat_plugin->name); WEECHAT_HDATA_VAR(struct t_plugin_script, filename, STRING, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_plugin_script, interpreter, POINTER, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_plugin_script, name, STRING, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_plugin_script, author, STRING, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_plugin_script, version, STRING, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_plugin_script, license, STRING, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_plugin_script, description, STRING, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_plugin_script, shutdown_func, STRING, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_plugin_script, charset, STRING, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_plugin_script, callbacks, POINTER, 0, NULL, str_hdata_callback); WEECHAT_HDATA_VAR(struct t_plugin_script, unloading, INTEGER, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_plugin_script, prev_script, POINTER, 0, NULL, hdata_name); WEECHAT_HDATA_VAR(struct t_plugin_script, next_script, POINTER, 0, NULL, hdata_name); weechat_hdata_new_list (hdata, "scripts", scripts, WEECHAT_HDATA_LIST_CHECK_POINTERS); weechat_hdata_new_list (hdata, "last_script", last_script, 0); } return hdata; } /* * Adds a script in an infolist. * * Returns: * 1: OK * 0: error */ int plugin_script_add_to_infolist (struct t_weechat_plugin *weechat_plugin, struct t_infolist *infolist, struct t_plugin_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_pointer (ptr_item, "pointer", script)) return 0; if (!weechat_infolist_new_var_string (ptr_item, "filename", script->filename)) return 0; if (!weechat_infolist_new_var_pointer (ptr_item, "interpreter", script->interpreter)) return 0; if (!weechat_infolist_new_var_string (ptr_item, "name", script->name)) return 0; if (!weechat_infolist_new_var_string (ptr_item, "author", script->author)) 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, "shutdown_func", script->shutdown_func)) return 0; if (!weechat_infolist_new_var_string (ptr_item, "charset", script->charset)) return 0; if (!weechat_infolist_new_var_integer (ptr_item, "unloading", script->unloading)) return 0; return 1; } /* * Builds infolist with list of scripts. */ struct t_infolist * plugin_script_infolist_list_scripts (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script *scripts, void *pointer, const char *arguments) { struct t_infolist *ptr_infolist; struct t_plugin_script *ptr_script; if (pointer && !plugin_script_valid (scripts, pointer)) return NULL; ptr_infolist = weechat_infolist_new (); if (ptr_infolist) { if (pointer) { /* build list with only one script */ if (!plugin_script_add_to_infolist (weechat_plugin, ptr_infolist, pointer)) { weechat_infolist_free (ptr_infolist); return NULL; } return ptr_infolist; } else { /* build list with all scripts matching arguments */ for (ptr_script = scripts; ptr_script; ptr_script = ptr_script->next_script) { if (!arguments || !arguments[0] || weechat_string_match (ptr_script->name, arguments, 0)) { if (!plugin_script_add_to_infolist (weechat_plugin, ptr_infolist, ptr_script)) { weechat_infolist_free (ptr_infolist); return NULL; } } } return ptr_infolist; } } return NULL; } /* * Ends script plugin. */ void plugin_script_end (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script **scripts, void (*callback_unload_all)()) { int scripts_loaded; scripts_loaded = (*scripts) ? 1 : 0; (void)(callback_unload_all) (); if (scripts_loaded) { weechat_printf (NULL, _("%s: scripts unloaded"), weechat_plugin->name); } } /* * Prints scripts in WeeChat log file (usually for crash dump). */ void plugin_script_print_log (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script *scripts) { struct t_plugin_script *ptr_script; struct t_plugin_script_cb *ptr_script_cb; weechat_log_printf (""); weechat_log_printf ("***** \"%s\" plugin dump *****", weechat_plugin->name); for (ptr_script = scripts; ptr_script; ptr_script = ptr_script->next_script) { weechat_log_printf (""); weechat_log_printf ("[script %s (addr:0x%lx)]", ptr_script->name, ptr_script); weechat_log_printf (" filename. . . . . . : '%s'", ptr_script->filename); weechat_log_printf (" interpreter . . . . : 0x%lx", ptr_script->interpreter); weechat_log_printf (" name. . . . . . . . : '%s'", ptr_script->name); weechat_log_printf (" author. . . . . . . : '%s'", ptr_script->author); 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 (" shutdown_func . . . : '%s'", ptr_script->shutdown_func); weechat_log_printf (" charset . . . . . . : '%s'", ptr_script->charset); weechat_log_printf (" callbacks . . . . . : 0x%lx", ptr_script->callbacks); weechat_log_printf (" unloading . . . . . : %d", ptr_script->unloading); weechat_log_printf (" prev_script . . . . : 0x%lx", ptr_script->prev_script); weechat_log_printf (" next_script . . . . : 0x%lx", ptr_script->next_script); for (ptr_script_cb = ptr_script->callbacks; ptr_script_cb; ptr_script_cb = ptr_script_cb->next_callback) { plugin_script_callback_print_log (weechat_plugin, ptr_script_cb); } } weechat_log_printf (""); weechat_log_printf ("***** End of \"%s\" plugin dump *****", weechat_plugin->name); }