Index: pbx.c
===================================================================
RCS file: /usr/cvsroot/asterisk/pbx.c,v
retrieving revision 1.206
diff -u -r1.206 pbx.c
--- pbx.c	23 Feb 2005 22:48:47 -0000	1.206
+++ pbx.c	24 Feb 2005 15:45:35 -0000
@@ -11,6 +11,8 @@
  * the GNU General Public License
  */
 
+#include <sys/types.h>
+#include <regex.h>
 #include <asterisk/lock.h>
 #include <asterisk/cli.h>
 #include <asterisk/pbx.h>
@@ -195,6 +197,8 @@
 
 static int autofallthrough = 0;
 
+static struct ast_custom_function_obj *acf_root = NULL;
+
 static struct pbx_builtin {
 	char name[AST_MAX_APP];
 	int (*execute)(struct ast_channel *chan, void *data);
@@ -1076,6 +1080,133 @@
 	}
 }
 
+static int handle_show_functions(int fd, int argc, char *argv[])
+{
+	struct ast_custom_function_obj *acfptr;
+
+	ast_cli(fd, "Installed Custom Functions:\n--------------------------------------------------------------------------------\n");
+	for (acfptr = acf_root ; acfptr ; acfptr = acfptr->next) {
+		ast_cli(fd, "%s\t(%s)\t[%s]\n", acfptr->name, acfptr->desc, acfptr->syntax);
+	}
+	ast_cli(fd, "\n");
+	return 0;
+}
+
+struct ast_custom_function_obj* ast_custom_function_find_obj(char *name) 
+{
+	struct ast_custom_function_obj *acfptr;
+
+	for (acfptr = acf_root ; acfptr ; acfptr = acfptr->next) {
+		if (!strcmp(name, acfptr->name)) {
+			break;
+		}
+	}	
+	
+	return acfptr;
+}
+
+int ast_custom_function_unregister(struct ast_custom_function_obj *acf) 
+{
+	struct ast_custom_function_obj *acfptr, *lastacf = NULL;
+
+	if (acf) {
+		for (acfptr = acf_root ; acfptr ; acfptr = acfptr->next) {
+			if (acfptr == acf) {
+				if (lastacf) {
+					lastacf->next = acf->next;
+				} else {
+					acf_root = acf->next;
+				}
+				ast_verbose(VERBOSE_PREFIX_1 "Unregistered custom function %s\n", acf->name);
+				return 0;
+			}
+			lastacf = acfptr;
+		}
+	}
+	return -1;
+}
+
+int ast_custom_function_register(struct ast_custom_function_obj *acf) 
+{
+	struct ast_custom_function_obj *acfptr;
+
+	if (acf) {
+		if((acfptr = ast_custom_function_find_obj(acf->name))) {
+			ast_log(LOG_ERROR, "Function %s already in use.\n", acf->name);
+			return -1;
+		}
+		acf->next = acf_root;
+		acf_root = acf;
+		ast_verbose(VERBOSE_PREFIX_1 "Registered custom function %s\n", acf->name);
+		return 0;
+	}
+
+	return -1;
+}
+
+static char *ast_func(char *in) {
+	char *out = NULL;
+	static char *ret = "0";
+	struct ast_custom_function_obj *acfptr;
+
+	if ((out = strchr(in, ' '))) {
+		*out = '\0';
+		out++;
+
+		if((acfptr = ast_custom_function_find_obj(in))) {
+			/* run the custom function */
+			return acfptr->function(in, out);
+		}
+	}
+	return strdup(ret);
+}
+
+static char *builtin_function_isnull(struct ast_channel *chan, char *cmd, char *data) 
+{
+	const char *ret_true = "1", *ret_false = "0";
+	return strdup(cmd ? ret_false : ret_true);
+}
+
+static char *builtin_function_exists(struct ast_channel *chan, char *cmd, char *data) 
+{
+	const char *ret_true = "1", *ret_false = "0";
+	return strdup(cmd ? ret_true : ret_false);
+}
+
+static char *builtin_function_regex(char *cmd, char *data)
+{
+	const char *ret_true = "1", *ret_false = "0", *ret;
+	char *arg, *tmp;
+	regex_t regexbuf;
+
+	ret = ret_false; /* convince me otherwise */
+	if ((tmp = ast_strdupa(data)) && (arg = strchr(tmp, '"')) && (data = strchr(arg+1, '"'))) {
+		arg++;
+		*data = '\0';
+		data++;
+		
+		if(data[0] == ' ') {
+			data++;
+		} else {
+			ast_log(LOG_WARNING, "Malformed input missing space in %s\n", cmd);
+			ret = ret_false;
+		}
+		
+		if (regcomp(&regexbuf, arg, REG_EXTENDED | REG_NOSUB)) {
+			ast_log(LOG_WARNING, "Malformed regex input %s\n", cmd);
+			ret = NULL;
+		}
+		ret = regexec(&regexbuf, data, 0, NULL, 0) ? ret_false : ret_true;
+		regfree(&regexbuf);
+		
+				
+	} else {
+		ast_log(LOG_WARNING, "Malformed input %s\n", cmd);
+	}
+
+	return strdup(ret);
+}
+
 static void pbx_substitute_variables_helper_full(struct ast_channel *c, const char *cp1, char *cp2, int count, struct varshead *headp)
 {
 	char *cp4;
@@ -1083,9 +1214,9 @@
 	int length;
 	char workspace[4096];
 	char ltmp[4096], var[4096];
-	char *nextvar, *nextexp;
+	char *nextvar, *nextexp, *nextfunc;
 	char *vars, *vare;
-	int pos, brackets, needsub, len;
+	int pos, brackets, needsub, len, needfunc;
 	
 	/* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
 	   zero-filled */
@@ -1099,13 +1230,27 @@
 		
 		/* Look for an expression */
 		nextexp = strstr(whereweare, "$[");
+
+		/* Look for a function */
+		nextfunc = strstr(whereweare, "$(");
 		
 		/* Pick the first one only */
-		if (nextvar && nextexp) {
-			if (nextvar < nextexp)
-				nextexp = NULL;
-			else
-				nextvar = NULL;
+		len = 0;
+		if (nextvar)
+			len++;
+		if(nextexp)
+			len++;
+		if(nextfunc)
+			len++;
+
+		if (len > 1) {
+			if(nextfunc) {
+				nextvar = nextexp = NULL;
+			} else if (nextvar) {
+                nextexp = nextfunc = NULL;
+			} else if (nextexp) {
+                nextvar = nextfunc = NULL;
+            }
 		}
 		
 		/* If there is one, we only go that far */
@@ -1113,7 +1258,9 @@
 			pos = nextvar - whereweare;
 		else if (nextexp)
 			pos = nextexp - whereweare;
-		
+		else if (nextfunc) {
+			pos = nextfunc - whereweare;
+		}
 		/* Can't copy more than 'count' bytes */
 		if (pos > count)
 			pos = count;
@@ -1184,7 +1331,8 @@
 			vars = vare = nextexp + 2;
 			brackets = 1;
 			needsub = 0;
-			
+			needfunc = 0;
+
 			/* Find the end of it */
 			while(brackets && *vare) {
 				if ((vare[0] == '$') && (vare[1] == '[')) {
@@ -1198,6 +1346,9 @@
 				} else if ((vare[0] == '$') && (vare[1] == '{')) {
 					needsub++;
 					vare++;
+				} else if ((vare[0] == '$') && (vare[1] == '(')) {
+					needfunc++;
+					vare++;
 				}
 				vare++;
 			}
@@ -1214,7 +1365,7 @@
 			var[len] = '\0';
 			
 			/* Substitute if necessary */
-			if (needsub) {
+			if (needsub || needfunc) {
 				memset(ltmp, 0, sizeof(ltmp));
 				pbx_substitute_variables_helper(c, var, ltmp, sizeof(ltmp) - 1);
 				vars = ltmp;
@@ -1236,6 +1387,63 @@
 				cp2 += length;
 				free(cp4);
 			}
+		} else if (nextfunc) {
+			/* We have a function.  Find the start and end */
+			vars = vare = nextfunc + 2;
+			brackets = 1;
+			needsub = 0;
+			
+			/* Find the end of it */
+			while(brackets && *vare) {
+				if ((vare[0] == '$') && (vare[1] == '(')) {
+					needsub++;
+					brackets++;
+					vare++;
+				} else if (vare[0] == '(') {
+					brackets++;
+				} else if (vare[0] == ')') {
+					brackets--;
+				} else if ((vare[0] == '$') && (vare[1] == '{')) {
+					needsub++;
+					vare++;
+				}
+				vare++;
+			}
+			if (brackets)
+				ast_log(LOG_NOTICE, "Error in extension logic (missing ')')\n");
+			len = vare - vars - 1;
+			
+			/* Skip totally over variable name */
+			whereweare += ( len + 3);
+			
+			/* Store variable name (and truncate) */
+			memset(var, 0, sizeof(var));
+			strncpy(var, vars, sizeof(var) - 1);
+			var[len] = '\0';
+			
+			/* Substitute if necessary */
+			if (needsub) {
+				memset(ltmp, 0, sizeof(ltmp));
+				pbx_substitute_variables_helper(c, var, ltmp, sizeof(ltmp) - 1);
+				vars = ltmp;
+			} else {
+				vars = var;
+			}
+
+			/* Evaluate expression */			
+			cp4 = ast_func(vars);
+			
+			ast_log(LOG_DEBUG, "Expression is '%s'\n", cp4);
+			
+			if (cp4) {
+				length = strlen(cp4);
+				if (length > count)
+					length = count;
+				memcpy(cp2, cp4, length);
+				count -= length;
+				cp2 += length;
+				free(cp4);
+			}
 			
 		} else
 			break;
@@ -1257,7 +1465,7 @@
 	memset(passdata, 0, datalen);
 		
 	/* No variables or expressions in e->data, so why scan it? */
-	if (!strstr(e->data,"${") && !strstr(e->data,"$[")) {
+	if (!strstr(e->data,"${") && !strstr(e->data,"$[") && !strstr(e->data,"$(")) {
 		strncpy(passdata, e->data, datalen - 1);
 		passdata[datalen-1] = '\0';
 		return;
@@ -2584,6 +2792,10 @@
 "Usage: show application <application> [<application> [<application> [...]]]\n"
 "       Describes a particular application.\n";
 
+static char show_functions_help[] =
+" Usage: show functions\n"
+"";
+
 static char show_applications_help[] =
 "Usage: show applications [{like|describing} <text>]\n"
 "       List applications which are currently available.\n"
@@ -3160,6 +3372,16 @@
 	return RESULT_SUCCESS;
 }
 
+
+/* custom commands */
+
+static struct ast_custom_function_obj regex_function = {
+    .name = "regex",
+    .desc = "Regular Expression",
+    .syntax = "$(regex \"<regular expression>\" <data>)",
+    .function = builtin_function_regex,
+};
+
 /*
  * CLI entries for upper commands ...
  */
@@ -3168,6 +3390,11 @@
 	handle_show_applications, "Shows registered applications",
 	show_applications_help, complete_show_applications };
 
+static struct ast_cli_entry show_functions_cli = 
+	{ { "show", "functions", NULL }, 
+	handle_show_functions, "Shows registered functions",
+	show_functions_help};
+
 static struct ast_cli_entry show_application_cli =
 	{ { "show", "application", NULL }, 
 	handle_show_application, "Describe a specific application",
@@ -5477,10 +5704,12 @@
 	}
         AST_LIST_HEAD_INIT(&globals);
 	ast_cli_register(&show_applications_cli);
+	ast_cli_register(&show_functions_cli);
 	ast_cli_register(&show_application_cli);
 	ast_cli_register(&show_dialplan_cli);
 	ast_cli_register(&show_switches_cli);
 	ast_cli_register(&show_hints_cli);
+	ast_custom_function_register(&regex_function);
 
 	/* Register builtin applications */
 	for (x=0; x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
Index: include/asterisk/pbx.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/pbx.h,v
retrieving revision 1.41
diff -u -r1.41 pbx.h
--- include/asterisk/pbx.h	2 Feb 2005 02:54:15 -0000	1.41
+++ include/asterisk/pbx.h	24 Feb 2005 15:45:36 -0000
@@ -53,6 +53,15 @@
 
 typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data);
 
+/*! Data structure associated with an asterisk custom function */
+struct ast_custom_function_obj {
+	char *name;
+	char *desc;
+	char *syntax;
+	char *(*function)(char *, char *);
+	struct ast_custom_function_obj *next;
+};
+
 /*! Data structure associated with an asterisk switch */
 struct ast_switch {
 	/*! NULL */
@@ -587,6 +596,10 @@
 int ast_parseable_goto(struct ast_channel *chan, const char *goto_string);
 int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority);
 int ast_async_goto_if_exists(struct ast_channel *chan, char* context, char *exten, int priority);
+struct ast_custom_function_obj* ast_custom_function_find_obj(char *name);
+int ast_custom_function_unregister(struct ast_custom_function_obj *acf);
+int ast_custom_function_register(struct ast_custom_function_obj *acf);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
