From 426360e3897a4bd682c71e10ea0415071411735a Mon Sep 17 00:00:00 2001
From: "Kevin P. Fleming" <kpfleming@digium.com>
Date: Thu, 3 Nov 2005 21:19:11 +0000
Subject: [PATCH] major update to arg/option parsing APIs and documentation

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@6953 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 ChangeLog                  |   5 ++
 app.c                      |  23 ++++---
 apps/app_chanspy.c         |  39 +++++++-----
 apps/app_controlplayback.c |   2 +-
 apps/app_cut.c             |   2 +-
 apps/app_dial.c            |  52 ++++++++--------
 apps/app_dictate.c         |   2 +-
 apps/app_meetme.c          |  46 +++++++-------
 apps/app_mixmonitor.c      |  51 +++++++++-------
 apps/app_page.c            |  16 ++---
 apps/app_read.c            |   2 +-
 apps/app_voicemail.c       |  42 +++++++------
 channels/chan_agent.c      |   2 +-
 funcs/func_cdr.c           |   4 +-
 funcs/func_db.c            |   6 +-
 funcs/func_math.c          |   2 +-
 funcs/func_md5.c           |   2 +-
 include/asterisk/app.h     | 120 ++++++++++++++++++++++++++++++++-----
 pbx.c                      |  35 +++++------
 19 files changed, 283 insertions(+), 170 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 6a4915ced0..c58b18cc69 100755
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2005-11-03  Kevin P. Fleming  <kpfleming@digium.com>
+
+	* include/asterisk/app.h: re-work application arg/option parsing APIs for consistent naming, add doxygen docs for option API
+	* many files: update to new APIs
+
 2005-11-02  Kevin P. Fleming  <kpfleming@digium.com>
 
 	* apps/app_dial.c (dial_exec_full): convert to use API calls for argument/option parsing
diff --git a/app.c b/app.c
index 0a72bec427..b76387825d 100755
--- a/app.c
+++ b/app.c
@@ -1105,7 +1105,7 @@ int ast_app_group_match_get_count(char *groupmatch, char *category)
 	return count;
 }
 
-int ast_separate_app_args(char *buf, char delim, char **array, int arraylen)
+unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
 {
 	int argc;
 	char *scan;
@@ -1523,41 +1523,40 @@ char *ast_read_textfile(const char *filename)
 	return output;
 }
 
-int ast_parseoptions(const struct ast_option *options, struct ast_flags *flags, char **args, char *optstr)
+int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
 {
 	char *s;
 	int curarg;
-	int argloc;
+	unsigned int argloc;
 	char *arg;
 	int res = 0;
 
-	flags->flags = 0;
+	ast_clear_flag(flags, AST_FLAGS_ALL);
 
 	if (!optstr)
 		return 0;
 
 	s = optstr;
 	while (*s) {
-		curarg = *s & 0x7f;
-		flags->flags |= options[curarg].flag;
+		curarg = *s++ & 0x7f;
+		ast_set_flag(flags, options[curarg].flag);
 		argloc = options[curarg].arg_index;
-		s++;
 		if (*s == '(') {
 			/* Has argument */
-			s++;
 			arg = s;
-			while (*s && (*s != ')')) s++;
+			while (*++s && (*s != ')'));
 			if (*s) {
 				if (argloc)
 					args[argloc - 1] = arg;
-				*s = '\0';
-				s++;
+				*s++ = '\0';
 			} else {
 				ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
 				res = -1;
 			}
-		} else if (argloc)
+		} else if (argloc) {
 			args[argloc - 1] = NULL;
+		}
 	}
+
 	return res;
 }
diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c
index d9138ed00f..116441cb91 100755
--- a/apps/app_chanspy.c
+++ b/apps/app_chanspy.c
@@ -69,18 +69,27 @@ static const char *desc = "   Chanspy([<scanspec>][|<options>])\n\n"
 
 static const char *chanspy_spy_type = "ChanSpy";
 
