core: add ternary operator (condition) in evaluation of expressions

v2.8-utf8proc
Sébastien Helleu 2017-03-04 19:41:23 +01:00
parent 07d16903f3
commit 83117f8d2a
10 changed files with 293 additions and 110 deletions

View File

@ -1955,7 +1955,7 @@ str3 = weechat.string_input_for_buffer("//test") # "/test"
==== string_eval_expression
_WeeChat ≥ 0.4.0, updated in 0.4.2, 1.0, 1.1, 1.2 and 1.3._
_WeeChat ≥ 0.4.0, updated in 0.4.2, 1.0, 1.1, 1.2, 1.3 and 1.8._
Evaluate an expression and return result as a string.
Special variables with format `+${variable}+` are expanded (see table below).
@ -2079,6 +2079,16 @@ expanded to last):
`+${env:HOME}+` |
`+/home/user+`
| `+${if:condition}+` +
`+${if:condition?true}+`
`+${if:condition?true:false}+`
(_WeeChat ≥ 1.8_) |
Ternary operator with a condition, a value if the condition is true (optional)
and another value if the condition is false (optional). If values are not
given, "1" or "0" are returned, according to the result of the condition. |
`+${if:${info:term_width}>80?big:small}+` |
`+big+`
| `+${sec.data.name}+` |
Value of the secured data `name`. |
`+${sec.data.freenode_pass}+` |

View File

@ -1991,7 +1991,7 @@ str3 = weechat.string_input_for_buffer("//test") # "/test"
==== string_eval_expression
_WeeChat ≥ 0.4.0, mis à jour dans la 0.4.2, 1.0, 1.1, 1.2 et 1.3._
_WeeChat ≥ 0.4.0, mis à jour dans la 0.4.2, 1.0, 1.1, 1.2, 1.3 et 1.8._
Évaluer l'expression et retourner le résultat sous forme de chaîne.
Les variables spéciales avec le format `+${variable}+` sont étendues (voir le
@ -2121,6 +2121,17 @@ première étendue à la dernière) :
`+${env:HOME}+` |
`+/home/user+`
| `+${if:condition}+` +
`+${if:condition?vrai}+`
`+${if:condition?vrai:faux}+` +
(_WeeChat ≥ 1.8_) |
Opérateur ternaire avec une condition, une valeur si la condition est vraie
(optionnelle) et une autre valeur si la condition est fausse (optionnelle).
Si les valeurs ne sont pas données, "1" ou "0" est retourné, selon le résultat
de la condition. |
`+${if:${info:term_width}>80?grand:petit}+` |
`+grand+`
| `+${sec.data.nom}+` |
Valeur de la donnée sécurisée `nom`. |
`+${sec.data.freenode_pass}+` |

View File

@ -2027,7 +2027,7 @@ str3 = weechat.string_input_for_buffer("//test") # "/test"
==== string_eval_expression
// TRANSLATION MISSING
_WeeChat ≥ 0.4.0, updated in 0.4.2, 1.0, 1.1, 1.2 and 1.3._
_WeeChat ≥ 0.4.0, updated in 0.4.2, 1.0, 1.1, 1.2, 1.3 and 1.8._
// TRANSLATION MISSING
Evaluate an expression and return result as a string.
@ -2098,7 +2098,6 @@ expanded to last):
`+${name}+` |
`+value+`
// TRANSLATION MISSING
| `+${eval:xxx}+` +
(_WeeChat ≥ 1.3_) |
String to evaluate. |
@ -2141,7 +2140,6 @@ expanded to last):
`+1.0+` +
`+lightblue+`
// TRANSLATION MISSING
| `+${date}+` +
`+${date:xxx}+` +
(_WeeChat ≥ 1.3_) |
@ -2158,6 +2156,16 @@ expanded to last):
`+${env:HOME}+` |
`+/home/user+`
| `+${if:condition}+` +
`+${if:condition?true}+`
`+${if:condition?true:false}+` +
(_WeeChat ≥ 1.8_) |
Ternary operator with a condition, a value if the condition is true (optional)
and another value if the condition is false (optional). If values are not
given, "1" or "0" are returned, according to the result of the condition. |
`+${if:${info:term_width}>80?big:small}+` |
`+big+`
| `+${sec.data.name}+` |
Value of the secured data `name`. |
`+${sec.data.freenode_pass}+` |

