diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/core/Makefile.am | 2 | ||||
-rw-r--r-- | src/core/wee-command.c | 142 | ||||
-rw-r--r-- | src/core/wee-config.c | 20 | ||||
-rw-r--r-- | src/core/wee-eval.c | 717 | ||||
-rw-r--r-- | src/core/wee-eval.h | 55 | ||||
-rw-r--r-- | src/core/wee-string.c | 34 | ||||
-rw-r--r-- | src/core/wee-string.h | 7 |
8 files changed, 955 insertions, 23 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f5afb1803..984c5be15 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -27,6 +27,7 @@ wee-completion.c wee-completion.h wee-config.c wee-config.h wee-config-file.c wee-config-file.h wee-debug.c wee-debug.h +wee-eval.c wee-eval.h wee-hashtable.c wee-hashtable.h wee-hdata.c wee-hdata.h wee-hook.c wee-hook.h diff --git a/src/core/Makefile.am b/src/core/Makefile.am index ae69f3ec0..6abf84b99 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -35,6 +35,8 @@ lib_weechat_core_a_SOURCES = weechat.c \ wee-config-file.h \ wee-debug.c \ wee-debug.h \ + wee-eval.c \ + wee-eval.h \ wee-hashtable.c \ wee-hashtable.h \ wee-hdata.c \ diff --git a/src/core/wee-command.c b/src/core/wee-command.c index 99e26d7a4..c0fce2c86 100644 --- a/src/core/wee-command.c +++ b/src/core/wee-command.c @@ -39,6 +39,7 @@ #include "wee-config.h" #include "wee-config-file.h" #include "wee-debug.h" +#include "wee-eval.h" #include "wee-hashtable.h" #include "wee-hdata.h" #include "wee-hook.h" @@ -1444,6 +1445,73 @@ COMMAND_CALLBACK(debug) } /* + * command_eval: evaluate expression and send result to buffer + */ + +COMMAND_CALLBACK(eval) +{ + int print_only; + char *result, *ptr_args; + + /* make C compiler happy */ + (void) buffer; + (void) data; + (void) argv; + + print_only = 0; + + if (argc < 2) + return WEECHAT_RC_OK; + + ptr_args = argv_eol[1]; + if (string_strcasecmp (argv[1], "-n") == 0) + { + print_only = 1; + ptr_args = argv_eol[2]; + } + + if (ptr_args) + { + result = eval_expression (ptr_args, NULL, NULL); + if (print_only) + { + gui_chat_printf_date_tags (NULL, 0, "no_log", ">> %s", ptr_args); + if (result) + { + gui_chat_printf_date_tags (NULL, 0, "no_log", "== %s[%s%s%s]", + GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS), + GUI_COLOR(GUI_COLOR_CHAT), + result, + GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS)); + } + else + { + gui_chat_printf_date_tags (NULL, 0, "no_log", "== %s<%s%s%s>", + GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS), + GUI_COLOR(GUI_COLOR_CHAT), + _("error"), + GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS)); + } + } + else + { + if (result) + input_data (buffer, result); + else + { + gui_chat_printf (NULL, + _("%sError in expression to evaluate"), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR]); + } + } + if (result) + free (result); + } + + return WEECHAT_RC_OK; +} + +/* * command_filter_display: display one filter */ @@ -5438,8 +5506,8 @@ command_init () hook_command (NULL, "bar", N_("manage bars"), N_("list|listfull|listitems" - " || add <name> <type>[,<cond1>[,<cond2>...]] <position> " - "<size> <separator> <item1>[,<item2>...]" + " || add <name> <type>[,<condition>] <position> <size> " + "<separator> <item1>[,<item2>...]" " || default [input|title|status|nicklist]" " || del <name>|-all" " || set <name> <option> <value>" @@ -5453,12 +5521,15 @@ command_init () " type: root: outside windows,\n" " window: inside windows, with optional " "conditions (see below)\n" - " cond1,...: condition(s) for displaying bar (only for " + " condition: condition(s) for displaying bar (only for " "type \"window\"):\n" " active: on active window\n" " inactive: on inactive windows\n" " nicklist: on windows with nicklist\n" - " without condition, bar is always displayed\n" + " other condition: see /help " + "weechat.bar.xxx.conditions and /help eval\n" + " without condition, the bar is always " + "displayed\n" " position: bottom, top, left or right\n" " size: size of bar (in chars)\n" " separator: 1 for using separator (line), 0 or nothing " @@ -5683,6 +5754,69 @@ command_init () " || term" " || windows", &command_debug, NULL); + hook_command (NULL, "eval", + N_("evaluate expression and send result to buffer"), + N_("[-n] <expression>" + " || [-n] <expression1> <operator> <expression2>"), + N_(" -n: display result without sending it to buffer " + "(debug mode)\n" + "expression: expression to evaluate, variables with format " + "${variable} are replaced (see below)\n" + " operator: a logical or comparison operator:\n" + " - logical operators:\n" + " && boolean \"and\"\n" + " || boolean \"or\"\n" + " - comparison operators:\n" + " == equal\n" + " != not equal\n" + " <= less or equal\n" + " < less\n" + " >= greater or equal\n" + " > greater\n" + " =~ is matching regex\n" + " !~ is NOT matching regex\n\n" + "An expression is considered as \"true\" if it is not NULL, " + "not empty, and different from \"0\".\n" + "The comparison is made using integers if the two " + "expressions are valid integers.\n" + "To force a string comparison, add double quotes around " + "each expression, for example:\n" + " 50 > 100 ==> 0\n" + " \"50\" > \"100\" ==> 1\n\n" + "Some variables are replaced in expression, using the " + "format ${variable}, variable can be, by order of prioity :\n" + " 1. the name of an option (file.section.option)\n" + " 2. a hdata name/variable (the value is automatically " + "converted to string), by default \"window\" and \"buffer\" " + "point to current window/buffer.\n" + "Format for hdata can be one of following:\n" + " hdata.var1.var2...: start with a hdata (pointer must be " + "known), and ask variables one after one (other hdata can " + "be followed)\n" + " hdata(list).var1.var2...: start with a hdata using a " + "list, for example:\n" + " ${buffer[gui_buffers].full_name}: full name of first " + "buffer in linked list of buffers\n" + " ${plugin[weechat_plugins].name}: name of first plugin " + "in linked list of plugins\n" + "For name of hdata and variables, please look at \"Plugin " + "API reference\", function \"weechat_hdata_get\".\n\n" + "Examples:\n" + " /eval -n ${weechat.look.scroll_amount} ==> 3\n" + " /eval -n ${window} ==> 0x2549aa0\n" + " /eval -n ${window.buffer} ==> 0x2549320\n" + " /eval -n ${window.buffer.full_name} ==> core.weechat\n" + " /eval -n ${window.buffer.number} ==> 1\n" + " /eval -n ${window.buffer.number} > 2 ==> 0\n" + " /eval -n ${window.win_width} > 100 ==> 1\n" + " /eval -n (8 > 12) || (5 > 2) ==> 1\n" + " /eval -n (8 > 12) && (5 > 2) ==> 0\n" + " /eval -n abcd =~ ^ABC ==> 1\n" + " /eval -n abcd =~ (?-i)^ABC ==> 0\n" + " /eval -n abcd =~ (?-i)^abc ==> 1\n" + " /eval -n abcd !~ abc ==> 0"), + "-n", + &command_eval, NULL); hook_command (NULL, "filter", N_("filter messages in buffers, to hide/show them according " "to tags or regex"), diff --git a/src/core/wee-config.c b/src/core/wee-config.c index 7070fe75f..390a75ba6 100644 --- a/src/core/wee-config.c +++ b/src/core/wee-config.c @@ -1163,6 +1163,16 @@ config_weechat_proxy_read_cb (void *data, struct t_config_file *config_file, proxy_create_option_temp (ptr_temp_proxy, index_option, value); } + else + { + gui_chat_printf (NULL, + _("%sWarning: unknown option for " + "section \"%s\": %s (value: \"%s\")"), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + section->name, + option_name, + value); + } } free (proxy_name); @@ -1232,6 +1242,16 @@ config_weechat_bar_read_cb (void *data, struct t_config_file *config_file, gui_bar_create_option_temp (ptr_temp_bar, index_option, value); } + else + { + gui_chat_printf (NULL, + _("%sWarning: unknown option for " + "section \"%s\": %s (value: \"%s\")"), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + section->name, + option_name, + value); + } } free (bar_name); diff --git a/src/core/wee-eval.c b/src/core/wee-eval.c new file mode 100644 index 000000000..45b2e1f01 --- /dev/null +++ b/src/core/wee-eval.c @@ -0,0 +1,717 @@ +/* + * Copyright (C) 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/>. + */ + +/* + * wee-eval.c: evaluate expressions with references to internal vars + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <regex.h> + +#include "weechat.h" +#include "wee-eval.h" +#include "wee-config-file.h" +#include "wee-hashtable.h" +#include "wee-hdata.h" +#include "wee-hook.h" +#include "wee-string.h" +#include "../gui/gui-color.h" +#include "../gui/gui-window.h" +#include "../plugins/plugin.h" + + +char *logical_ops[EVAL_NUM_LOGICAL_OPS] = { "&&", "||" }; +char *comparisons[EVAL_NUM_COMPARISONS] = { "==", "!=", "<=", "<", ">=", ">", + "=~", "!~" }; + + +/* + * eval_is_true: return 1 if value is true, 0 otherwise + * A value is true if string is non-NULL, non-empty and different + * from "0". + */ + +int +eval_is_true (const char *value) +{ + return (value && value[0] && (strcmp (value, "0") != 0)) ? 1 : 0; +} + +/* + * eval_hdata_get_value: get value of hdata using "path" to a variable + * Note: result must be freed after use + */ + +char * +eval_hdata_get_value (struct t_hdata *hdata, void *pointer, const char *path) +{ + char *value, *old_value, *var_name, str_value[128], *pos; + const char *ptr_value, *hdata_name; + int type; + struct t_hashtable *hashtable; + + value = NULL; + var_name = NULL; + + /* NULL pointer? return empty string */ + if (!pointer) + return strdup (""); + + /* no path? just return current pointer as string */ + if (!path || !path[0]) + { + snprintf (str_value, sizeof (str_value), + "0x%lx", (long unsigned int)pointer); + return strdup (str_value); + } + + /* + * look for name of hdata, for example in "window.buffer.full_name", the + * hdata name is "window" + */ + pos = strchr (path, '.'); + if (pos > path) + var_name = string_strndup (path, pos - path); + else + var_name = strdup (path); + + if (!var_name) + goto end; + + /* search type of variable in hdata */ + type = hdata_get_var_type (hdata, var_name); + if (type < 0) + goto end; + + /* build a string with the value or variable */ + switch (type) + { + case WEECHAT_HDATA_CHAR: + snprintf (str_value, sizeof (str_value), + "%c", hdata_char (hdata, pointer, var_name)); + value = strdup (str_value); + break; + case WEECHAT_HDATA_INTEGER: + snprintf (str_value, sizeof (str_value), + "%d", hdata_integer (hdata, pointer, var_name)); + value = strdup (str_value); + break; + case WEECHAT_HDATA_LONG: + snprintf (str_value, sizeof (str_value), + "%ld", hdata_long (hdata, pointer, var_name)); + value = strdup (str_value); + break; + case WEECHAT_HDATA_STRING: + value = strdup (hdata_string (hdata, pointer, var_name)); + break; + case WEECHAT_HDATA_POINTER: + pointer = hdata_pointer (hdata, pointer, var_name); + snprintf (str_value, sizeof (str_value), + "0x%lx", (long unsigned int)pointer); + value = strdup (str_value); + break; + case WEECHAT_HDATA_TIME: + snprintf (str_value, sizeof (str_value), + "%ld", hdata_time (hdata, pointer, var_name)); + value = strdup (str_value); + break; + case WEECHAT_HDATA_HASHTABLE: + pointer = hdata_hashtable (hdata, pointer, var_name); + if (pos) + { + /* + * for a hashtable, if there is a "." after name of hdata, + * get the value for this key in hashtable + */ + hashtable = pointer; + ptr_value = hashtable_get (hashtable, pos + 1); + if (ptr_value) + { + switch (hashtable->type_values) + { + case HASHTABLE_INTEGER: + snprintf (str_value, sizeof (str_value), + "%d", *((int *)ptr_value)); + value = strdup (str_value); + break; + case HASHTABLE_STRING: + value = strdup (ptr_value); + break; + case HASHTABLE_POINTER: + case HASHTABLE_BUFFER: + snprintf (str_value, sizeof (str_value), + "0x%lx", (long unsigned int)ptr_value); + value = strdup (str_value); + break; + case HASHTABLE_TIME: + snprintf (str_value, sizeof (str_value), + "%ld", *((time_t *)ptr_value)); + value = strdup (str_value); + break; + case HASHTABLE_NUM_TYPES: + break; + } + } + } + else + { + snprintf (str_value, sizeof (str_value), + "0x%lx", (long unsigned int)pointer); + value = strdup (str_value); + } + break; + } + + /* + * if we are on a pointer and that something else is in path (after "."), + * go on with this pointer and remaining path + */ + if ((type == WEECHAT_HDATA_POINTER) && pos) + { + hdata_name = hdata_get_var_hdata (hdata, var_name); + if (!hdata_name) + goto end; + + hdata = hook_hdata_get (NULL, hdata_name); + old_value = value; + value = eval_hdata_get_value (hdata, pointer, (pos) ? pos + 1 : NULL); + if (old_value) + free (old_value); + } + +end: + if (var_name) + free (var_name); + + return value; +} + +/* + * eval_replace_vars_cb: callback to replace variables, which can be, + * by order of priority: + * 1. an extra variable (from hashtable "extra_vars") + * 2. an name of option (file.section.option) + * 3. a hdata name/variable + * Examples: + * option: ${weechat.look.scroll_amount} + * hdata : ${window.buffer.full_name} + * ${window.buffer.local_variables.type} + */ + +char * +eval_replace_vars_cb (void *data, const char *text) +{ + struct t_hashtable *pointers, *extra_vars; + struct t_config_option *ptr_option; + char str_value[64], *value, *pos, *pos1, *pos2, *hdata_name, *list_name; + char *tmp; + const char *ptr_value; + struct t_hdata *hdata; + void *pointer; + + pointers = (struct t_hashtable *)(((void **)data)[0]); + extra_vars = (struct t_hashtable *)(((void **)data)[1]); + + /* first look for var in hashtable "extra_vars" */ + ptr_value = hashtable_get (extra_vars, text); + if (ptr_value) + return strdup (ptr_value); + + /* look for name of option: if found, return this value */ + config_file_search_with_string (text, NULL, NULL, &ptr_option, NULL); + if (ptr_option) + { + switch (ptr_option->type) + { + case CONFIG_OPTION_TYPE_BOOLEAN: + return strdup (CONFIG_BOOLEAN(ptr_option) ? EVAL_STR_TRUE : EVAL_STR_FALSE); + case CONFIG_OPTION_TYPE_INTEGER: + if (ptr_option->string_values) + return strdup (ptr_option->string_values[CONFIG_INTEGER(ptr_option)]); + snprintf (str_value, sizeof (str_value), + "%d", CONFIG_INTEGER(ptr_option)); + return strdup (str_value); + case CONFIG_OPTION_TYPE_STRING: + return strdup (CONFIG_STRING(ptr_option)); + case CONFIG_OPTION_TYPE_COLOR: + return strdup (gui_color_get_name (CONFIG_COLOR(ptr_option))); + case CONFIG_NUM_OPTION_TYPES: + return NULL; + } + } + + /* look for hdata */ + value = NULL; + hdata_name = NULL; + list_name = NULL; + pointer = NULL; + + pos = strchr (text, '.'); + if (pos > text) + hdata_name = string_strndup (text, pos - text); + else + hdata_name = strdup (text); + + if (!hdata_name) + goto end; + + pos1 = strchr (hdata_name, '['); + if (pos1 > hdata_name) + { + pos2 = strchr (pos1 + 1, ']'); + if (pos2 > pos1 + 1) + { + list_name = string_strndup (pos1 + 1, pos2 - pos1 - 1); + } + tmp = string_strndup (hdata_name, pos1 - hdata_name); + if (tmp) + { + free (hdata_name); + hdata_name = tmp; + } + } + + hdata = hook_hdata_get (NULL, hdata_name); + if (!hdata) + goto end; + + if (list_name) + pointer = hdata_get_list (hdata, list_name); + if (!pointer) + { + pointer = hashtable_get (pointers, hdata_name); + if (!pointer) + goto end; + } + + value = eval_hdata_get_value (hdata, pointer, (pos) ? pos + 1 : NULL); + +end: + if (hdata_name) + free (hdata_name); + if (list_name) + free (list_name); + + return (value) ? value : strdup (""); +} + +/* + * eval_replace_vars: replace variables in a string + */ + +char * +eval_replace_vars (const char *expr, struct t_hashtable *pointers, + struct t_hashtable *extra_vars) +{ + int errors; + void *ptr[2]; + + ptr[0] = pointers; + ptr[1] = extra_vars; + + return string_replace_with_callback (expr, + &eval_replace_vars_cb, + ptr, + &errors); +} + +/* + * eval_compare: compate two expressions + */ + +char * +eval_compare (const char *expr1, int comparison, const char *expr2) +{ + int rc, string_compare, length1, length2; + regex_t regex; + long value1, value2; + char *error; + + rc = 0; + string_compare = 0; + + if (!expr1 || !expr2) + goto end; + + if ((comparison == EVAL_COMPARE_REGEX_MATCHING) + || (comparison == EVAL_COMPARE_REGEX_NOT_MATCHING)) + { + if (string_regcomp (®ex, expr2, + REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0) + { + goto end; + } + rc = (regexec (®ex, expr1, 0, NULL, 0) == 0) ? 1 : 0; + if (comparison == EVAL_COMPARE_REGEX_NOT_MATCHING) + rc ^= 1; + goto end; + } + + length1 = strlen (expr1); + length2 = strlen (expr2); + + /* + * string comparison is forced if expr1 and expr2 have double quotes at + * beginning/end + */ + if (((length1 == 0) || ((expr1[0] == '"') && expr1[length1 - 1] == '"')) + && ((length2 == 0) || ((expr2[0] == '"') && expr2[length2 - 1] == '"'))) + { + string_compare = 1; + } + + if (!string_compare) + { + value1 = strtol (expr1, &error, 10); + if (!error || error[0]) + string_compare = 1; + else + { + value2 = strtol (expr2, &error, 10); + if (!error || error[0]) + string_compare = 1; + } + } + + if (string_compare) + rc = strcmp (expr1, expr2); + else + rc = (value1 < value2) ? -1 : ((value1 > value2) ? 1 : 0); + + switch (comparison) + { + case EVAL_COMPARE_EQUAL: + rc = (rc == 0); + break; + case EVAL_COMPARE_NOT_EQUAL: + rc = (rc != 0); + break; + case EVAL_COMPARE_LESS_EQUAL: + rc = (rc <= 0); + break; + case EVAL_COMPARE_LESS: + rc = (rc < 0); + break; + case EVAL_COMPARE_GREATER_EQUAL: + rc = (rc >= 0); + break; + case EVAL_COMPARE_GREATER: + rc = (rc > 0); + break; + case EVAL_NUM_COMPARISONS: + break; + } + +end: + return strdup ((rc) ? EVAL_STR_TRUE : EVAL_STR_FALSE); +} + +/* + * eval_expression_internal: evaluate an expression and return a string with + * the result (see function eval_expression()) + * (should not be called directly) + * Argument keep_parentheses is almost always 0, + * it is 1 only if the expression is a regex (to keep + * flags inside the parentheses) + */ + +char * +eval_expression_internal (const char *expr, struct t_hashtable *pointers, + struct t_hashtable *extra_vars, + int keep_parentheses) +{ + int logic, comp, length, level, rc; + const char *pos_end; + char *expr2, *sub_expr, *pos, *value, *tmp_value, *tmp_value2; + + value = NULL; + + if (!expr) + return NULL; + + if (!expr[0]) + return strdup (expr); + + /* + * skip spaces at beginning of string + */ + while (expr[0] == ' ') + { + expr++; + } + if (!expr[0]) + return strdup (expr); + + /* skip spaces at end of string */ + pos_end = expr + strlen (expr) - 1; + while ((pos_end > expr) && (pos_end[0] == ' ')) + { + pos_end--; + } + + expr2 = string_strndup (expr, pos_end + 1 - expr); + if (!expr2) + return NULL; + + /* evaluate sub-expression in parentheses and replace it with value */ + if (!keep_parentheses) + { + while (expr2[0] == '(') + { + level = 0; + pos = expr2 + 1; + while (pos[0]) + { + if (pos[0] == '(') + level++; + else if (pos[0] == ')') + { + if (level == 0) + break; + level--; + } + pos++; + } + /* closing parenthese not found */ + if (pos[0] != ')') + goto end; + sub_expr = string_strndup (expr2 + 1, pos - expr2 - 1); + if (!sub_expr) + goto end; + tmp_value = eval_expression_internal (sub_expr, pointers, extra_vars, 0); + free (sub_expr); + if (!pos[1]) + { + /* nothing after ')', then return value of sub-expression as-is */ + value = tmp_value; + goto end; + } + length = ((tmp_value) ? strlen (tmp_value) : 0) + 1 + strlen (pos + 1) + 1; + tmp_value2 = malloc (length); + if (!tmp_value2) + goto end; + tmp_value2[0] = '\0'; + if (tmp_value) + strcat (tmp_value2, tmp_value); + strcat (tmp_value2, " "); + strcat (tmp_value2, pos + 1); + free (expr2); + expr2 = tmp_value2; + } + } + + /* + * search for a logical operator, and if one is found: + * - split expression into two sub-expressions + * - evaluate first sub-expression + * - if needed, evaluate second sub-expression + * - return result + */ + for (logic = 0; logic < EVAL_NUM_LOGICAL_OPS; logic++) + { + pos = strstr (expr2, logical_ops[logic]); + if (pos > expr2) + { + pos_end = pos - 1; + while ((pos_end > expr2) && (pos_end[0] == ' ')) + { + pos_end--; + } + sub_expr = string_strndup (expr2, pos_end + 1 - expr2); + if (!sub_expr) + goto end; + tmp_value = eval_expression_internal (sub_expr, pointers, extra_vars, 0); + free (sub_expr); + rc = eval_is_true (tmp_value); + /* + * if rc == 0 with "&&" or rc == 1 with "||", no need to evaluate + * second sub-expression, just return the rc + */ + if ((!rc && (logic == EVAL_LOGICAL_OP_AND)) + || (rc && (logic == EVAL_LOGICAL_OP_OR))) + { + if (tmp_value) + free (tmp_value); + value = strdup ((rc) ? EVAL_STR_TRUE : EVAL_STR_FALSE); + goto end; + } + pos += strlen (logical_ops[logic]); + while (pos[0] == ' ') + { + pos++; + } + tmp_value = eval_expression_internal (pos, pointers, extra_vars, 0); + rc = eval_is_true (tmp_value); + if (tmp_value) + free (tmp_value); + value = strdup ((rc) ? EVAL_STR_TRUE : EVAL_STR_FALSE); + goto end; + } + } + + /* + * search for a comparison, and if one is found: + * - split expression into two sub-expressions + * - evaluate the two sub-expressions + * - compare sub-expressions + * - return result + */ + for (comp = 0; comp < EVAL_NUM_COMPARISONS; comp++) + { + pos = strstr (expr2, comparisons[comp]); + if (pos > expr2) + { + pos_end = pos - 1; + while ((pos_end > expr2) && (pos_end[0] == ' ')) + { + pos_end--; + } + sub_expr = string_strndup (expr2, pos_end + 1 - expr2); + if (!sub_expr) + goto end; + tmp_value = eval_expression_internal (sub_expr, pointers, extra_vars, 0); + free (sub_expr); + pos += strlen (comparisons[comp]); + while (pos[0] == ' ') + { + pos++; + } + tmp_value2 = eval_expression_internal (pos, pointers, extra_vars, + ((comp == EVAL_COMPARE_REGEX_MATCHING) + || (comp == EVAL_COMPARE_REGEX_NOT_MATCHING)) ? 1 : 0); + value = eval_compare (tmp_value, comp, tmp_value2); + if (tmp_value) + free (tmp_value); + if (tmp_value2) + free (tmp_value2); + goto end; + } + } + + /* + * at this point, there is no more logical operator neither comparison, + * so we just replace variables in string and return the result + */ + value = eval_replace_vars (expr2, pointers, extra_vars); + +end: + if (expr2) + free (expr2); + + return value; +} + +/* + * eval_expression: evaluate an expression and return a string with the result + * The hashtable "pointers" must have string for keys, pointer + * for values. + * The hashtable "extra_vars" must have string for keys and + * values. + * The expression can contain: + * - conditions: == != < <= > >= + * - logical operators: && || + * - parentheses for priority + * Examples (the [ ] are NOT part of result): + * >> ${window.buffer.number} + * == [2] + * >> buffer:${window.buffer.full_name} + * == [buffer:irc.freenode.#weechat] + * >> ${window.buffer.full_name} == irc.freenode.#weechat + * == [1] + * >> ${window.buffer.full_name} == irc.freenode.#test + * == [0] + * >> ${window.win_width} + * == [112] + * >> ${window.win_height} + * == [40] + * >> ${window.win_width} >= 30 && ${window.win_height} >= 20 + * == [1] + * Note: result must be freed after use + */ + +char * +eval_expression (const char *expr, struct t_hashtable *pointers, + struct t_hashtable *extra_vars) +{ + int pointers_created, extra_vars_created; + char *value; + struct t_gui_window *window; + + if (!expr) + return NULL; + + pointers_created = 0; + extra_vars_created = 0; + + /* create hashtable pointers if it's NULL */ + if (!pointers) + { + pointers = hashtable_new (16, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_POINTER, + NULL, + NULL); + if (!pointers) + return NULL; + pointers_created = 1; + } + + /* + * set window/buffer with pointer to current window/buffer + * (if not already defined in the hashtable) + */ + if (gui_current_window) + { + if (!hashtable_has_key (pointers, "window")) + hashtable_set (pointers, "window", gui_current_window); + if (!hashtable_has_key (pointers, "buffer")) + { + window = (struct t_gui_window *)hashtable_get (pointers, "window"); + if (window) + hashtable_set (pointers, "buffer", window->buffer); + } + } + + /* create hashtable extra_vars if it's NULL */ + if (!extra_vars) + { + extra_vars = hashtable_new (16, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, + NULL); + if (!extra_vars) + return NULL; + extra_vars_created = 1; + } + + value = eval_expression_internal (expr, pointers, extra_vars, 0); + + if (pointers_created) + hashtable_free (pointers); + if (extra_vars_created) + hashtable_free (extra_vars); + + return value; +} diff --git a/src/core/wee-eval.h b/src/core/wee-eval.h new file mode 100644 index 000000000..482de2e2e --- /dev/null +++ b/src/core/wee-eval.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 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_EVAL_H +#define __WEECHAT_EVAL_H 1 + +#define EVAL_STR_FALSE "0" +#define EVAL_STR_TRUE "1" + +struct t_hashtable; + +enum t_eval_logical_op +{ + EVAL_LOGICAL_OP_AND = 0, + EVAL_LOGICAL_OP_OR, + /* number of comparison strings */ + EVAL_NUM_LOGICAL_OPS, +}; + +enum t_eval_comparison +{ + EVAL_COMPARE_EQUAL = 0, + EVAL_COMPARE_NOT_EQUAL, + EVAL_COMPARE_LESS_EQUAL, + EVAL_COMPARE_LESS, + EVAL_COMPARE_GREATER_EQUAL, + EVAL_COMPARE_GREATER, + EVAL_COMPARE_REGEX_MATCHING, + EVAL_COMPARE_REGEX_NOT_MATCHING, + /* number of comparison strings */ + EVAL_NUM_COMPARISONS, +}; + +extern int eval_is_true (const char *value); +extern char *eval_expression (const char *expr, + struct t_hashtable *pointers, + struct t_hashtable *extra_vars); + +#endif /* __WEECHAT_EVAL_H */ diff --git a/src/core/wee-string.c b/src/core/wee-string.c index 1d16d6d0f..269663cb8 100644 --- a/src/core/wee-string.c +++ b/src/core/wee-string.c @@ -49,7 +49,6 @@ #include "weechat.h" #include "wee-string.h" #include "wee-config.h" -#include "wee-hashtable.h" #include "wee-utf8.h" #include "../gui/gui-color.h" #include "../plugins/plugin.h" @@ -1774,28 +1773,29 @@ string_input_for_buffer (const char *string) } /* - * string_replace_with_hashtable: replace ${codes} with value from hashtable - * "errors" is set with number of keys not found - * in hashtable + * string_replace_with_callback: replace ${codes} using a callback that return + * replacement value (this value must be newly + * allocated because it will be freed in this + * function) + * "errors" is set with number of keys not found + * by callback */ char * -string_replace_with_hashtable (const char *string, - struct t_hashtable *hashtable, - int *errors) +string_replace_with_callback (const char *string, + char *(*callback)(void *data, const char *text), + void *callback_data, + int *errors) { int length, length_value, index_string, index_result; - char *result, *result2, *key; - const char *pos_end_name, *ptr_value; + char *result, *result2, *key, *value; + const char *pos_end_name; *errors = 0; if (!string) return NULL; - if (!hashtable) - return strdup (string); - length = strlen (string) + 1; result = malloc (length); if (result) @@ -1820,10 +1820,10 @@ string_replace_with_hashtable (const char *string, pos_end_name - (string + index_string + 2)); if (key) { - ptr_value = (const char *)hashtable_get (hashtable, key); - if (ptr_value) + value = (*callback) (callback_data, key); + if (value) { - length_value = strlen (ptr_value); + length_value = strlen (value); length += length_value; result2 = realloc (result, length); if (!result2) @@ -1831,13 +1831,15 @@ string_replace_with_hashtable (const char *string, if (result) free (result); free (key); + free (value); return NULL; } result = result2; - strcpy (result + index_result, ptr_value); + strcpy (result + index_result, value); index_result += length_value; index_string += pos_end_name - string - index_string + 1; + free (value); } else { diff --git a/src/core/wee-string.h b/src/core/wee-string.h index a000e65f3..a9fc76ea8 100644 --- a/src/core/wee-string.h +++ b/src/core/wee-string.h @@ -75,8 +75,9 @@ extern void string_encode_base64 (const char *from, int length, char *to); extern int string_decode_base64 (const char *from, char *to); extern int string_is_command_char (const char *string); extern const char *string_input_for_buffer (const char *string); -extern char *string_replace_with_hashtable (const char *string, - struct t_hashtable *hashtable, - int *errors); +extern char *string_replace_with_callback (const char *string, + char *(*callback)(void *data, const char *text), + void *callback_data, + int *errors); #endif /* __WEECHAT_STRING_H */ |