diff --git a/apps/app_read.c b/apps/app_read.c
index 6398281bec..dd48f05e13 100644
--- a/apps/app_read.c
+++ b/apps/app_read.c
@@ -75,6 +75,16 @@
+
@@ -114,12 +124,20 @@ enum read_option_flags {
OPT_SKIP = (1 << 0),
OPT_INDICATION = (1 << 1),
OPT_NOANSWER = (1 << 2),
+ OPT_TERMINATOR = (1 << 3),
+};
+
+enum {
+ OPT_ARG_TERMINATOR,
+ /* note: this entry _MUST_ be the last one in the enum */
+ OPT_ARG_ARRAY_SIZE,
};
AST_APP_OPTIONS(read_app_options, {
AST_APP_OPTION('s', OPT_SKIP),
AST_APP_OPTION('i', OPT_INDICATION),
AST_APP_OPTION('n', OPT_NOANSWER),
+ AST_APP_OPTION_ARG('t', OPT_TERMINATOR, OPT_ARG_TERMINATOR),
});
static char *app = "Read";
@@ -132,9 +150,11 @@ static int read_exec(struct ast_channel *chan, const char *data)
int tries = 1, to = 0, x = 0;
double tosec;
char *argcopy = NULL;
+ char *opt_args[OPT_ARG_ARRAY_SIZE];
struct ast_tone_zone_sound *ts = NULL;
struct ast_flags flags = {0};
const char *status = "ERROR";
+ char *terminator = NULL; /* use default terminator # by default */
AST_DECLARE_APP_ARGS(arglist,
AST_APP_ARG(variable);
@@ -156,7 +176,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
AST_STANDARD_APP_ARGS(arglist, argcopy);
if (!ast_strlen_zero(arglist.options)) {
- ast_app_parse_options(read_app_options, &flags, NULL, arglist.options);
+ ast_app_parse_options(read_app_options, &flags, opt_args, arglist.options);
}
if (!ast_strlen_zero(arglist.attempts)) {
@@ -192,6 +212,13 @@ static int read_exec(struct ast_channel *chan, const char *data)
ts = ast_get_indication_tone(ast_channel_zone(chan), arglist.filename);
}
}
+ if (ast_test_flag(&flags, OPT_TERMINATOR)) {
+ if (!ast_strlen_zero(arglist.filename)) {
+ terminator = opt_args[OPT_ARG_TERMINATOR];
+ } else {
+ terminator = ""; /* no digit inherently will terminate input */
+ }
+ }
if (ast_channel_state(chan) != AST_STATE_UP) {
if (ast_test_flag(&flags, OPT_SKIP)) {
/* At the user's option, skip if the line is not up */
@@ -223,7 +250,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
break;
}
tmp[x++] = res;
- if (tmp[x-1] == '#') {
+ if (strchr(terminator, tmp[x-1])) {
tmp[x-1] = '\0';
status = "OK";
break;
@@ -233,7 +260,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
}
}
} else {
- res = ast_app_getdata(chan, arglist.filename, tmp, maxdigits, to);
+ res = ast_app_getdata_terminator(chan, arglist.filename, tmp, maxdigits, to, terminator);
if (res == AST_GETDATA_COMPLETE || res == AST_GETDATA_EMPTY_END_TERMINATED)
status = "OK";
else if (res == AST_GETDATA_TIMEOUT)
diff --git a/doc/CHANGES-staging/app_read.txt b/doc/CHANGES-staging/app_read.txt
new file mode 100644
index 0000000000..df3247c1e1
--- /dev/null
+++ b/doc/CHANGES-staging/app_read.txt
@@ -0,0 +1,5 @@
+Subject: app_read
+
+A new option allows the digit '#' to be read literally,
+rather than used exclusively as the input terminator
+character.
diff --git a/include/asterisk/app.h b/include/asterisk/app.h
index 7690364f6d..ab246c8b7d 100644
--- a/include/asterisk/app.h
+++ b/include/asterisk/app.h
@@ -137,6 +137,23 @@ int ast_ivr_menu_run(struct ast_channel *c, struct ast_ivr_menu *menu, void *cbd
*/
int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout);
+/*! \brief Plays a stream and gets DTMF data from a channel
+ * \param c Which channel one is interacting with
+ * \param prompt File to pass to ast_streamfile (the one that you wish to play).
+ * It is also valid for this to be multiple files concatenated by "&".
+ * For example, "file1&file2&file3".
+ * \param s The location where the DTMF data will be stored
+ * \param maxlen Max Length of the data
+ * \param timeout Timeout length waiting for data(in milliseconds). Set to 0 for standard timeout(six seconds), or -1 for no time out.
+ * \param terminator A string of characters that may be used as terminators to end input. If NULL, "#" will be used.
+ *
+ * This function was designed for application programmers for situations where they need
+ * to play a message and then get some DTMF data in response to the message. If a digit
+ * is pressed during playback, it will immediately break out of the message and continue
+ * execution of your code.
+ */
+int ast_app_getdata_terminator(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, char *terminator);
+
/*! \brief Full version with audiofd and controlfd. NOTE: returns '2' on ctrlfd available, not '1' like other full functions */
int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd);
diff --git a/main/app.c b/main/app.c
index 09c0123604..f5fbffd7f7 100644
--- a/main/app.c
+++ b/main/app.c
@@ -193,8 +193,25 @@ int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect,
* \param s The string to read in to. Must be at least the size of your length
* \param maxlen How many digits to read (maximum)
* \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
- * "ludicrous time" (essentially never times out) */
+ * "ludicrous time" (essentially never times out)
+ */
enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
+{
+ return ast_app_getdata_terminator(c, prompt, s, maxlen, timeout, NULL);
+}
+
+/*!
+ * \brief ast_app_getdata
+ * \param c The channel to read from
+ * \param prompt The file to stream to the channel
+ * \param s The string to read in to. Must be at least the size of your length
+ * \param maxlen How many digits to read (maximum)
+ * \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
+ * "ludicrous time" (essentially never times out)
+ * \param terminator A string of characters that may be used as terminators to end input. Default if NULL is "#"
+ */
+enum ast_getdata_result ast_app_getdata_terminator(struct ast_channel *c, const char *prompt, char *s,
+ int maxlen, int timeout, char *terminator)
{
int res = 0, to, fto;
char *front, *filename;
@@ -232,7 +249,7 @@ enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *promp
fto = 50;
to = ast_channel_pbx(c) ? ast_channel_pbx(c)->dtimeoutms : 2000;
}
- res = ast_readstring(c, s, maxlen, to, fto, "#");
+ res = ast_readstring(c, s, maxlen, to, fto, S_OR(terminator, "#"));
if (res == AST_GETDATA_EMPTY_END_TERMINATED) {
return res;
}