Skip to content
Snippets Groups Projects
Commit 6cc004dc authored by Naveen Albert's avatar Naveen Albert Committed by George Joseph
Browse files

app_read: Allow reading # as a digit

Allows for the digit # to be read as a digit,
just like any other DTMF digit, as opposed to
forcing it to be used as an end of input
indicator. The default behavior remains
unchanged.

ASTERISK-18454 #close

Change-Id: I3033432adb9d296ad227e76b540b8b4a2417665b
parent 6fbf55ac
No related branches found
No related tags found
3 merge requests!138Merge branch asterisk-20.3.0 into devel properly,!123Merge asterisk '20.3.0' into devel,!118Draft: manager: AOC-S support for AOCMessage
...@@ -75,6 +75,16 @@ ...@@ -75,6 +75,16 @@
<option name="n"> <option name="n">
<para>to read digits even if the line is not up.</para> <para>to read digits even if the line is not up.</para>
</option> </option>
<option name="t">
<para>Terminator digit(s) to use for ending input.
Default is <literal>#</literal>. If you need to read
the digit <literal>#</literal> literally, you should
remove or change the terminator character. Multiple
terminator characters may be specified. If no terminator
digit is present, input cannot be ended using digits
and you will need to rely on duration and max digits
for ending input.</para>
</option>
</optionlist> </optionlist>
</parameter> </parameter>
<parameter name="attempts"> <parameter name="attempts">
...@@ -114,12 +124,20 @@ enum read_option_flags { ...@@ -114,12 +124,20 @@ enum read_option_flags {
OPT_SKIP = (1 << 0), OPT_SKIP = (1 << 0),
OPT_INDICATION = (1 << 1), OPT_INDICATION = (1 << 1),
OPT_NOANSWER = (1 << 2), 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_OPTIONS(read_app_options, {
AST_APP_OPTION('s', OPT_SKIP), AST_APP_OPTION('s', OPT_SKIP),
AST_APP_OPTION('i', OPT_INDICATION), AST_APP_OPTION('i', OPT_INDICATION),
AST_APP_OPTION('n', OPT_NOANSWER), AST_APP_OPTION('n', OPT_NOANSWER),
AST_APP_OPTION_ARG('t', OPT_TERMINATOR, OPT_ARG_TERMINATOR),
}); });
static char *app = "Read"; static char *app = "Read";
...@@ -132,9 +150,11 @@ static int read_exec(struct ast_channel *chan, const char *data) ...@@ -132,9 +150,11 @@ static int read_exec(struct ast_channel *chan, const char *data)
int tries = 1, to = 0, x = 0; int tries = 1, to = 0, x = 0;
double tosec; double tosec;
char *argcopy = NULL; char *argcopy = NULL;
char *opt_args[OPT_ARG_ARRAY_SIZE];
struct ast_tone_zone_sound *ts = NULL; struct ast_tone_zone_sound *ts = NULL;
struct ast_flags flags = {0}; struct ast_flags flags = {0};
const char *status = "ERROR"; const char *status = "ERROR";
char *terminator = NULL; /* use default terminator # by default */
AST_DECLARE_APP_ARGS(arglist, AST_DECLARE_APP_ARGS(arglist,
AST_APP_ARG(variable); AST_APP_ARG(variable);
...@@ -156,7 +176,7 @@ static int read_exec(struct ast_channel *chan, const char *data) ...@@ -156,7 +176,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
AST_STANDARD_APP_ARGS(arglist, argcopy); AST_STANDARD_APP_ARGS(arglist, argcopy);
if (!ast_strlen_zero(arglist.options)) { 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)) { if (!ast_strlen_zero(arglist.attempts)) {
...@@ -192,6 +212,13 @@ static int read_exec(struct ast_channel *chan, const char *data) ...@@ -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); 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_channel_state(chan) != AST_STATE_UP) {
if (ast_test_flag(&flags, OPT_SKIP)) { if (ast_test_flag(&flags, OPT_SKIP)) {
/* At the user's option, skip if the line is not up */ /* 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) ...@@ -223,7 +250,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
break; break;
} }
tmp[x++] = res; tmp[x++] = res;
if (tmp[x-1] == '#') { if (strchr(terminator, tmp[x-1])) {
tmp[x-1] = '\0'; tmp[x-1] = '\0';
status = "OK"; status = "OK";
break; break;
...@@ -233,7 +260,7 @@ static int read_exec(struct ast_channel *chan, const char *data) ...@@ -233,7 +260,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
} }
} }
} else { } 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) if (res == AST_GETDATA_COMPLETE || res == AST_GETDATA_EMPTY_END_TERMINATED)
status = "OK"; status = "OK";
else if (res == AST_GETDATA_TIMEOUT) else if (res == AST_GETDATA_TIMEOUT)
......
Subject: app_read
A new option allows the digit '#' to be read literally,
rather than used exclusively as the input terminator
character.
...@@ -137,6 +137,23 @@ int ast_ivr_menu_run(struct ast_channel *c, struct ast_ivr_menu *menu, void *cbd ...@@ -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); 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 */ /*! \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); int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd);
......
...@@ -193,8 +193,25 @@ int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, ...@@ -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 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 maxlen How many digits to read (maximum)
* \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for * \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) 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; int res = 0, to, fto;
char *front, *filename; char *front, *filename;
...@@ -232,7 +249,7 @@ enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *promp ...@@ -232,7 +249,7 @@ enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *promp
fto = 50; fto = 50;
to = ast_channel_pbx(c) ? ast_channel_pbx(c)->dtimeoutms : 2000; 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) { if (res == AST_GETDATA_EMPTY_END_TERMINATED) {
return res; return res;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment