diff --git a/configs/features.conf.sample b/configs/features.conf.sample
index 6e582f7a3d45424b31566550b8d56b74dd9d1bce..e8e428010b9ad7ab1a2d648557f0c57f14be1863 100644
--- a/configs/features.conf.sample
+++ b/configs/features.conf.sample
@@ -77,9 +77,11 @@ context => parkedcalls		; Which context parked calls are in (default parking lot
 ;  on the outbound channels, as well.  Otherwise, only the original channel
 ;  will have access to these features.)
 ;
-; The syntax for declaring a dynamic feature is the following:
+; The syntax for declaring a dynamic feature is any of the following:
 ;
 ;<FeatureName> => <DTMF_sequence>,<ActivateOn>[/<ActivatedBy>],<Application>[,<AppArguments>[,MOH_Class]]
+;<FeatureName> => <DTMF_sequence>,<ActivateOn>[/<ActivatedBy>],<Application>[,"<AppArguments>"[,MOH_Class]]
+;<FeatureName> => <DTMF_sequence>,<ActivateOn>[/<ActivatedBy>],<Application>([<AppArguments>])[,MOH_Class]
 ;
 ;  FeatureName   -> This is the name of the feature used in when setting the
 ;                   DYNAMIC_FEATURES variable to enable usage of this feature.
@@ -94,7 +96,9 @@ context => parkedcalls		; Which context parked calls are in (default parking lot
 ;                   The "caller" is the channel that executed the Dial application, while
 ;                   the "callee" is the channel called by the Dial application.
 ;  Application   -> This is the application to execute.
-;  AppArguments  -> These are the arguments to be passed into the application.
+;  AppArguments  -> These are the arguments to be passed into the application.  If you need
+;                   commas in your arguments, you should use either the second or third
+;                   syntax, above.
 ;  MOH_Class     -> This is the music on hold class to play while the idle
 ;                   channel waits for the feature to complete. If left blank,
 ;                   no music will be played.
@@ -116,6 +120,10 @@ context => parkedcalls		; Which context parked calls are in (default parking lot
 ;testfeature => #9,peer,Playback,tt-monkeys  ;Allow both the caller and callee to play
 ;                                            ;tt-monkeys to the opposite channel
 ;
+; Set arbitrary channel variables, based upon CALLERID number (Note that the application
+; argument contains commas)
+;retrieveinfo => #8,peer,Set(ARRAY(CDR(mark),CDR(name))=${ODBC_FOO(${CALLERID(num)})})
+;
 ;pauseMonitor   => #1,self/callee,Pausemonitor     ;Allow the callee to pause monitoring
 ;                                                  ;on their channel
 ;unpauseMonitor => #3,self/callee,UnPauseMonitor   ;Allow the callee to unpause monitoring
diff --git a/main/features.c b/main/features.c
index 012dcd35596896f0bbf6a6fb9737be8631963be8..0041b2782b8de2c830b3f4fd1eeab0d760b5b1c4 100644
--- a/main/features.c
+++ b/main/features.c
@@ -4419,25 +4419,33 @@ static int load_config(void)
 	ast_unregister_features();
 	for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
 		char *tmp_val = ast_strdupa(var->value);
-		char *exten, *activateon, *activatedby, *app, *app_args, *moh_class; 
+		char *activateon; 
 		struct ast_call_feature *feature;
+		AST_DECLARE_APP_ARGS(args,
+			AST_APP_ARG(exten);
+			AST_APP_ARG(activatedby);
+			AST_APP_ARG(app);
+			AST_APP_ARG(app_args);
+			AST_APP_ARG(moh_class);
+		);
 
-		/* strsep() sets the argument to NULL if match not found, and it
-		 * is safe to use it with a NULL argument, so we don't check
-		 * between calls.
-		 */
-		exten = strsep(&tmp_val,",");
-		activatedby = strsep(&tmp_val,",");
-		app = strsep(&tmp_val,",");
-		app_args = strsep(&tmp_val,",");
-		moh_class = strsep(&tmp_val,",");
+		AST_STANDARD_APP_ARGS(args, tmp_val);
+		if (strchr(args.app, '(')) {
+			/* New syntax */
+			args.moh_class = args.app_args;
+			args.app_args = strchr(args.app, '(');
+			*args.app_args++ = '\0';
+			if (args.app_args[strlen(args.app_args) - 1] == ')') {
+				args.app_args[strlen(args.app_args) - 1] = '\0';
+			}
+		}
 
-		activateon = strsep(&activatedby, "/");	
+		activateon = strsep(&args.activatedby, "/");	
 
 		/*! \todo XXX var_name or app_args ? */
-		if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
+		if (ast_strlen_zero(args.app) || ast_strlen_zero(args.exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
 			ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
-				app, exten, activateon, var->name);
+				args.app, args.exten, activateon, var->name);
 			continue;
 		}
 
@@ -4449,20 +4457,23 @@ static int load_config(void)
 		}
 		AST_RWLIST_UNLOCK(&feature_list);
 				
-		if (!(feature = ast_calloc(1, sizeof(*feature))))
-			continue;					
+		if (!(feature = ast_calloc(1, sizeof(*feature)))) {
+			continue;
+		}
 
 		ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
-		ast_copy_string(feature->app, app, FEATURE_APP_LEN);
-		ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN);
+		ast_copy_string(feature->app, args.app, FEATURE_APP_LEN);
+		ast_copy_string(feature->exten, args.exten, FEATURE_EXTEN_LEN);
 		
-		if (app_args) 
-			ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN);
+		if (args.app_args) {
+			ast_copy_string(feature->app_args, args.app_args, FEATURE_APP_ARGS_LEN);
+		}
 
-		if (moh_class)
-			ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN);
-			
-		ast_copy_string(feature->exten, exten, sizeof(feature->exten));
+		if (args.moh_class) {
+			ast_copy_string(feature->moh_class, args.moh_class, FEATURE_MOH_LEN);
+		}
+
+		ast_copy_string(feature->exten, args.exten, sizeof(feature->exten));
 		feature->operation = feature_exec_app;
 		ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
 
@@ -4477,13 +4488,13 @@ static int load_config(void)
 			continue;
 		}
 
-		if (ast_strlen_zero(activatedby))
+		if (ast_strlen_zero(args.activatedby))
 			ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
-		else if (!strcasecmp(activatedby, "caller"))
+		else if (!strcasecmp(args.activatedby, "caller"))
 			ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
-		else if (!strcasecmp(activatedby, "callee"))
+		else if (!strcasecmp(args.activatedby, "callee"))
 			ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
-		else if (!strcasecmp(activatedby, "both"))
+		else if (!strcasecmp(args.activatedby, "both"))
 			ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
 		else {
 			ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
@@ -4492,8 +4503,8 @@ static int load_config(void)
 		}
 
 		ast_register_feature(feature);
-			
-		ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);
+
+		ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, args.app, args.app_args, args.exten);
 	}
 
 	ast_unregister_groups();