summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/Makefile.am2
-rw-r--r--src/core/wee-command.c142
-rw-r--r--src/core/wee-config.c20
-rw-r--r--src/core/wee-eval.c717
-rw-r--r--src/core/wee-eval.h55
-rw-r--r--src/core/wee-string.c34
-rw-r--r--src/core/wee-string.h7
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 (&regex, expr2,
+ REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0)
+ {
+ goto end;
+ }
+ rc = (regexec (&regex, 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 */