Skip to content
Snippets Groups Projects
func_strings.c 55.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		if (strncmp(prefix, ast_var_name(var), len) == 0) {
    
    			AST_LIST_REMOVE_CURRENT(entries);
    
    static int exec_clearhash(struct ast_channel *chan, const char *data)
    
    {
    	char prefix[80];
    	snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
    	clearvar_prefix(chan, prefix);
    	return 0;
    }
    
    
    static int array(struct ast_channel *chan, const char *cmd, char *var,
    
    		 const char *value)
    
    	AST_DECLARE_APP_ARGS(arg1,
    
    			     AST_APP_ARG(var)[100];
    
    			     AST_APP_ARG(val)[100];
    
    	char *origvar = "", *value2, varname[256];
    	int i, ishash = 0;
    
    		return -1;
    
    	}
    	value2 = ast_strdupa(value);
    
    	if (!strcmp(cmd, "HASH")) {
    		const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
    		origvar = var;
    		if (var2)
    			var = ast_strdupa(var2);
    
    		else {
    			if (chan)
    				ast_autoservice_stop(chan);
    
    	/* The functions this will generally be used with are SORT and ODBC_*, which
    	 * both return comma-delimited lists.  However, if somebody uses literal lists,
    	 * their commas will be translated to vertical bars by the load, and I don't
    	 * want them to be surprised by the result.  Hence, we prefer commas as the
    	 * delimiter, but we'll fall back to vertical bars if commas aren't found.
    	 */
    
    	ast_debug(1, "array (%s=%s)\n", var, S_OR(value2, ""));
    
    	for (i = 0; i < arg1.argc; i++) {
    
    		ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
    
    				S_OR(arg2.val[i], ""));
    
    				if (origvar[0] == '_') {
    					if (origvar[1] == '_') {
    						snprintf(varname, sizeof(varname), "__" HASH_FORMAT, origvar + 2, arg1.var[i]);
    					} else {
    						snprintf(varname, sizeof(varname), "_" HASH_FORMAT, origvar + 1, arg1.var[i]);
    					}
    				} else {
    					snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
    				}
    
    
    				pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
    			} else {
    				pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
    			}
    
    		} else {
    			/* We could unset the variable, by passing a NULL, but due to
    			 * pushvar semantics, that could create some undesired behavior. */
    
    			if (ishash) {
    				snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
    				pbx_builtin_setvar_helper(chan, varname, "");
    			} else {
    				pbx_builtin_setvar_helper(chan, arg1.var[i], "");
    			}
    
    static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
    
    	struct ast_str *prefix = ast_str_alloca(80);
    
    	ast_str_set(&prefix, -1, HASH_PREFIX, data);
    
    	AST_LIST_TRAVERSE(ast_channel_varshead(chan), newvar, entries) {
    
    		if (strncmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
    
    			/* Copy everything after the prefix */
    
    			strncat(buf, ast_var_name(newvar) + ast_str_strlen(prefix), len - strlen(buf) - 1);
    
    			/* Trim the trailing ~ */
    			buf[strlen(buf) - 1] = ',';
    		}
    	}
    	/* Trim the trailing comma */
    	buf[strlen(buf) - 1] = '\0';
    	return 0;
    }
    
    
    static int hashkeys_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
    {
    	struct ast_var_t *newvar;
    	struct ast_str *prefix = ast_str_alloca(80);
    	char *tmp;
    
    	ast_str_set(&prefix, -1, HASH_PREFIX, data);
    
    
    	AST_LIST_TRAVERSE(ast_channel_varshead(chan), newvar, entries) {
    
    		if (strncmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
    
    			/* Copy everything after the prefix */
    			ast_str_append(buf, len, "%s", ast_var_name(newvar) + ast_str_strlen(prefix));
    			/* Trim the trailing ~ */
    			tmp = ast_str_buffer(*buf);
    			tmp[ast_str_strlen(*buf) - 1] = ',';
    		}
    	}
    	/* Trim the trailing comma */
    	tmp = ast_str_buffer(*buf);
    	tmp[ast_str_strlen(*buf) - 1] = '\0';
    	return 0;
    }
    
    
    static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
    
    {
    	char varname[256];
    	AST_DECLARE_APP_ARGS(arg,
    		AST_APP_ARG(hashname);
    		AST_APP_ARG(hashkey);
    	);
    
    
    		/* Single argument version */
    		return array(chan, "HASH", var, value);
    	}
    
    	AST_STANDARD_APP_ARGS(arg, var);
    
    	if (arg.hashname[0] == '_') {
    		if (arg.hashname[1] == '_') {
    			snprintf(varname, sizeof(varname), "__" HASH_FORMAT, arg.hashname + 2, arg.hashkey);
    		} else {
    			snprintf(varname, sizeof(varname), "_" HASH_FORMAT, arg.hashname + 1, arg.hashkey);
    		}
    	} else {
    		snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
    	}
    
    	pbx_builtin_setvar_helper(chan, varname, value);
    
    	return 0;
    }
    
    
    static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
    
    {
    	char varname[256];
    	const char *varvalue;
    	AST_DECLARE_APP_ARGS(arg,
    		AST_APP_ARG(hashname);
    		AST_APP_ARG(hashkey);
    	);
    
    	AST_STANDARD_APP_ARGS(arg, data);
    	if (arg.argc == 2) {
    		snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
    		varvalue = pbx_builtin_getvar_helper(chan, varname);
    		if (varvalue)
    			ast_copy_string(buf, varvalue, len);
    		else
    			*buf = '\0';
    	} else if (arg.argc == 1) {
    		char colnames[4096];
    		int i;
    		AST_DECLARE_APP_ARGS(arg2,
    			AST_APP_ARG(col)[100];
    		);
    
    		/* Get column names, in no particular order */
    		hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
    		pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
    
    
    		*buf = '\0';
    
    		/* Now get the corresponding column values, in exactly the same order */
    		for (i = 0; i < arg2.argc; i++) {
    			snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
    			varvalue = pbx_builtin_getvar_helper(chan, varname);
    
    			strncat(buf, varvalue, len - strlen(buf) - 1);
    			strncat(buf, ",", len - strlen(buf) - 1);
    
    		}
    
    		/* Strip trailing comma */
    		buf[strlen(buf) - 1] = '\0';
    	}
    
    	return 0;
    }
    
    static struct ast_custom_function hash_function = {
    	.name = "HASH",
    	.write = hash_write,
    	.read = hash_read,
    };
    
    static struct ast_custom_function hashkeys_function = {
    	.name = "HASHKEYS",
    	.read = hashkeys_read,
    
    	.read2 = hashkeys_read2,
    
    static struct ast_custom_function array_function = {
    
    	.name = "ARRAY",
    
    static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
    
    {
    	char *bufptr = buf, *dataptr = data;
    
    
    	if (len < 3){ /* at least two for quotes and one for binary zero */
    
    		ast_log(LOG_ERROR, "Not enough buffer\n");
    
    	if (ast_strlen_zero(data)) {
    		ast_log(LOG_WARNING, "No argument specified!\n");
    		ast_copy_string(buf, "\"\"", len);
    		return 0;
    	}
    
    
    	for (; bufptr < buf + len - 3; dataptr++) {
    
    		if (*dataptr == '\\') {
    			*bufptr++ = '\\';
    			*bufptr++ = '\\';
    		} else if (*dataptr == '"') {
    			*bufptr++ = '\\';
    			*bufptr++ = '"';
    		} else if (*dataptr == '\0') {
    			break;
    		} else {
    			*bufptr++ = *dataptr;
    		}
    	}
    	*bufptr++ = '"';
    	*bufptr = '\0';
    
    }
    
    static struct ast_custom_function quote_function = {
    	.name = "QUOTE",
    
    static int csv_quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
    {
    	char *bufptr = buf, *dataptr = data;
    
    
    	if (len < 3) { /* at least two for quotes and one for binary zero */
    
    		ast_log(LOG_ERROR, "Not enough buffer\n");
    
    		return -1;
    	}
    
    	if (ast_strlen_zero(data)) {
    
    		return 0;
    	}
    
    	*bufptr++ = '"';
    	for (; bufptr < buf + len - 3; dataptr++){
    		if (*dataptr == '"') {
    			*bufptr++ = '"';
    			*bufptr++ = '"';
    		} else if (*dataptr == '\0') {
    			break;
    		} else {
    			*bufptr++ = *dataptr;
    		}
    	}
    	*bufptr++ = '"';
    	*bufptr='\0';
    	return 0;
    }
    
    static struct ast_custom_function csv_quote_function = {
    	.name = "CSV_QUOTE",
    	.read = csv_quote,
    };
    
    static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
    
    		length = strlen(data);
    
    	snprintf(buf, buflen, "%d", length);
    
    static struct ast_custom_function len_function = {
    
    	.read_max = 12,
    
    static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
    
    	AST_DECLARE_APP_ARGS(args,
    
    			     AST_APP_ARG(epoch);
    			     AST_APP_ARG(timezone);
    			     AST_APP_ARG(format);
    
    	AST_STANDARD_APP_ARGS(args, parse);
    
    	ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
    	ast_localtime(&when, &tm, args.timezone);
    
    	if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
    
    		ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
    
    static struct ast_custom_function strftime_function = {
    
    static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
    
    {
    	AST_DECLARE_APP_ARGS(args,
    
    			     AST_APP_ARG(timestring);
    			     AST_APP_ARG(timezone);
    			     AST_APP_ARG(format);
    
    	buf[0] = '\0';
    
    	if (!data) {
    
    		ast_log(LOG_ERROR,
    				"Asterisk function STRPTIME() requires an argument.\n");
    		return -1;
    
    	}
    
    	AST_STANDARD_APP_ARGS(args, data);
    
    
    	if (ast_strlen_zero(args.format)) {
    		ast_log(LOG_ERROR,
    
    				"No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
    
    		return -1;
    
    	if (!ast_strptime(args.timestring, args.format, &tm)) {
    		ast_log(LOG_WARNING, "STRPTIME() found no time specified within the string\n");
    
    		when = ast_mktime(&tm, args.timezone);
    
    		snprintf(buf, buflen, "%d", (int) when.tv_sec);
    
    static struct ast_custom_function strptime_function = {
    
    	.name = "STRPTIME",
    	.read = acf_strptime,
    };
    
    
    static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (ast_strlen_zero(data)) {
    
    		ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
    
    		return -1;
    
    	pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
    
    static int function_eval2(struct ast_channel *chan, const char *cmd, char *data,
    			 struct ast_str **buf, ssize_t buflen)
    {
    	if (ast_strlen_zero(data)) {
    		ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
    		return -1;
    	}
    
    	ast_str_substitute_variables(buf, buflen, chan, data);
    
    	return 0;
    }
    
    
    static struct ast_custom_function eval_function = {
    
    	.name = "EVAL",
    	.read = function_eval,
    
    	.read2 = function_eval2,
    
    static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
    
    	for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
    
    		if (*dataptr == '\0') {
    			*bufptr++ = '\0';
    			break;
    		} else if (*dataptr == '1') {
    
    			*bufptr++ = '1';
    		} else if (strchr("AaBbCc2", *dataptr)) {
    			*bufptr++ = '2';
    		} else if (strchr("DdEeFf3", *dataptr)) {
    			*bufptr++ = '3';
    		} else if (strchr("GgHhIi4", *dataptr)) {
    			*bufptr++ = '4';
    		} else if (strchr("JjKkLl5", *dataptr)) {
    			*bufptr++ = '5';
    		} else if (strchr("MmNnOo6", *dataptr)) {
    			*bufptr++ = '6';
    		} else if (strchr("PpQqRrSs7", *dataptr)) {
    			*bufptr++ = '7';
    		} else if (strchr("TtUuVv8", *dataptr)) {
    			*bufptr++ = '8';
    		} else if (strchr("WwXxYyZz9", *dataptr)) {
    			*bufptr++ = '9';
    		} else if (*dataptr == '0') {
    			*bufptr++ = '0';
    		}
    	}
    
    
    	return 0;
    }
    
    static struct ast_custom_function keypadhash_function = {
    	.name = "KEYPADHASH",
    	.read = keypadhash,
    };
    
    
    static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
    
    	while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
    
    static int string_toupper2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
    {
    	char *bufptr, *dataptr = data;
    
    	if (buflen > -1) {
    		ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
    	}
    	bufptr = ast_str_buffer(*buf);
    	while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = toupper(*dataptr++)));
    	ast_str_update(*buf);
    
    	return 0;
    }
    
    
    static struct ast_custom_function toupper_function = {
    	.name = "TOUPPER",
    	.read = string_toupper,
    
    	.read2 = string_toupper2,
    
    static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
    
    	while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
    
    static int string_tolower2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
    {
    	char *bufptr, *dataptr = data;
    
    	if (buflen > -1) {
    		ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
    	}
    	bufptr = ast_str_buffer(*buf);
    	while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = tolower(*dataptr++)));
    	ast_str_update(*buf);
    
    	return 0;
    }
    
    
    static struct ast_custom_function tolower_function = {
    	.name = "TOLOWER",
    	.read = string_tolower,
    
    	.read2 = string_tolower2,
    
    static int shift_pop(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
    
    #define beginning	(cmd[0] == 'S') /* SHIFT */
    	char *after, delimiter[2] = ",", *varsubst;
    	size_t unused;
    	struct ast_str *before = ast_str_thread_get(&result_buf, 16);
    	char *(*search_func)(const char *s, int c) = (beginning ? strchr : strrchr);
    
    	AST_DECLARE_APP_ARGS(args,
    		AST_APP_ARG(var);
    		AST_APP_ARG(delimiter);
    	);
    
    
    
    	if (ast_strlen_zero(args.var)) {
    
    		ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
    
    	varsubst = ast_alloca(strlen(args.var) + 4);
    
    	sprintf(varsubst, "${%s}", args.var);
    	ast_str_substitute_variables(&before, 0, chan, varsubst);
    
    	if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
    		ast_get_encoded_char(args.delimiter, delimiter, &unused);
    
    	if (!ast_str_strlen(before)) {
    		/* Nothing to pop */
    		return -1;
    	}
    
    	if (!(after = search_func(ast_str_buffer(before), delimiter[0]))) {
    		/* Only one entry in array */
    		ast_str_set(buf, len, "%s", ast_str_buffer(before));
    
    		pbx_builtin_setvar_helper(chan, args.var, "");
    	} else {
    		*after++ = '\0';
    
    		ast_str_set(buf, len, "%s", beginning ? ast_str_buffer(before) : after);
    		pbx_builtin_setvar_helper(chan, args.var, beginning ? after : ast_str_buffer(before));
    
    }
    
    static struct ast_custom_function shift_function = {
    	.name = "SHIFT",
    
    };
    
    static struct ast_custom_function pop_function = {
    	.name = "POP",
    
    static int unshift_push(struct ast_channel *chan, const char *cmd, char *data, const char *new_value)
    
    #define beginning	(cmd[0] == 'U') /* UNSHIFT */
    	char delimiter[2] = ",", *varsubst;
    	size_t unused;
    	struct ast_str *buf, *previous_value;
    
    	AST_DECLARE_APP_ARGS(args,
    		AST_APP_ARG(var);
    		AST_APP_ARG(delimiter);
    	);
    
    
    	if (!(buf = ast_str_thread_get(&result_buf, 16)) ||
    		!(previous_value = ast_str_thread_get(&tmp_buf, 16))) {
    
    	if (ast_strlen_zero(args.var)) {
    		ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
    
    	if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
    		ast_get_encoded_char(args.delimiter, delimiter, &unused);
    
    	varsubst = ast_alloca(strlen(args.var) + 4);
    
    	sprintf(varsubst, "${%s}", args.var);
    	ast_str_substitute_variables(&previous_value, 0, chan, varsubst);
    
    	if (!ast_str_strlen(previous_value)) {
    		ast_str_set(&buf, 0, "%s", new_value);
    
    		ast_str_set(&buf, 0, "%s%c%s",
    			beginning ? new_value : ast_str_buffer(previous_value),
    			delimiter[0],
    			beginning ? ast_str_buffer(previous_value) : new_value);
    
    	}
    
    	pbx_builtin_setvar_helper(chan, args.var, ast_str_buffer(buf));
    
    	return 0;
    
    }
    
    static struct ast_custom_function push_function = {
    	.name = "PUSH",
    
    static struct ast_custom_function unshift_function = {
    	.name = "UNSHIFT",
    	.write = unshift_push,
    };
    
    static int passthru(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
    
    static struct ast_custom_function passthru_function = {
    	.name = "PASSTHRU",
    	.read2 = passthru,
    
    #ifdef TEST_FRAMEWORK
    
    AST_TEST_DEFINE(test_FIELDNUM)
    {
    	int i, res = AST_TEST_PASS;
    	struct ast_channel *chan;
    	struct ast_str *str;
    	char expression[256];
    	struct {
    		const char *fields;
    		const char *delim;
    		const char *field;
    		const char *expected;
    	} test_args[] = {
    		{"abc,def,ghi,jkl", "\\,",     "ghi", "3"},
    		{"abc def ghi jkl", " ",       "abc", "1"},
    		{"abc/def/ghi/jkl", "\\\\x2f", "def", "2"},
    		{"abc$def$ghi$jkl", "",        "ghi", "0"},
    		{"abc,def,ghi,jkl", "-",       "",    "0"},
    		{"abc-def-ghi-jkl", "-",       "mno", "0"}
    	};
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = "func_FIELDNUM_test";
    		info->category = "/funcs/func_strings/";
    		info->summary = "Test FIELDNUM function";
    		info->description = "Verify FIELDNUM 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(16))) {
    		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_args); i++) {
    		struct ast_var_t *var = ast_var_assign("FIELDS", test_args[i].fields);
    
    		AST_LIST_INSERT_HEAD(ast_channel_varshead(chan), var, entries);
    
    
    		snprintf(expression, sizeof(expression), "${FIELDNUM(%s,%s,%s)}", var->name, test_args[i].delim, test_args[i].field);
    		ast_str_substitute_variables(&str, 0, chan, expression);
    
    
    		AST_LIST_REMOVE(ast_channel_varshead(chan), var, entries);
    
    		ast_var_delete(var);
    
    		if (strcasecmp(ast_str_buffer(str), test_args[i].expected)) {
    			ast_test_status_update(test, "Evaluation of '%s' returned '%s' instead of the expected value '%s'\n",
    				expression, ast_str_buffer(str), test_args[i].expected);
    			res = AST_TEST_FAIL;
    			break;
    		}
    	}
    
    	ast_free(str);
    	ast_channel_release(chan);
    
    	return res;
    }
    
    
    AST_TEST_DEFINE(test_FILTER)
    {
    	int i, res = AST_TEST_PASS;
    	const char *test_strings[][2] = {
    		{"A-R",            "DAHDI"},
    		{"A\\-R",          "A"},
    		{"\\x41-R",        "DAHDI"},
    		{"0-9A-Ca-c",      "0042133333A12212"},
    		{"0-9a-cA-C_+\\-", "0042133333A12212"},
    		{NULL,             NULL},
    	};
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = "func_FILTER_test";
    
    		info->summary = "Test FILTER function";
    		info->description = "Verify FILTER behavior";
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	for (i = 0; test_strings[i][0]; i++) {
    		char tmp[256], tmp2[256] = "";
    		snprintf(tmp, sizeof(tmp), "${FILTER(%s,0042133333&DAHDI/g1/2212)}", test_strings[i][0]);
    		pbx_substitute_variables_helper(NULL, tmp, tmp2, sizeof(tmp2) - 1);
    		if (strcmp(test_strings[i][1], tmp2)) {
    			ast_test_status_update(test, "Format string '%s' substituted to '%s'.  Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][1]);
    			res = AST_TEST_FAIL;
    		}
    	}
    	return res;
    }
    
    Jonathan Rose's avatar
    Jonathan Rose committed
    
    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 */
    
    		{"foofoofoofoofoofoofoo", "foofoo", "bar", NULL, "barbarbarfoo"}, /* Found string within previous match */
    
    Jonathan Rose's avatar
    Jonathan Rose committed
    		{"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 <max-replacements> = 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(ast_channel_varshead(chan), var, entries);
    
    Jonathan Rose's avatar
    Jonathan Rose committed
    
    		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;
    }
    
    static int unload_module(void)
    
    	AST_TEST_UNREGISTER(test_FIELDNUM);
    
    	AST_TEST_UNREGISTER(test_FILTER);
    
    Jonathan Rose's avatar
    Jonathan Rose committed
    	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);
    
    Jonathan Rose's avatar
    Jonathan Rose committed
    	res |= ast_custom_function_unregister(&strreplace_function);
    
    	res |= ast_custom_function_unregister(&listfilter_function);
    
    	res |= ast_custom_function_unregister(&regex_function);
    	res |= ast_custom_function_unregister(&array_function);
    
    	res |= ast_custom_function_unregister(&quote_function);
    
    	res |= ast_custom_function_unregister(&csv_quote_function);
    
    	res |= ast_custom_function_unregister(&len_function);
    	res |= ast_custom_function_unregister(&strftime_function);
    	res |= ast_custom_function_unregister(&strptime_function);
    	res |= ast_custom_function_unregister(&eval_function);
    
    	res |= ast_custom_function_unregister(&keypadhash_function);
    
    	res |= ast_custom_function_unregister(&hashkeys_function);
    	res |= ast_custom_function_unregister(&hash_function);
    	res |= ast_unregister_application(app_clearhash);
    
    	res |= ast_custom_function_unregister(&toupper_function);
    	res |= ast_custom_function_unregister(&tolower_function);
    
    	res |= ast_custom_function_unregister(&shift_function);
    	res |= ast_custom_function_unregister(&pop_function);
    	res |= ast_custom_function_unregister(&push_function);
    	res |= ast_custom_function_unregister(&unshift_function);
    
    	res |= ast_custom_function_unregister(&passthru_function);
    
    static int load_module(void)
    
    	AST_TEST_REGISTER(test_FIELDNUM);
    
    	AST_TEST_REGISTER(test_FILTER);
    
    Jonathan Rose's avatar
    Jonathan Rose committed
    	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);
    
    Jonathan Rose's avatar
    Jonathan Rose committed
    	res |= ast_custom_function_register(&strreplace_function);
    
    	res |= ast_custom_function_register(&listfilter_function);
    
    	res |= ast_custom_function_register(&regex_function);
    	res |= ast_custom_function_register(&array_function);
    
    	res |= ast_custom_function_register(&quote_function);
    
    	res |= ast_custom_function_register(&csv_quote_function);
    
    	res |= ast_custom_function_register(&len_function);
    	res |= ast_custom_function_register(&strftime_function);
    	res |= ast_custom_function_register(&strptime_function);
    	res |= ast_custom_function_register(&eval_function);
    
    	res |= ast_custom_function_register(&keypadhash_function);
    
    	res |= ast_custom_function_register(&hashkeys_function);
    	res |= ast_custom_function_register(&hash_function);
    
    	res |= ast_register_application_xml(app_clearhash, exec_clearhash);
    
    	res |= ast_custom_function_register(&toupper_function);
    	res |= ast_custom_function_register(&tolower_function);
    
    	res |= ast_custom_function_register(&shift_function);
    	res |= ast_custom_function_register(&pop_function);
    	res |= ast_custom_function_register(&push_function);
    	res |= ast_custom_function_register(&unshift_function);
    
    	res |= ast_custom_function_register(&passthru_function);
    
    AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");