-#define OPTION_QUIET	 (1 << 0)	/* Quiet, no announcement */
-#define OPTION_BRIDGED   (1 << 1)	/* Only look at bridged calls */
-#define OPTION_VOLUME    (1 << 2)	/* Specify initial volume */
-#define OPTION_GROUP     (1 << 3)   /* Only look at channels in group */
-#define OPTION_RECORD    (1 << 4)   /* Record */
-
-AST_DECLARE_OPTIONS(chanspy_opts,{
-	['q'] = { OPTION_QUIET },
-	['b'] = { OPTION_BRIDGED },
-	['v'] = { OPTION_VOLUME, 1 },
-	['g'] = { OPTION_GROUP, 2 },
-	['r'] = { OPTION_RECORD, 3 },
+enum {
+	OPTION_QUIET	 = (1 << 0),	/* Quiet, no announcement */
+	OPTION_BRIDGED   = (1 << 1),	/* Only look at bridged calls */
+	OPTION_VOLUME    = (1 << 2),	/* Specify initial volume */
+	OPTION_GROUP     = (1 << 3),	/* Only look at channels in group */
+	OPTION_RECORD    = (1 << 4),	/* Record */
+} chanspy_opt_flags;
+
+enum {
+	OPT_ARG_VOLUME = 0,
+	OPT_ARG_GROUP,
+	OPT_ARG_RECORD,
+	OPT_ARG_ARRAY_SIZE,
+} chanspy_opt_args;
+
+AST_APP_OPTIONS(chanspy_opts, {
+	AST_APP_OPTION('q', OPTION_QUIET),
+	AST_APP_OPTION('b', OPTION_BRIDGED),
+	AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
+	AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
+	AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
 });
 
 STANDARD_LOCAL_USER;
@@ -384,7 +393,7 @@ static int chanspy_exec(struct ast_channel *chan, void *data)
 
 	ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
 
-	if ((argc = ast_separate_app_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
+	if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
 		spec = argv[0];
 		if ( argc > 1) {
 			options = argv[1];
@@ -395,8 +404,8 @@ static int chanspy_exec(struct ast_channel *chan, void *data)
 	}
 	
 	if (options) {
-		char *opts[3];
-		ast_parseoptions(chanspy_opts, &flags, opts, options);
+		char *opts[OPT_ARG_ARRAY_SIZE];
+		ast_app_parse_options(chanspy_opts, &flags, opts, options);
 		if (ast_test_flag(&flags, OPTION_GROUP)) {
 			mygroup = opts[1];
 		}
diff --git a/apps/app_controlplayback.c b/apps/app_controlplayback.c
index 21320ac9da..0c56c926f5 100755
--- a/apps/app_controlplayback.c
+++ b/apps/app_controlplayback.c
@@ -91,7 +91,7 @@ static int controlplayback_exec(struct ast_channel *chan, void *data)
 	tmp = ast_strdupa(data);
 	memset(argv, 0, sizeof(argv));
 
-	argc = ast_separate_app_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
+	argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
 
 	if (argc < 1) {
 		ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
diff --git a/apps/app_cut.c b/apps/app_cut.c
index 95b5b1ded1..ab41d34379 100755
--- a/apps/app_cut.c
+++ b/apps/app_cut.c
@@ -160,7 +160,7 @@ static int cut_internal(struct ast_channel *chan, char *data, char *buffer, size
 	if (data) {
 		s = ast_strdupa((char *)data);
 		if (s) {
-			ast_separate_app_args(s, '|', args, 3);
+			ast_app_separate_args(s, '|', args, 3);
 			varname = args[0];
 			delimiter = args[1];
 			field = args[2];
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 50b2761c00..bfaefba1e7 100755
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -670,31 +670,31 @@ enum {
 	OPT_ARG_ARRAY_SIZE,
 } dial_exec_option_args;
 
-AST_DECLARE_OPTIONS(dial_exec_options, {
-	['A'] = { .flag = OPT_ANNOUNCE, .arg_index = OPT_ARG_ANNOUNCE + 1, },
-	['C'] = { .flag = OPT_RESETCDR, },
-	['d'] = { .flag = OPT_DTMF_EXIT, },
-	['D'] = { .flag = OPT_SENDDTMF, .arg_index = OPT_ARG_SENDDTMF + 1, },
-	['f'] = { .flag = OPT_FORCECLID, },
-	['g'] = { .flag = OPT_GO_ON, },
-	['G'] = { .flag = OPT_GOTO, .arg_index = OPT_ARG_GOTO + 1, },
-	['h'] = { .flag = OPT_CALLEE_HANGUP, },
-	['H'] = { .flag = OPT_CALLER_HANGUP, },
-	['j'] = { .flag = OPT_PRIORITY_JUMP, },
-	['L'] = { .flag = OPT_DURATION_LIMIT, .arg_index = OPT_ARG_DURATION_LIMIT + 1, },
-	['m'] = { .flag = OPT_MUSICBACK, .arg_index = OPT_ARG_MUSICBACK + 1, },
-	['M'] = { .flag = OPT_CALLEE_MACRO, .arg_index = OPT_ARG_CALLEE_MACRO + 1, },
-	['n'] = { .flag = OPT_SCREEN_NOINTRO, },
-	['N'] = { .flag = OPT_SCREEN_NOCLID, },
-	['o'] = { .flag = OPT_ORIGINAL_CLID, },
-	['p'] = { .flag = OPT_SCREENING, },
-	['P'] = { .flag = OPT_PRIVACY, .arg_index = OPT_ARG_PRIVACY + 1, },
-	['r'] = { .flag = OPT_RINGBACK, },
-	['S'] = { .flag = OPT_DURATION_STOP, .arg_index = OPT_ARG_DURATION_STOP + 1, },
-	['t'] = { .flag = OPT_CALLEE_TRANSFER, },
-	['T'] = { .flag = OPT_CALLER_TRANSFER, },
-	['w'] = { .flag = OPT_CALLEE_MONITOR, },
-	['W'] = { .flag = OPT_CALLER_MONITOR, },
+AST_APP_OPTIONS(dial_exec_options, {
+	AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE),
+	AST_APP_OPTION('C', OPT_RESETCDR),
+	AST_APP_OPTION('d', OPT_DTMF_EXIT),
+	AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF),
+	AST_APP_OPTION('f', OPT_FORCECLID),
+	AST_APP_OPTION('g', OPT_GO_ON),
+	AST_APP_OPTION_ARG('G', OPT_GOTO, OPT_ARG_GOTO),
+	AST_APP_OPTION('h', OPT_CALLEE_HANGUP),
+	AST_APP_OPTION('H', OPT_CALLER_HANGUP),
+	AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
+	AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
+	AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK),
+	AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO),
+	AST_APP_OPTION('n', OPT_SCREEN_NOINTRO),
+	AST_APP_OPTION('N', OPT_SCREEN_NOCLID),
+	AST_APP_OPTION('o', OPT_ORIGINAL_CLID),
+	AST_APP_OPTION('p', OPT_SCREENING),
+	AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY),
+	AST_APP_OPTION('r', OPT_RINGBACK),
+	AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP),
+	AST_APP_OPTION('t', OPT_CALLEE_TRANSFER),
+	AST_APP_OPTION('T', OPT_CALLER_TRANSFER),
+	AST_APP_OPTION('w', OPT_CALLEE_MONITOR),
+	AST_APP_OPTION('W', OPT_CALLER_MONITOR),
 });
 
 static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags)
@@ -762,7 +762,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
 	AST_STANDARD_APP_ARGS(args, parse);
 
 	if (!ast_strlen_zero(args.options)) {
-		if (ast_parseoptions(dial_exec_options, &opts, opt_args, args.options)) {
+		if (ast_app_parse_options(dial_exec_options, &opts, opt_args, args.options)) {
 			LOCAL_USER_REMOVE(u);
 			return -1;
 		}
diff --git a/apps/app_dictate.c b/apps/app_dictate.c
index 0df5a56041..71c7fb8637 100755
--- a/apps/app_dictate.c
+++ b/apps/app_dictate.c
@@ -102,7 +102,7 @@ static int dictate_exec(struct ast_channel *chan, void *data)
 	
 	snprintf(dftbase, sizeof(dftbase), "%s/dictate", ast_config_AST_SPOOL_DIR);
 	if (!ast_strlen_zero(data) && (mydata = ast_strdupa(data))) {
-		argc = ast_separate_app_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
+		argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
 	}
 	
 	if (argc) {
diff --git a/apps/app_meetme.c b/apps/app_meetme.c
index 88e5b76ffd..742b6ec68a 100755
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -219,28 +219,28 @@ static void *recordthread(void *args);
 #define CONFFLAG_ANNOUNCEUSERCOUNT (1 << 22) /* If set, when user joins the conference, they will be told the number of users that are already in */
 
 
-AST_DECLARE_OPTIONS(meetme_opts,{
-	['a'] = { CONFFLAG_ADMIN },
-	['c'] = { CONFFLAG_ANNOUNCEUSERCOUNT },
-	['T'] = { CONFFLAG_MONITORTALKER },
-	['i'] = { CONFFLAG_INTROUSER },
-	['m'] = { CONFFLAG_MONITOR },
-	['p'] = { CONFFLAG_POUNDEXIT },
-	['s'] = { CONFFLAG_STARMENU },
-	['t'] = { CONFFLAG_TALKER },
-	['q'] = { CONFFLAG_QUIET },
-	['M'] = { CONFFLAG_MOH },
-	['x'] = { CONFFLAG_MARKEDEXIT },
-	['X'] = { CONFFLAG_EXIT_CONTEXT },
-	['A'] = { CONFFLAG_MARKEDUSER },
-	['b'] = { CONFFLAG_AGI },
-	['w'] = { CONFFLAG_WAITMARKED },
-	['r'] = { CONFFLAG_RECORDCONF },
-	['d'] = { CONFFLAG_DYNAMIC },
-	['D'] = { CONFFLAG_DYNAMICPIN },
-	['e'] = { CONFFLAG_EMPTY },
-	['E'] = { CONFFLAG_EMPTYNOPIN },
-	['P'] = { CONFFLAG_ALWAYSPROMPT },
+AST_APP_OPTIONS(meetme_opts, {
+	AST_APP_OPTION('a', CONFFLAG_ADMIN ),
+	AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
+	AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
+	AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
+	AST_APP_OPTION('m', CONFFLAG_MONITOR ),
+	AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
+	AST_APP_OPTION('s', CONFFLAG_STARMENU ),
+	AST_APP_OPTION('t', CONFFLAG_TALKER ),
+	AST_APP_OPTION('q', CONFFLAG_QUIET ),
+	AST_APP_OPTION('M', CONFFLAG_MOH ),
+	AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
+	AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
+	AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
+	AST_APP_OPTION('b', CONFFLAG_AGI ),
+	AST_APP_OPTION('w', CONFFLAG_WAITMARKED ),
+	AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
+	AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
+	AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
+	AST_APP_OPTION('e', CONFFLAG_EMPTY ),
+	AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
+	AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
 });
 
 static char *istalking(int x)
@@ -1727,7 +1727,7 @@ static int conf_exec(struct ast_channel *chan, void *data)
 		ast_copy_string(the_pin, inpin, sizeof(the_pin));
 
 	if (inflags) {
-		ast_parseoptions(meetme_opts, &confflags, NULL, inflags);
+		ast_app_parse_options(meetme_opts, &confflags, NULL, inflags);
 		dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
 		if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !inpin)
 			strcpy(the_pin, "q");
diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c
index 554a02c8e2..5ad56e4fd2 100755
--- a/apps/app_mixmonitor.c
+++ b/apps/app_mixmonitor.c
@@ -85,19 +85,26 @@ struct mixmonitor {
 };
 
 enum {
-    MUXFLAG_APPEND = (1 << 1),
-    MUXFLAG_BRIDGED = (1 << 2),
-    MUXFLAG_VOLUME = (1 << 3),
-    MUXFLAG_READVOLUME = (1 << 4),
-    MUXFLAG_WRITEVOLUME = (1 << 5),
+	MUXFLAG_APPEND = (1 << 1),
+	MUXFLAG_BRIDGED = (1 << 2),
+	MUXFLAG_VOLUME = (1 << 3),
+	MUXFLAG_READVOLUME = (1 << 4),
+	MUXFLAG_WRITEVOLUME = (1 << 5),
 } mixmonitor_flags;
 
-AST_DECLARE_OPTIONS(mixmonitor_opts,{
-	['a'] = { MUXFLAG_APPEND },
-	['b'] = { MUXFLAG_BRIDGED },
-	['v'] = { MUXFLAG_READVOLUME, 1 },
-	['V'] = { MUXFLAG_WRITEVOLUME, 2 },
-	['W'] = { MUXFLAG_VOLUME, 3 },
+enum {
+	OPT_ARG_READVOLUME = 0,
+	OPT_ARG_WRITEVOLUME,
+	OPT_ARG_VOLUME,
+	OPT_ARG_ARRAY_SIZE,
+} mixmonitor_args;
+
+AST_APP_OPTIONS(mixmonitor_opts, {
+	AST_APP_OPTION('a', MUXFLAG_APPEND),
+	AST_APP_OPTION('b', MUXFLAG_BRIDGED),
+	AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
+	AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
+	AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
 });
 
 static void stopmon(struct ast_channel *chan, struct ast_channel_spy *spy) 
@@ -331,35 +338,35 @@ static int mixmonitor_exec(struct ast_channel *chan, void *data)
 	}
 
 	if (args.options) {
-		char *opts[3] = { NULL, };
+		char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
 
-		ast_parseoptions(mixmonitor_opts, &flags, opts, args.options);
+		ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
 
 		if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
-			if (ast_strlen_zero(opts[0])) {
+			if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
 				ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
-			} else if ((sscanf(opts[0], "%d", &x) != 1) || (x < -4) || (x > 4)) {
-				ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[0]);
+			} else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
+				ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
 			} else {
 				readvol = get_volfactor(x);
 			}
 		}
 		
 		if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
-			if (ast_strlen_zero(opts[1])) {
+			if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
 				ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
-			} else if ((sscanf(opts[1], "%d", &x) != 1) || (x < -4) || (x > 4)) {
-				ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[1]);
+			} else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
+				ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
 			} else {
 				writevol = get_volfactor(x);
 			}
 		}
 		
 		if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
-			if (ast_strlen_zero(opts[2])) {
+			if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
 				ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
-			} else if ((sscanf(opts[2], "%d", &x) != 1) || (x < -4) || (x > 4)) {
-				ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[2]);
+			} else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) {
+				ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
 			} else {
 				readvol = writevol = get_volfactor(x);
 			}
diff --git a/apps/app_page.c b/apps/app_page.c
index 921316d812..d2a65f4e84 100755
--- a/apps/app_page.c
+++ b/apps/app_page.c
@@ -61,12 +61,14 @@ STANDARD_LOCAL_USER;
 
 LOCAL_USER_DECL;
 
-#define PAGE_DUPLEX (1 << 0)
-#define PAGE_QUIET  (1 << 1)
-
-AST_DECLARE_OPTIONS(page_opts,{
-	['d'] = { PAGE_DUPLEX },
-	['q'] = { PAGE_QUIET },
+enum {
+	PAGE_DUPLEX = (1 << 0),
+	PAGE_QUIET = (1 << 1),
+} page_opt_flags;
+
+AST_APP_OPTIONS(page_opts, {
+	AST_APP_OPTION('d', PAGE_DUPLEX),
+	AST_APP_OPTION('q', PAGE_QUIET),
 });
 
 struct calloutdata {
@@ -142,7 +144,7 @@ static int page_exec(struct ast_channel *chan, void *data)
 
 	tmp = strsep(&options, "|");
 	if (options)
-		ast_parseoptions(page_opts, &flags, NULL, options);
+		ast_app_parse_options(page_opts, &flags, NULL, options);
 
 	snprintf(meetmeopts, sizeof(meetmeopts), "%ud|%sqxdw", confid, ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "m");
 	while ((tech = strsep(&tmp, "&"))) {
diff --git a/apps/app_read.c b/apps/app_read.c
index 0c4e2872b7..ed4272da6d 100755
--- a/apps/app_read.c
+++ b/apps/app_read.c
@@ -103,7 +103,7 @@ static int read_exec(struct ast_channel *chan, void *data)
 		return -1;
 	}
 
-	if (ast_separate_app_args(argcopy, '|', args, sizeof(args) / sizeof(args[0])) < 1) {
+	if (ast_app_separate_args(argcopy, '|', args, sizeof(args) / sizeof(args[0])) < 1) {
 		ast_log(LOG_WARNING, "Cannot Parse Arguments.\n");
 		LOCAL_USER_REMOVE(u);
 		return -1;
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index fab9015901..1ab7dceff6 100755
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -107,21 +107,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #define ERROR_LOCK_PATH		-100
 
-#define OPT_SILENT 		(1 << 0)
-#define OPT_BUSY_GREETING	(1 << 1)
-#define OPT_UNAVAIL_GREETING	(1 << 2)
-#define OPT_RECORDGAIN		(1 << 3)
-#define OPT_PREPEND_MAILBOX	(1 << 4)
-
-#define OPT_ARG_RECORDGAIN	0
-#define OPT_ARG_ARRAY_SIZE	1
-
-AST_DECLARE_OPTIONS(vm_app_options, {
-	['s'] = { .flag = OPT_SILENT },
-	['b'] = { .flag = OPT_BUSY_GREETING },
-	['u'] = { .flag = OPT_UNAVAIL_GREETING },
-	['g'] = { .flag = OPT_RECORDGAIN, .arg_index = OPT_ARG_RECORDGAIN + 1},
-	['p'] = { .flag = OPT_PREPEND_MAILBOX },
+enum {
+	OPT_SILENT =(1 << 0),
+	OPT_BUSY_GREETING = (1 << 1),
+	OPT_UNAVAIL_GREETING = (1 << 2),
+	OPT_RECORDGAIN = (1 << 3),
+	OPT_PREPEND_MAILBOX = (1 << 4),
+} vm_option_flags;
+
+enum {
+	OPT_ARG_RECORDGAIN = 0,
+	OPT_ARG_ARRAY_SIZE = 1,
+} vm_option_args;
+
+AST_APP_OPTIONS(vm_app_options, {
+	AST_APP_OPTION('s', OPT_SILENT),
+	AST_APP_OPTION('b', OPT_BUSY_GREETING),
+	AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
+	AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
+	AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
 });
 
 static int load_config(void);
@@ -5033,9 +5037,9 @@ static int vm_execmain(struct ast_channel *chan, void *data)
 		char *opts[OPT_ARG_ARRAY_SIZE];
 
 		tmp = ast_strdupa(data);
-		argc = ast_separate_app_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
+		argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
 		if (argc == 2) {
-			if (ast_parseoptions(vm_app_options, &flags, opts, argv[1])) {
+			if (ast_app_parse_options(vm_app_options, &flags, opts, argv[1])) {
 				LOCAL_USER_REMOVE(u);
 				return -1;
 			}
@@ -5460,9 +5464,9 @@ static int vm_exec(struct ast_channel *chan, void *data)
 
 	if (!ast_strlen_zero(data)) {
 		ast_copy_string(tmp, data, sizeof(tmp));
-		argc = ast_separate_app_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
+		argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
 		if (argc == 2) {
-			if (ast_parseoptions(vm_app_options, &flags, opts, argv[1])) {
+			if (ast_app_parse_options(vm_app_options, &flags, opts, argv[1])) {
 				LOCAL_USER_REMOVE(u);
 				return -1;
 			}
diff --git a/channels/chan_agent.c b/channels/chan_agent.c
index 8859afcbd7..812e87be0e 100755
--- a/channels/chan_agent.c
+++ b/channels/chan_agent.c
@@ -317,7 +317,7 @@ static struct agent_pvt *add_agent(char *agent, int pending)
 	args = ast_strdupa(agent);
 
 	// Extract username (agt), password and name from agent (args).
-	if ((argc = ast_separate_app_args(args, ',', argv, sizeof(argv) / sizeof(argv[0])))) {
+	if ((argc = ast_app_separate_args(args, ',', argv, sizeof(argv) / sizeof(argv[0])))) {
 		agt = argv[0];
 		if (argc > 1) {
 			password = argv[1];
diff --git a/funcs/func_cdr.c b/funcs/func_cdr.c
index 1612d54eba..66bef45510 100755
--- a/funcs/func_cdr.c
+++ b/funcs/func_cdr.c
@@ -52,7 +52,7 @@ static char *builtin_function_cdr_read(struct ast_channel *chan, char *cmd, char
 		return NULL;
 
 	mydata = ast_strdupa(data);
-	argc = ast_separate_app_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
+	argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
 
 	/* check for a trailing flags argument */
 	if (argc > 1) {
@@ -77,7 +77,7 @@ static void builtin_function_cdr_write(struct ast_channel *chan, char *cmd, char
 		return;
 	
 	mydata = ast_strdupa(data);
-	argc = ast_separate_app_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
+	argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
 
 	/* check for a trailing flags argument */
 	if (argc > 1) {
diff --git a/funcs/func_db.c b/funcs/func_db.c
index 728bdb2188..a01af59c12 100755
--- a/funcs/func_db.c
+++ b/funcs/func_db.c
@@ -55,7 +55,7 @@ static char *function_db_read(struct ast_channel *chan, char *cmd, char *data, c
 	}
 
 	args = ast_strdupa(data);
-	argc = ast_separate_app_args(args, '/', argv, sizeof(argv) / sizeof(argv[0]));
+	argc = ast_app_separate_args(args, '/', argv, sizeof(argv) / sizeof(argv[0]));
 	
 	if (argc > 1) {
 		family = argv[0];
@@ -88,7 +88,7 @@ static void function_db_write(struct ast_channel *chan, char *cmd, char *data, c
 	}
 
 	args = ast_strdupa(data);
-	argc = ast_separate_app_args(args, '/', argv, sizeof(argv) / sizeof(argv[0]));
+	argc = ast_app_separate_args(args, '/', argv, sizeof(argv) / sizeof(argv[0]));
 	
 	if (argc > 1) {
 		family = argv[0];
@@ -134,7 +134,7 @@ static char *function_db_exists(struct ast_channel *chan, char *cmd, char *data,
 	}
 
 	args = ast_strdupa(data);
-	argc = ast_separate_app_args(args, '/', argv, sizeof(argv) / sizeof(argv[0]));
+	argc = ast_app_separate_args(args, '/', argv, sizeof(argv) / sizeof(argv[0]));
 	
 	if (argc > 1) {
 		family = argv[0];
diff --git a/funcs/func_math.c b/funcs/func_math.c
index 876fb22111..46085275d5 100755
--- a/funcs/func_math.c
+++ b/funcs/func_math.c
@@ -84,7 +84,7 @@ static char *builtin_function_math(struct ast_channel *chan, char *cmd, char *da
 	}
 
 	args = ast_strdupa(data);	
-	argc = ast_separate_app_args(args, '|', argv, sizeof(argv) / sizeof(argv[0]));
+	argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0]));
 
 	if (argc < 1) {
 		ast_log(LOG_WARNING, "Syntax: Math(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
diff --git a/funcs/func_md5.c b/funcs/func_md5.c
index f320d07496..fed75722c3 100755
--- a/funcs/func_md5.c
+++ b/funcs/func_md5.c
@@ -64,7 +64,7 @@ static char *builtin_function_checkmd5(struct ast_channel *chan, char *cmd, char
 	}
 
 	args = ast_strdupa(data);	
-	argc = ast_separate_app_args(args, '|', argv, sizeof(argv) / sizeof(argv[0]));
+	argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0]));
 
 	if (argc < 2) {
 		ast_log(LOG_WARNING, "Syntax: CHECK_MD5(<digest>,<data>) - missing argument!\n");
diff --git a/include/asterisk/app.h b/include/asterisk/app.h
index 5f64f33916..7cc02163c5 100755
--- a/include/asterisk/app.h
+++ b/include/asterisk/app.h
@@ -73,17 +73,7 @@ struct ast_ivr_menu {
 
 #define AST_IVR_FLAG_AUTORESTART (1 << 0)
 
-struct ast_option {
-	unsigned int flag;
-	int arg_index;
-};
-
-extern int ast_parseoptions(const struct ast_option *options, struct ast_flags *flags, char **args, char *optstr);
-
-#define AST_DECLARE_OPTIONS(holder,args...) \
-	static struct ast_option holder[128] = args
-
-#define AST_IVR_DECLARE_MENU(holder,title,flags,foo...) \
+#define AST_IVR_DECLARE_MENU(holder, title, flags, foo...) \
 	static struct ast_ivr_option __options_##holder[] = foo;\
 	static struct ast_ivr_menu holder = { title, flags, __options_##holder }
 	
@@ -204,7 +194,7 @@ int ast_app_group_match_get_count(char *groupmatch, char *category);
  */
 #define AST_DECLARE_APP_ARGS(name, arglist) \
 	struct { \
-		int argc; \
+		unsigned int argc; \
 		char *argv[0]; \
 		arglist \
 	} name;
@@ -219,7 +209,7 @@ int ast_app_group_match_get_count(char *groupmatch, char *category);
   the argc argument counter field.
  */
 #define AST_STANDARD_APP_ARGS(args, parse) \
-	args.argc = ast_separate_app_args(parse, '|', args.argv, (sizeof(args) - sizeof(args.argc)) / sizeof(args.argv[0]))
+	args.argc = ast_app_separate_args(parse, '|', args.argv, (sizeof(args) - sizeof(args.argc)) / sizeof(args.argv[0]))
 	
 /*!
   \brief Separate a string into arguments in an array
@@ -235,7 +225,109 @@ int ast_app_group_match_get_count(char *groupmatch, char *category);
 
   \return The number of arguments found, or zero if the function arguments are not valid.
 */
-int ast_separate_app_args(char *buf, char delim, char **array, int arraylen);
+unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen);
+
+/*!
+  \brief A structure to hold the description of an application 'option'.
+
+  Application 'options' are single-character flags that can be supplied
+  to the application to affect its behavior; they can also optionally
+  accept arguments enclosed in parenthesis.
+
+  These structures are used by the ast_app_parse_options function, uses
+  this data to fill in a flags structure (to indicate which options were
+  supplied) and array of argument pointers (for those options that had
+  arguments supplied).
+ */
+struct ast_app_option {
+	/*! \brief The flag bit that represents this option. */
+	unsigned int flag;
+	/*! \brief The index of the entry in the arguments array
+	  that should be used for this option's argument. */
+	unsigned int arg_index;
+};
+
+/*!
+  \brief Declares an array of options for an application.
+  \param holder The name of the array to be created
+  \param options The actual options to be placed into the array
+  \sa ast_app_parse_options
+
+  This macro declares a 'static const' array of \c struct \c ast_option
+  elements to hold the list of available options for an application.
+  Each option must be declared using either the AST_APP_OPTION()
+  or AST_APP_OPTION_ARG() macros.
+
+  Example usage:
+  \code
+  enum {
+        OPT_JUMP = (1 << 0),
+        OPT_BLAH = (1 << 1),
+        OPT_BLORT = (1 << 2),
+  } my_app_option_flags;
+
+  enum {
+        OPT_ARG_BLAH = 0,
+        OPT_ARG_BLORT,
+        !! this entry tells how many possible arguments there are,
+           and must be the last entry in the list
+        OPT_ARG_ARRAY_SIZE,
+  } my_app_option_args;
+
+  AST_APP_OPTIONS(my_app_options, {
+        AST_APP_OPTION('j', OPT_JUMP),
+        AST_APP_OPTION_ARG('b', OPT_BLAH, OPT_ARG_BLAH),
+        AST_APP_OPTION_BLORT('B', OPT_BLORT, OPT_ARG_BLORT),
+  });
+
+  static int my_app_exec(struct ast_channel *chan, void *data)
+  {
+  	char *options;
+	struct ast_flags opts = { 0, };
+	char *opt_args[OPT_ARG_ARRAY_SIZE];
+
+  	... do any argument parsing here ...
+
+	if (ast_parseoptions(my_app_options, &opts, opt_args, options)) {
+		LOCAL_USER_REMOVE(u);
+		return -1;
+	}
+  }
+  \endcode
+ */
+#define AST_APP_OPTIONS(holder, options...) \
+	static const struct ast_app_option holder[128] = options
+
+/*!
+  \brief Declares an application option that does not accept an argument.
+  \param option The single character representing the option
+  \param flagno The flag index to be set if this option is present
+  \sa AST_APP_OPTIONS, ast_app_parse_options
+ */
+#define AST_APP_OPTION(option, flagno) \
+	[option] = { .flag = flagno }
+
+/*!
+  \brief Declares an application option that accepts an argument.
+  \param option The single character representing the option
+  \param flagno The flag index to be set if this option is present
+  \param argno The index into the argument array where the argument should
+  be placed
+  \sa AST_APP_OPTIONS, ast_app_parse_options
+ */
+#define AST_APP_OPTION_ARG(option, flagno, argno) \
+	[option] = { .flag = flagno, .arg_index = argno }
+
+/*!
+  \brief Parses a string containing application options and sets flags/arguments.
+  \param options The array of possible options declared with AST_APP_OPTIONS
+  \param flags The flag structure to have option flags set
+  \param args The array of argument pointers to hold arguments found
+  \param optstr The string containing the options to be parsed
+  \return zero for success, non-zero if an error occurs
+  \sa AST_APP_OPTIONS
+ */
+int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr);
 
 /*! Present a dialtone and collect a certain length extension.  Returns 1 on valid extension entered, -1 on hangup, or 0 on invalid extension. Note that if 'collect' holds digits already, new digits will be appended, so be sure it's initialized properly */
 int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout);
diff --git a/pbx.c b/pbx.c
index c5459886f6..0d721e1368 100755
--- a/pbx.c
+++ b/pbx.c
@@ -86,17 +86,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #define BACKGROUND_MATCHEXTEN	(1 << 2)
 #define BACKGROUND_PLAYBACK	(1 << 3)
 
-AST_DECLARE_OPTIONS(background_opts,{
-	['s'] = { BACKGROUND_SKIP },
-	['n'] = { BACKGROUND_NOANSWER },
-	['m'] = { BACKGROUND_MATCHEXTEN },
-	['p'] = { BACKGROUND_PLAYBACK },
+AST_APP_OPTIONS(background_opts, {
+	AST_APP_OPTION('s', BACKGROUND_SKIP),
+	AST_APP_OPTION('n', BACKGROUND_NOANSWER),
+	AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
+	AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
 });
 
 #define WAITEXTEN_MOH		(1 << 0)
 
-AST_DECLARE_OPTIONS(waitexten_opts,{
-	['m'] = { WAITEXTEN_MOH, 1 },
+AST_APP_OPTIONS(waitexten_opts, {
+	AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 1),
 });
 
 struct ast_context;
@@ -5616,13 +5616,13 @@ static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
 	char *args;
 	char *argv[2];
 	char *options = NULL; 
-	char *mohclass = NULL;
 	char *timeout = NULL;
 	struct ast_flags flags = {0};
+	char *opts[1] = { NULL };
 
 	args = ast_strdupa(data);
 
-	if ((argc = ast_separate_app_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
+	if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
 		if (argc > 0) {
 			timeout = argv[0];
 			if (argc > 1)
@@ -5630,16 +5630,11 @@ static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
 		}
 	}
 
-	if (options) {
-		char *opts[1];
-		ast_parseoptions(waitexten_opts, &flags, opts, options);
-		if (ast_test_flag(&flags, WAITEXTEN_MOH)) {
-			mohclass = opts[0];
-		}
-	}
+	if (options)
+		ast_app_parse_options(waitexten_opts, &flags, opts, options);
 	
 	if (ast_test_flag(&flags, WAITEXTEN_MOH))
-		ast_moh_start(chan, mohclass);
+		ast_moh_start(chan, opts[0]);
 
 	/* Wait for "n" seconds */
 	if (timeout && atof((char *)timeout)) 
@@ -5685,7 +5680,7 @@ static int pbx_builtin_background(struct ast_channel *chan, void *data)
 
 	parse = ast_strdupa(data);
 
-	if ((argc = ast_separate_app_args(parse, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
+	if ((argc = ast_app_separate_args(parse, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
 		switch (argc) {
 		case 4:
 			context = argv[3];
@@ -5714,7 +5709,7 @@ static int pbx_builtin_background(struct ast_channel *chan, void *data)
 		else if (!strcasecmp(options, "noanswer"))
 			flags.flags = BACKGROUND_NOANSWER;
 		else
-			ast_parseoptions(background_opts, &flags, NULL, options);
+			ast_app_parse_options(background_opts, &flags, NULL, options);
 	}
 
 	/* Answer if need be */
@@ -5948,7 +5943,7 @@ int pbx_builtin_setvar(struct ast_channel *chan, void *data)
 	}
 
 	mydata = ast_strdupa(data);
-	argc = ast_separate_app_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
+	argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
 
 	/* check for a trailing flags argument */
 	if ((argc > 1) && !strchr(argv[argc-1], '=')) {
-- 
GitLab