From 1b57da8673cf02f89d3db845c2e4507ed543fa18 Mon Sep 17 00:00:00 2001 From: Jonathan Rose Date: Fri, 20 May 2011 16:27:12 +0000 Subject: Adds STRREPLACE function Adds a new STRREPLACe function to func_strings.c that allows users to search and replace against a variable in the dialplan. (closes issue #18023) Reported by: wdoekes Review: https://reviewboard.asterisk.org/r/1219/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@320040 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- funcs/func_strings.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) (limited to 'funcs') diff --git a/funcs/func_strings.c b/funcs/func_strings.c index d95a3cbf0..0d2b95efc 100644 --- a/funcs/func_strings.c +++ b/funcs/func_strings.c @@ -132,6 +132,24 @@ AST_THREADSTORAGE(tmp_buf); altered. + + + Replace instances of a substring within a string with another string. + + + + + + + + + Searches for all instances of the find-string in provided variable and + replaces them with replace-string. If replace-string + is an empty string, this will effecively delete that substring. If max-replacements + is specified, this function will stop after performing replacements max-replacements times. + The replacement only occurs in the output. The original variable is not altered. + + Pass the given argument back as a value. @@ -835,6 +853,97 @@ static struct ast_custom_function replace_function = { .read2 = replace, }; +static int strreplace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len) +{ + char *starts[len]; /* marks starts of substrings */ + char *varsubstr; /* substring for input var */ + int count_len = 0; /* counter for starts */ + char *p; /* tracks position of cursor for search and replace */ + int find_size; /* length of given find-string */ + int max_matches; /* number of matches we find before terminating search */ + + int x; /* loop counter */ + struct ast_str *str = ast_str_thread_get(&result_buf, 16); /* Holds the data obtained from varname */ + + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(varname); + AST_APP_ARG(find_string); + AST_APP_ARG(replace_string); + AST_APP_ARG(max_replacements); + ); + + AST_STANDARD_APP_ARGS(args, data); + + if (!str) { /* If we failed to allocate str, forget it. We failed. */ + return -1; + } + + if (args.argc < 2) { /* Didn't receive enough arguments to do anything */ + ast_log(LOG_ERROR, "Usage: %s(,[,, ])\n", cmd); + return -1; + } + + /* No var name specified. Return failure, string is already empty. */ + if (ast_strlen_zero(args.varname)) { + return -1; + } + + /* set varsubstr to the matching variable */ + varsubstr = alloca(strlen(args.varname) + 4); + sprintf(varsubstr, "${%s}", args.varname); + ast_str_substitute_variables(&str, 0, chan, varsubstr); + + p = ast_str_buffer(str); + + /* Zero length find strings are a no-no. Kill the function if we run into one. */ + if (ast_strlen_zero(args.find_string)) { + ast_log(LOG_ERROR, "The must have length > 0\n"); + return -1; + } + find_size = strlen(args.find_string); + + /* If the replace string is a null pointer, set it to an empty string */ + if (!args.replace_string) { + args.replace_string = ""; + } + + /* + * If max_replacements specified and is a number, max_matches will become that. + * otherwise, just go the length of the input string + */ + if (!(args.max_replacements && (max_matches = atoi(args.max_replacements)))) { + max_matches = strlen(p); + } + + /* Iterate through string finding matches until it is exhausted or we reach max_matches */ + for (x = 0; x < max_matches; x++) { + if ((p = strstr(p, args.find_string))) { + starts[count_len++] = p; + *p++ = '\0'; + } else { + break; + } + } + + p = ast_str_buffer(str); + + /* here we rebuild the string with the replaced words by using fancy ast_string_append on the buffer */ + for (x = 0; x < count_len; x++) { + ast_str_append(buf, 0, "%s", p); + p = starts[x]; + p += find_size; + ast_str_append(buf, 0, "%s", args.replace_string); + } + ast_str_append(buf, 0, "%s", p); + + return 0; +} + +static struct ast_custom_function strreplace_function = { + .name = "STRREPLACE", + .read2 = strreplace, +}; + static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len) { @@ -1639,6 +1748,74 @@ AST_TEST_DEFINE(test_FILTER) } return res; } + +AST_TEST_DEFINE(test_STRREPLACE) +{ + int i, res = AST_TEST_PASS; + struct ast_channel *chan; /* dummy channel */ + struct ast_str *str; /* fancy string for holding comparing value */ + + const char *test_strings[][5] = { + {"Weasels have eaten my telephone system", "have eaten my", "are eating our", "", "Weasels are eating our telephone system"}, /*Test normal conditions */ + {"Did you know twenty plus two is twenty-two?", "twenty", "thirty", NULL, "Did you know thirty plus two is thirty-two?"}, /* Test no third comma */ + {"My pet dog once ate a dog who sat on a dog while eating a corndog.", "dog", "cat", "3", "My pet cat once ate a cat who sat on a cat while eating a corndog."}, + {"One and one and one is three", "and", "plus", "1", "One plus one and one is three"}, /* Test = 1*/ + {"", "fhqwagads", "spelunker", NULL, ""}, /* Empty primary string */ + {"Part of this string is missing.", "missing", NULL, NULL, "Part of this string is ."}, /* Empty replace string */ + {"'Accidentally' left off a bunch of stuff.", NULL, NULL, NULL, ""}, /* Deliberate error test from too few args */ + {"This test will also error.", "", "", "", ""}, /* Deliberate error test from blank find string */ + {"This is an \"escape character\" test.", "\\\"escape character\\\"", "evil", NULL, "This is an evil test."} + }; + + switch (cmd) { + case TEST_INIT: + info->name = "func_STRREPLACE_test"; + info->category = "/funcs/func_strings/"; + info->summary = "Test STRREPLACE function"; + info->description = "Verify STRREPLACE behavior"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + if (!(chan = ast_dummy_channel_alloc())) { + ast_test_status_update(test, "Unable to allocate dummy channel\n"); + return AST_TEST_FAIL; + } + + if (!(str = ast_str_create(64))) { + ast_test_status_update(test, "Unable to allocate dynamic string buffer\n"); + ast_channel_release(chan); + return AST_TEST_FAIL; + } + + for (i = 0; i < ARRAY_LEN(test_strings); i++) { + char tmp[512], tmp2[512] = ""; + + struct ast_var_t *var = ast_var_assign("test_string", test_strings[i][0]); + AST_LIST_INSERT_HEAD(&chan->varshead, var, entries); + + if (test_strings[i][3]) { + snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s,%s,%s)}", "test_string", test_strings[i][1], test_strings[i][2], test_strings[i][3]); + } else if (test_strings[i][2]) { + snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s,%s)}", "test_string", test_strings[i][1], test_strings[i][2]); + } else if (test_strings[i][1]) { + snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s)}", "test_string", test_strings[i][1]); + } else { + snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s)}", "test_string"); + } + ast_str_substitute_variables(&str, 0, chan, tmp); + if (strcmp(test_strings[i][4], ast_str_buffer(str))) { + ast_test_status_update(test, "Format string '%s' substituted to '%s'. Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][4]); + res = AST_TEST_FAIL; + } + } + + ast_free(str); + ast_channel_release(chan); + + return res; +} #endif static int unload_module(void) @@ -1647,10 +1824,12 @@ static int unload_module(void) AST_TEST_UNREGISTER(test_FIELDNUM); AST_TEST_UNREGISTER(test_FILTER); + AST_TEST_UNREGISTER(test_STRREPLACE); res |= ast_custom_function_unregister(&fieldqty_function); res |= ast_custom_function_unregister(&fieldnum_function); res |= ast_custom_function_unregister(&filter_function); res |= ast_custom_function_unregister(&replace_function); + res |= ast_custom_function_unregister(&strreplace_function); res |= ast_custom_function_unregister(&listfilter_function); res |= ast_custom_function_unregister(®ex_function); res |= ast_custom_function_unregister(&array_function); @@ -1681,10 +1860,12 @@ static int load_module(void) AST_TEST_REGISTER(test_FIELDNUM); AST_TEST_REGISTER(test_FILTER); + AST_TEST_REGISTER(test_STRREPLACE); res |= ast_custom_function_register(&fieldqty_function); res |= ast_custom_function_register(&fieldnum_function); res |= ast_custom_function_register(&filter_function); res |= ast_custom_function_register(&replace_function); + res |= ast_custom_function_register(&strreplace_function); res |= ast_custom_function_register(&listfilter_function); res |= ast_custom_function_register(®ex_function); res |= ast_custom_function_register(&array_function); -- cgit v1.2.3