View File

@ -1961,7 +1961,7 @@ str3 = weechat.string_input_for_buffer("//test") # "/test"
==== string_eval_expression
_WeeChat バージョン 0.4.0 以上で利用可、バージョン 0.4.2、1.0、1.1、1.2、1.3 で更新。_
_WeeChat バージョン 0.4.0 以上で利用可、バージョン 0.4.2、1.0、1.1、1.2、1.3、1.8 で更新。_
式を評価して文字列として返す。`+${variable}+`
という書式で書かれた特殊変数は展開されます (以下の表を参照)。
@ -2085,6 +2085,17 @@ char *weechat_string_eval_expression (const char *expr,
`+${env:HOME}+` |
`+/home/user+`
// TRANSLATION MISSING
| `+${if:condition}+` +
`+${if:condition?true}+`
`+${if:condition?true:false}+` +
(_WeeChat ≥ 1.8_) |
Ternary operator with a condition, a value if the condition is true (optional)
and another value if the condition is false (optional). If values are not
given, "1" or "0" are returned, according to the result of the condition. |
`+${if:${info:term_width}>80?big:small}+` |
`+big+`
| `+${sec.data.name}+` |
セキュアデータ `name` の値 |
`+${sec.data.freenode_pass}+` |

View File

@ -7273,9 +7273,11 @@ command_init ()
"optional)\n"
" 6. current date/time (format: \"date\" or \"date:format\")\n"
" 7. an environment variable (format: \"env:XXX\")\n"
" 8. an option (format: \"file.section.option\")\n"
" 9. a local variable in buffer\n"
" 10. a hdata name/variable (the value is automatically converted "
" 8. a ternary operator (format: "
"if:condition?value_if_true:value_if_false)\n"
" 9. an option (format: \"file.section.option\")\n"
" 10. a local variable in buffer\n"
" 11. 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"
@ -7295,16 +7297,17 @@ command_init ()
"reference\", function \"weechat_hdata_get\".\n"
"\n"
"Examples (simple strings):\n"
" /eval -n ${info:version} ==> 0.4.3\n"
" /eval -n ${env:HOME} ==> /home/user\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 ${\\t} ==> <tab>\n"
" /eval -n ${hide:-,${relay.network.password}} ==> --------\n"
" /eval -n ${date:%H:%M:%S} ==> 07:46:40\n"
" /eval -n ${info:version} ==> 0.4.3\n"
" /eval -n ${env:HOME} ==> /home/user\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 ${\\t} ==> <tab>\n"
" /eval -n ${hide:-,${relay.network.password}} ==> --------\n"
" /eval -n ${date:%H:%M:%S} ==> 07:46:40\n"
" /eval -n ${if:${info:term_width}>80?big:small} ==> big\n"
"\n"
"Examples (conditions):\n"
" /eval -n -c ${window.buffer.number} > 2 ==> 0\n"

View File

@ -49,10 +49,17 @@ char *logical_ops[EVAL_NUM_LOGICAL_OPS] =
char *comparisons[EVAL_NUM_COMPARISONS] =
{ "=~", "!~", "==", "!=", "<=", "<", ">=", ">" };
char *eval_replace_vars (const char *expr, struct t_hashtable *pointers,
struct t_hashtable *extra_vars, int extra_vars_eval,
const char *prefix, const char *suffix,
struct t_eval_regex *eval_regex);
char *eval_expression_condition (const char *expr,
struct t_hashtable *pointers,
struct t_hashtable *extra_vars,
int extra_vars_eval,
const char *prefix,
const char *suffix);
/*
@ -70,6 +77,66 @@ eval_is_true (const char *value)
return (value && value[0] && (strcmp (value, "0") != 0)) ? 1 : 0;
}
/*
* Searches a string in another at same level (skip sub-expressions between
* prefix/suffix).
*
* If escape is 1, the prefix can be escaped with '\' (and then is ignored).
*
* For example: eval_strstr_level ("(x || y) || z", "||")
* will return a pointer on "|| z" (because the first "||" is
* in a sub-expression, which is skipped).
*
* Returns pointer to string found, or NULL if not found.
*/
const char *
eval_strstr_level (const char *string, const char *search,
const char *prefix, const char *suffix, int escape)
{
const char *ptr_string;
int level, length_search, length_prefix, length_suffix;
if (!string || !search)
return NULL;
length_search = strlen (search);
length_prefix = strlen (prefix);
length_suffix = strlen (suffix);
ptr_string = string;
level = 0;
while (ptr_string[0])
{
if (escape && (ptr_string[0] == '\\') && (ptr_string[1] == prefix[0]))
{
ptr_string++;
}
else if (strncmp (ptr_string, prefix, length_prefix) == 0)
{
level++;
ptr_string += length_prefix;
}
else if (strncmp (ptr_string, suffix, length_suffix) == 0)
{
if (level > 0)
level--;
ptr_string += length_suffix;
}
else if ((level == 0)
&& (strncmp (ptr_string, search, length_search) == 0))
{
return ptr_string;
}
else
{
ptr_string++;
}
}
return NULL;
}
/*
* Gets value of hdata using "path" to a variable.
*
@ -233,9 +300,10 @@ end:
* 7. an info (format: info:name,arguments)
* 8. current date/time (format: date or date:xxx)
* 9. an environment variable (format: env:XXX)
* 10. an option (format: file.section.option)
* 11. a buffer local variable
* 12. a hdata variable (format: hdata.var1.var2 or hdata[list].var1.var2
* 10. a ternary operator (format: if:condition?value_if_true:value_if_false)
* 11. an option (format: file.section.option)
* 12. a buffer local variable
* 13. a hdata variable (format: hdata.var1.var2 or hdata[list].var1.var2
* or hdata[ptr].var1.var2)
*
* See /help in WeeChat for examples.
@ -251,7 +319,7 @@ eval_replace_vars_cb (void *data, const char *text)
struct t_config_option *ptr_option;
struct t_gui_buffer *ptr_buffer;
char str_value[512], *value, *pos, *pos1, *pos2, *hdata_name, *list_name;
char *tmp, *info_name, *hide_char, *hidden_string, *error;
char *tmp, *info_name, *hide_char, *hidden_string, *error, *condition;
const char *prefix, *suffix, *ptr_value, *ptr_arguments, *ptr_string;
struct t_hdata *hdata;
void *pointer;
@ -410,7 +478,71 @@ eval_replace_vars_cb (void *data, const char *text)
return strdup (ptr_value);
}
/* 10. option: if found, return this value */
/* 10: ternary operator: if:condition?value_if_true:value_if_false */
if (strncmp (text, "if:", 3) == 0)
{
value = NULL;
pos = (char *)eval_strstr_level (text + 3, "?", prefix, suffix, 1);
pos2 = (pos) ?
(char *)eval_strstr_level (pos + 1, ":", prefix, suffix, 1) : NULL;
condition = (pos) ?
strndup (text + 3, pos - (text + 3)) : strdup (text + 3);
if (!condition)
return strdup ("");
tmp = eval_expression_condition (condition, pointers,
extra_vars, extra_vars_eval,
prefix, suffix);
rc = (tmp && strcmp (tmp, "1") == 0);
if (tmp)
free (tmp);
if (rc)
{
/*
* condition is true: return the "value_if_true"
* (or EVAL_STR_TRUE if value is missing)
*/
if (pos)
{
tmp = (pos2) ?
strndup (pos + 1, pos2 - pos - 1) : strdup (pos + 1);
if (tmp)
{
value = eval_replace_vars (tmp, pointers,
extra_vars, extra_vars_eval,
prefix, suffix,
eval_regex);
free (tmp);
}
}
else
{
value = strdup (EVAL_STR_TRUE);
}
}
else
{
/*
* condition is false: return the "value_if_false"
* (or EVAL_STR_FALSE if both values are missing)
*/
if (pos2)
{
value = eval_replace_vars (pos2 + 1, pointers,
extra_vars, extra_vars_eval,
prefix, suffix,
eval_regex);
}
else
{
if (!pos)
value = strdup (EVAL_STR_FALSE);
}
}
free (condition);
return (value) ? value : strdup ("");
}
/* 11. option: if found, return this value */
if (strncmp (text, "sec.data.", 9) == 0)
{
ptr_value = hashtable_get (secure_hashtable_data, text + 9);
@ -443,7 +575,7 @@ eval_replace_vars_cb (void *data, const char *text)
}
}
/* 11. local variable in buffer */
/* 12. local variable in buffer */
ptr_buffer = hashtable_get (pointers, "buffer");
if (ptr_buffer)
{
@ -452,7 +584,7 @@ eval_replace_vars_cb (void *data, const char *text)
return strdup (ptr_value);
}
/* 12. hdata */
/* 13. hdata */
value = NULL;
hdata_name = NULL;
list_name = NULL;
@ -536,6 +668,7 @@ eval_replace_vars (const char *expr, struct t_hashtable *pointers,
struct t_eval_regex *eval_regex)
{
const void *ptr[6];
const char *no_replace_prefix_list[] = { "if:", NULL };
ptr[0] = pointers;
ptr[1] = extra_vars;
@ -545,6 +678,7 @@ eval_replace_vars (const char *expr, struct t_hashtable *pointers,
ptr[5] = eval_regex;
return string_replace_with_callback (expr, prefix, suffix,
no_replace_prefix_list,
&eval_replace_vars_cb, ptr, NULL);
}
@ -650,51 +784,6 @@ end:
return strdup ((rc) ? EVAL_STR_TRUE : EVAL_STR_FALSE);
}
/*
* Searches a string in another at same level (skip sub-expressions between
* parentheses).
*
* For example: eval_strstr_level ("(x || y) || z", "||")
* will return a pointer on "|| z" (because the first "||" is
* in a sub-expression, which is skipped).
*
* Returns pointer to string found, or NULL if not found.
*/
const char *
eval_strstr_level (const char *string, const char *search)
{
const char *ptr_string;
int level, length;
if (!string || !search)
return NULL;
length = strlen (search);
ptr_string = string;
level = 0;
while (ptr_string[0])
{
if (ptr_string[0] == '(')
{
level++;
}
else if (ptr_string[0] == ')')
{
if (level > 0)
level--;
}
if ((level == 0) && (strncmp (ptr_string, search, length) == 0))
return ptr_string;
ptr_string++;
}
return NULL;
}
/*
* Evaluates a condition (this function must not be called directly).
*
@ -751,7 +840,7 @@ eval_expression_condition (const char *expr,
*/
for (logic = 0; logic < EVAL_NUM_LOGICAL_OPS; logic++)
{
pos = eval_strstr_level (expr2, logical_ops[logic]);
pos = eval_strstr_level (expr2, logical_ops[logic], "(", ")", 0);
if (pos > expr2)
{
pos_end = pos - 1;
@ -804,7 +893,7 @@ eval_expression_condition (const char *expr,
*/
for (comp = 0; comp < EVAL_NUM_COMPARISONS; comp++)
{
pos = eval_strstr_level (expr2, comparisons[comp]);
pos = eval_strstr_level (expr2, comparisons[comp], "(", ")", 0);
if (pos > expr2)
{
pos_end = pos - 1;

View File

@ -2910,6 +2910,12 @@ string_input_for_buffer (const char *string)
*
* Nested variables are supported, for example: "${var1:${var2}}".
*
* Argument "list_prefix_no_replace" is a list to prevent replacements in
* string if beginning with one of the prefixes. For example if the list is
* { "if:", NULL } and string is: "${if:cond?true:false}${test${abc}}"
* then the "if:cond?true:false" is NOT replaced (via a recursive call) but
* "test${abc}" will be replaced.
*
* Argument "errors" (if not NULL) is set with number of keys not found by
* callback.
*
@ -2920,12 +2926,13 @@ char *
string_replace_with_callback (const char *string,
const char *prefix,
const char *suffix,
const char **list_prefix_no_replace,
char *(*callback)(void *data, const char *text),
void *callback_data,
int *errors)
{
int length_prefix, length_suffix, length, length_value, index_string;
int index_result, sub_count, sub_level, sub_errors;
int index_result, sub_count, sub_level, sub_errors, replace, i;
char *result, *result2, *key, *key2, *value;
const char *pos_end_name;
@ -2991,15 +2998,36 @@ string_replace_with_callback (const char *string,
{
if (sub_count > 0)
{
sub_errors = 0;
key2 = string_replace_with_callback (key, prefix,
suffix, callback,
callback_data,
&sub_errors);
if (errors)
(*errors) += sub_errors;
free (key);
key = key2;
replace = 1;
if (list_prefix_no_replace)
{
for (i = 0; list_prefix_no_replace[i]; i++)
{
if (strncmp (
key, list_prefix_no_replace[i],
strlen (list_prefix_no_replace[i])) == 0)
{
replace = 0;
break;
}
}
}
if (replace)
{
sub_errors = 0;
key2 = string_replace_with_callback (
key,
prefix,
suffix,
list_prefix_no_replace,
callback,
callback_data,
&sub_errors);
if (errors)
(*errors) += sub_errors;
free (key);
key = key2;
}
}
value = (*callback) (callback_data, (key) ? key : "");
if (value)

View File

@ -110,6 +110,7 @@ extern const char *string_input_for_buffer (const char *string);
extern char *string_replace_with_callback (const char *string,
const char *prefix,
const char *suffix,
const char **list_prefix_no_replace,
char *(*callback)(void *data, const char *text),
void *callback_data,
int *errors);

View File

@ -248,6 +248,17 @@ TEST(Eval, EvalExpression)
LONGS_EQUAL(8, strlen (value));
free (value);
/* test ternary operator */
WEE_CHECK_EVAL("1", "${if:5>2}");
WEE_CHECK_EVAL("0", "${if:1>7}");
WEE_CHECK_EVAL("yes", "${if:5>2?yes:no}");
WEE_CHECK_EVAL("no", "${if:1>7?yes:no}");
WEE_CHECK_EVAL("yes", "${if:5>2 && 6>3?yes:no}");
WEE_CHECK_EVAL("yes-yes", "${if:5>2?${if:6>3?yes-yes:yes-no}:${if:9>4?no-yes:no-no}}");
WEE_CHECK_EVAL("yes-no", "${if:5>2?${if:1>7?yes-yes:yes-no}:${if:9>4?no-yes:no-no}}");
WEE_CHECK_EVAL("no-yes", "${if:1>7?${if:6>3?yes-yes:yes-no}:${if:9>4?no-yes:no-no}}");
WEE_CHECK_EVAL("no-no", "${if:1>7?${if:1>7?yes-yes:yes-no}:${if:1>7?no-yes:no-no}}");
/* test option */
snprintf (str_value, sizeof (str_value),
"%d", CONFIG_INTEGER(config_look_scroll_amount));

View File

@ -80,11 +80,12 @@ extern "C"
#define WEE_REPLACE_CB(__result_replace, __result_errors, \
__str, __prefix, __suffix, \
__list_prefix_no_replace, \
__callback, __callback_data, __errors) \
errors = -1; \
result = string_replace_with_callback ( \
__str, __prefix, __suffix, __callback, __callback_data, \
__errors); \
__str, __prefix, __suffix, __list_prefix_no_replace, \
__callback, __callback_data, __errors); \
if (__result_replace == NULL) \
{ \
POINTERS_EQUAL(NULL, result); \
@ -676,6 +677,9 @@ test_replace_cb (void *data, const char *text)
if (strcmp (text, "xxx") == 0)
return strdup ("");
if (strncmp (text, "no_replace:", 11) == 0)
return strdup (text);
return NULL;
}
@ -734,48 +738,55 @@ TEST(String, ReplaceRegex)
TEST(String, ReplaceWithCallback)
{
char *result;
const char *list_prefix_no_replace[] = { "no_replace:", NULL };
int errors;
/* tests with invalid arguments */
WEE_REPLACE_CB(NULL, -1, NULL, NULL, NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, "", NULL, NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, NULL, "", NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, NULL, NULL, "", NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, NULL, NULL, NULL, &test_replace_cb, NULL, NULL);
WEE_REPLACE_CB(NULL, 0, NULL, NULL, NULL, NULL, NULL, &errors);
WEE_REPLACE_CB(NULL, -1, "test", NULL, NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, "test", "${", NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, "test", NULL, "}", NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, "test", NULL, NULL, &test_replace_cb, NULL, NULL);
WEE_REPLACE_CB(NULL, 0, "test", NULL, NULL, NULL, NULL, &errors);
WEE_REPLACE_CB(NULL, -1, "test", "${", "}", NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, "", NULL, NULL, NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, NULL, "", NULL, NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, NULL, NULL, "", NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, NULL, NULL, NULL, NULL, &test_replace_cb, NULL, NULL);
WEE_REPLACE_CB(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, &errors);
WEE_REPLACE_CB(NULL, -1, "test", NULL, NULL, NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, "test", "${", NULL, NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, "test", NULL, "}", NULL, NULL, NULL, NULL);
WEE_REPLACE_CB(NULL, -1, "test", NULL, NULL, NULL, &test_replace_cb, NULL, NULL);
WEE_REPLACE_CB(NULL, 0, "test", NULL, NULL, NULL, NULL, NULL, &errors);
WEE_REPLACE_CB(NULL, -1, "test", "${", "}", NULL, NULL, NULL, NULL);
/* valid arguments */
WEE_REPLACE_CB("test", -1, "test", "${", "}",
WEE_REPLACE_CB("test", -1, "test", "${", "}", NULL,
&test_replace_cb, NULL, NULL);
WEE_REPLACE_CB("test", 0, "test", "${", "}",
WEE_REPLACE_CB("test", 0, "test", "${", "}", NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("test def", 0, "test ${abc}", "${", "}",
WEE_REPLACE_CB("test def", 0, "test ${abc}", "${", "}", NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("test ", 0, "test ${xxx}", "${", "}",
WEE_REPLACE_CB("test ", 0, "test ${xxx}", "${", "}", NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("test ${aaa}", 1, "test ${aaa}", "${", "}",
WEE_REPLACE_CB("test ${aaa}", 1, "test ${aaa}", "${", "}", NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("test def ${aaa}", 1, "test ${abc} ${xxx} ${aaa}",
"${", "}", &test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("test ", 1, "test ${abc", "${", "}",
"${", "}", NULL, &test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("test ", 1, "test ${abc", "${", "}", NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("test abc}", 0, "test abc}", "${", "}",
WEE_REPLACE_CB("test abc}", 0, "test abc}", "${", "}", NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("test ${}", 1, "test ${}", "${", "}",
WEE_REPLACE_CB("test ${}", 1, "test ${}", "${", "}", NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("test ${ }", 1, "test ${ }", "${", "}",
WEE_REPLACE_CB("test ${ }", 1, "test ${ }", "${", "}", NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("def", 0, "${abc}", "${", "}",
WEE_REPLACE_CB("def", 0, "${abc}", "${", "}", NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("", 0, "${xxx}", "${", "}",
WEE_REPLACE_CB("", 0, "${xxx}", "${", "}", NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("${aaa}", 1, "${aaa}", "${", "}",
WEE_REPLACE_CB("${aaa}", 1, "${aaa}", "${", "}", NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("no_replace:def", 0, "${no_replace:${abc}}", "${", "}",
NULL,
&test_replace_cb, NULL, &errors);
WEE_REPLACE_CB("no_replace:${abc}", 0, "${no_replace:${abc}}", "${", "}",
list_prefix_no_replace,
&test_replace_cb, NULL, &errors);
}