func_sayfiles: Retrieve say file names

Up until now, all of the logic used to translate
arguments to the Say applications has been
directly coupled to playback, preventing other
modules from using this logic.

This refactors code in say.c and adds a SAYFILES
function that can be used to retrieve the file
names that would be played. These can then be
used in other applications or for other purposes.

Additionally, a SayMoney application and a SayOrdinal
application are added. Both SayOrdinal and SayNumber
are also expanded to support integers greater than
one billion.

ASTERISK-29531

Change-Id: If9718c89353b8e153d84add3cc4637b79585db19
This commit is contained in:
Naveen Albert
2021-07-26 17:46:44 +00:00
committed by George Joseph
parent a6eb1b6f95
commit b6b7b1490b
6 changed files with 1056 additions and 95 deletions

View File

@@ -8257,6 +8257,12 @@ int ast_say_number(struct ast_channel *chan, int num,
return ast_say_number_full(chan, num, ints, language, options, -1, -1);
}
int ast_say_ordinal(struct ast_channel *chan, int num,
const char *ints, const char *language, const char *options)
{
return ast_say_ordinal_full(chan, num, ints, language, options, -1, -1);
}
int ast_say_enumeration(struct ast_channel *chan, int num,
const char *ints, const char *language, const char *options)
{
@@ -8275,6 +8281,12 @@ int ast_say_digit_str(struct ast_channel *chan, const char *str,
return ast_say_digit_str_full(chan, str, ints, lang, -1, -1);
}
int ast_say_money_str(struct ast_channel *chan, const char *str,
const char *ints, const char *lang)
{
return ast_say_money_str_full(chan, str, ints, lang, -1, -1);
}
int ast_say_character_str(struct ast_channel *chan, const char *str,
const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity)
{

View File

@@ -37,6 +37,7 @@
#include "asterisk/say.h"
#include "asterisk/app.h"
#include "asterisk/module.h"
#include "asterisk/conversions.h"
#include "pbx_private.h"
/*** DOCUMENTATION
@@ -440,9 +441,12 @@
</description>
<see-also>
<ref type="application">SayDigits</ref>
<ref type="application">SayMoney</ref>
<ref type="application">SayNumber</ref>
<ref type="application">SayOrdinal</ref>
<ref type="application">SayPhonetic</ref>
<ref type="function">CHANNEL</ref>
<ref type="function">SAYFILES</ref>
</see-also>
</application>
<application name="SayAlphaCase" language="en_US">
@@ -481,7 +485,9 @@
</description>
<see-also>
<ref type="application">SayDigits</ref>
<ref type="application">SayMoney</ref>
<ref type="application">SayNumber</ref>
<ref type="application">SayOrdinal</ref>
<ref type="application">SayPhonetic</ref>
<ref type="application">SayAlpha</ref>
<ref type="function">CHANNEL</ref>
@@ -503,9 +509,35 @@
</description>
<see-also>
<ref type="application">SayAlpha</ref>
<ref type="application">SayMoney</ref>
<ref type="application">SayNumber</ref>
<ref type="application">SayOrdinal</ref>
<ref type="application">SayPhonetic</ref>
<ref type="function">CHANNEL</ref>
<ref type="function">SAYFILES</ref>
</see-also>
</application>
<application name="SayMoney" language="en_US">
<synopsis>
Say Money.
</synopsis>
<syntax>
<parameter name="dollars" required="true" />
</syntax>
<description>
<para>This application will play the currency sounds for the given floating point number
in the current language. Currently only English and US Dollars is supported.
If the channel variable <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true'
(case insensitive), then this application will react to DTMF in the same way as
<literal>Background</literal>.</para>
</description>
<see-also>
<ref type="application">SayAlpha</ref>
<ref type="application">SayNumber</ref>
<ref type="application">SayOrdinal</ref>
<ref type="application">SayPhonetic</ref>
<ref type="function">CHANNEL</ref>
<ref type="function">SAYFILES</ref>
</see-also>
</application>
<application name="SayNumber" language="en_US">
@@ -527,8 +559,37 @@
<see-also>
<ref type="application">SayAlpha</ref>
<ref type="application">SayDigits</ref>
<ref type="application">SayMoney</ref>
<ref type="application">SayPhonetic</ref>
<ref type="function">CHANNEL</ref>
<ref type="function">SAYFILES</ref>
</see-also>
</application>
<application name="SayOrdinal" language="en_US">
<synopsis>
Say Ordinal Number.
</synopsis>
<syntax>
<parameter name="digits" required="true" />
<parameter name="gender" />
</syntax>
<description>
<para>This application will play the ordinal sounds that correspond to the given
<replaceable>digits</replaceable> (e.g. 1st, 42nd). Currently only English is supported.</para>
<para>Optionally, a <replaceable>gender</replaceable> may be
specified. This will use the language that is currently set for the channel. See the CHANNEL()
function for more information on setting the language for the channel. If the channel variable
<variable>SAY_DTMF_INTERRUPT</variable> is set to 'true' (case insensitive), then this
application will react to DTMF in the same way as <literal>Background</literal>.</para>
</description>
<see-also>
<ref type="application">SayAlpha</ref>
<ref type="application">SayDigits</ref>
<ref type="application">SayMoney</ref>
<ref type="application">SayNumber</ref>
<ref type="application">SayPhonetic</ref>
<ref type="function">CHANNEL</ref>
<ref type="function">SAYFILES</ref>
</see-also>
</application>
<application name="SayPhonetic" language="en_US">
@@ -547,7 +608,10 @@
<see-also>
<ref type="application">SayAlpha</ref>
<ref type="application">SayDigits</ref>
<ref type="application">SayMoney</ref>
<ref type="application">SayNumber</ref>
<ref type="application">SayOrdinal</ref>
<ref type="function">SAYFILES</ref>
</see-also>
</application>
<application name="SetAMAFlags" language="en_US">
@@ -1283,7 +1347,7 @@ static int pbx_builtin_saynumber(struct ast_channel *chan, const char *data)
ast_copy_string(tmp, data, sizeof(tmp));
strsep(&number, ",");
if (sscanf(tmp, "%d", &number_val) != 1) {
if (ast_str_to_int(tmp, &number_val)) {
ast_log(LOG_WARNING, "argument '%s' to SayNumber could not be parsed as a number.\n", tmp);
return 0;
}
@@ -1306,6 +1370,53 @@ static int pbx_builtin_saynumber(struct ast_channel *chan, const char *data)
return interrupt ? res : 0;
}
static int pbx_builtin_sayordinal(struct ast_channel *chan, const char *data)
{
char tmp[256];
char *number = tmp;
int number_val;
char *options;
int res;
int interrupt = 0;
const char *interrupt_string;
ast_channel_lock(chan);
interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
if (ast_true(interrupt_string)) {
interrupt = 1;
}
ast_channel_unlock(chan);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "SayOrdinal requires an argument (number)\n");
return -1;
}
ast_copy_string(tmp, data, sizeof(tmp));
strsep(&number, ",");
if (ast_str_to_int(tmp, &number_val)) {
ast_log(LOG_WARNING, "argument '%s' to SayOrdinal could not be parsed as a number.\n", tmp);
return 0;
}
options = strsep(&number, ",");
if (options) {
if ( strcasecmp(options, "f") && strcasecmp(options, "m") &&
strcasecmp(options, "c") && strcasecmp(options, "n") ) {
ast_log(LOG_WARNING, "SayOrdinal gender option is either 'f', 'm', 'c' or 'n'\n");
return -1;
}
}
res = ast_say_ordinal(chan, number_val, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan), options);
if (res < 0 && !ast_check_hangup_locked(chan)) {
ast_log(LOG_WARNING, "We were unable to say the number %s, is it too large?\n", tmp);
}
return interrupt ? res : 0;
}
static int pbx_builtin_saydigits(struct ast_channel *chan, const char *data)
{
int res = 0;
@@ -1326,6 +1437,26 @@ static int pbx_builtin_saydigits(struct ast_channel *chan, const char *data)
return res;
}
static int pbx_builtin_saymoney(struct ast_channel *chan, const char *data)
{
int res = 0;
int interrupt = 0;
const char *interrupt_string;
ast_channel_lock(chan);
interrupt_string = pbx_builtin_getvar_helper(chan, "SAY_DTMF_INTERRUPT");
if (ast_true(interrupt_string)) {
interrupt = 1;
}
ast_channel_unlock(chan);
if (data) {
res = ast_say_money_str(chan, data, interrupt ? AST_DIGIT_ANY : "", ast_channel_language(chan));
}
return res;
}
static int pbx_builtin_saycharacters_case(struct ast_channel *chan, const char *data)
{
int res = 0;
@@ -1483,7 +1614,9 @@ struct pbx_builtin {
{ "SayAlpha", pbx_builtin_saycharacters },
{ "SayAlphaCase", pbx_builtin_saycharacters_case },
{ "SayDigits", pbx_builtin_saydigits },
{ "SayMoney", pbx_builtin_saymoney },
{ "SayNumber", pbx_builtin_saynumber },
{ "SayOrdinal", pbx_builtin_sayordinal },
{ "SayPhonetic", pbx_builtin_sayphonetic },
{ "SetAMAFlags", pbx_builtin_setamaflags },
{ "Wait", pbx_builtin_wait },

View File

@@ -29,6 +29,8 @@
* Next Generation Networks (NGN).
* \note 2007-03-20 : Support for Thai added by Dome C. <dome@tel.co.th>,
* IP Crossing Co., Ltd.
* \note 2021-07-26 : Refactoring to separate string buildup and playback
* by Naveen Albert <asterisk@phreaknet.org>
*/
/*** MODULEINFO
@@ -58,9 +60,7 @@
/* Forward declaration */
static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity, int audiofd, int ctrlfd)
{
struct ast_str* ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity) {
const char *fn;
char fnbuf[10], asciibuf[20] = "letters/ascii";
char ltr;
@@ -69,6 +69,12 @@ static int say_character_str_full(struct ast_channel *chan, const char *str, con
int upper = 0;
int lower = 0;
struct ast_str *filenames = ast_str_create(20);
if (!filenames) {
return NULL;
}
ast_str_reset(filenames);
while (str[num] && !res) {
fn = NULL;
switch (str[num]) {
@@ -154,14 +160,7 @@ static int say_character_str_full(struct ast_channel *chan, const char *str, con
}
if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
(snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
res = ast_streamfile(chan, fn, lang);
if (!res) {
if ((audiofd > -1) && (ctrlfd > -1))
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
else
res = ast_waitstream(chan, ints);
}
ast_stopstream(chan);
ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);
}
if (upper || lower) {
continue;
@@ -169,18 +168,56 @@ static int say_character_str_full(struct ast_channel *chan, const char *str, con
num++;
}
return filenames;
}
static int say_filenames(struct ast_channel *chan, const char *ints, const char *lang, int audiofd, int ctrlfd, struct ast_str *filenames)
{
int res = 0;
char *files;
const char *fn;
if (!filenames) {
return -1;
}
files = ast_str_buffer(filenames);
while ((fn = strsep(&files, "&"))) {
res = ast_streamfile(chan, fn, lang);
if (!res) {
if ((audiofd > -1) && (ctrlfd > -1))
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
else
res = ast_waitstream(chan, ints);
}
ast_stopstream(chan);
}
ast_free(filenames);
return res;
}
static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity, int audiofd, int ctrlfd)
{
struct ast_str *filenames = ast_get_character_str(str, lang, sensitivity);
return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
}
struct ast_str* ast_get_phonetic_str(const char *str, const char *lang)
{
const char *fn;
char fnbuf[256];
char ltr;
int num = 0;
int res = 0;
while (str[num] && !res) {
struct ast_str *filenames = ast_str_create(20);
if (!filenames) {
return NULL;
}
ast_str_reset(filenames);
while (str[num]) {
fn = NULL;
switch (str[num]) {
case ('*'):
@@ -237,29 +274,33 @@ static int say_phonetic_str_full(struct ast_channel *chan, const char *str, cons
fn = fnbuf;
}
if (fn && ast_fileexists(fn, NULL, lang) > 0) {
res = ast_streamfile(chan, fn, lang);
if (!res) {
if ((audiofd > -1) && (ctrlfd > -1))
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
else
res = ast_waitstream(chan, ints);
}
ast_stopstream(chan);
ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);
}
num++;
}
return res;
return filenames;
}
static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
{
struct ast_str *filenames = ast_get_phonetic_str(str, lang);
return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
}
struct ast_str* ast_get_digit_str(const char *str, const char *lang)
{
const char *fn;
char fnbuf[256];
int num = 0;
int res = 0;
while (str[num] && !res) {
struct ast_str *filenames = ast_str_create(20);
if (!filenames) {
return NULL;
}
ast_str_reset(filenames);
while (str[num]) {
fn = NULL;
switch (str[num]) {
case ('*'):
@@ -287,19 +328,340 @@ static int say_digit_str_full(struct ast_channel *chan, const char *str, const c
break;
}
if (fn && ast_fileexists(fn, NULL, lang) > 0) {
res = ast_streamfile(chan, fn, lang);
if (!res) {
if ((audiofd > -1) && (ctrlfd > -1))
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
else
res = ast_waitstream(chan, ints);
}
ast_stopstream(chan);
ast_str_append(&filenames, 0, (num == 0 ? "%s" : "&%s"), fn);
}
num++;
}
return res;
return filenames;
}
static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
{
struct ast_str *filenames = ast_get_digit_str(str, lang);
return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
}
static struct ast_str* ast_get_money_en_dollars_str(const char *str, const char *lang)
{
const char *fnr;
double dollars = 0;
int amt, cents;
struct ast_str *fnrecurse = NULL;
struct ast_str *filenames = ast_str_create(20);
if (!filenames) {
return NULL;
}
ast_str_reset(filenames);
if (sscanf(str, "%30lf", &dollars) != 1) {
amt = 0;
} else { /* convert everything to cents */
amt = dollars * 100;
}
/* Just the cents after the dollar decimal point */
cents = amt - (((int) dollars) * 100);
ast_debug(1, "Cents is %d, amount is %d\n", cents, amt);
if (amt >= 100) {
fnrecurse = ast_get_number_str((amt / 100), lang);
if (!fnrecurse) {
ast_log(LOG_WARNING, "Couldn't get string for dollars\n");
} else {
fnr = ast_str_buffer(fnrecurse);
ast_str_append(&filenames, 0, "%s", fnr);
}
/* If this is it, end on a down pitch, otherwise up pitch */
if (amt < 200) {
ast_str_append(&filenames, 0, "&%s", (cents > 0) ? "letters/dollar_" : "letters/dollar");
} else {
ast_str_append(&filenames, 0, "&%s", "dollars");
}
/* If dollars and cents, add "and" in the middle */
if (cents > 0) {
ast_str_append(&filenames, 0, "&%s", "and");
}
}
if (cents > 0) {
fnrecurse = ast_get_number_str(cents, lang);
if (!fnrecurse) {
ast_log(LOG_ERROR, "Couldn't get string for cents\n");
} else {
fnr = ast_str_buffer(fnrecurse);
ast_str_append(&filenames, 0, (amt < 100 ? "%s" : "&%s"), fnr);
}
ast_str_append(&filenames, 0, "&%s", (cents == 1) ? "cent" : "cents");
} else if (amt == 0) {
fnrecurse = ast_get_digit_str("0", lang);
if (!fnrecurse) {
ast_log(LOG_ERROR, "Couldn't get string for cents\n");
} else {
fnr = ast_str_buffer(fnrecurse);
ast_str_append(&filenames, 0, "%s", fnr);
}
ast_str_append(&filenames, 0, "&%s", "cents");
}
if (fnrecurse) {
ast_free(fnrecurse);
}
return filenames;
}
/*! \brief ast_get_money_str: call language-specific functions */
struct ast_str* ast_get_money_str(const char *str, const char *lang)
{
if (!strncasecmp(lang, "en", 2)) { /* English syntax */
return ast_get_money_en_dollars_str(str, lang);
}
ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to US Dollars\n", lang);
/* Default to english */
return ast_get_money_en_dollars_str(str, lang);
}
static int say_money_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
{
struct ast_str *filenames = ast_get_money_str(str, lang);
return say_filenames(chan, ints, lang, audiofd, ctrlfd, filenames);
}
static struct ast_str* get_number_str_en(int num, const char *lang)
{
const char *fnr;
int loops = 0;
int res = 0;
int playh = 0;
char fn[256] = "";
struct ast_str *filenames;
if (!num) {
return ast_get_digit_str("0", lang);
}
filenames = ast_str_create(20);
if (!filenames) {
return NULL;
}
ast_str_reset(filenames);
while (!res && (num || playh)) {
if (num < 0) {
ast_copy_string(fn, "digits/minus", sizeof(fn));
if ( num > INT_MIN ) {
num = -num;
} else {
num = 0;
}
} else if (playh) {
ast_copy_string(fn, "digits/hundred", sizeof(fn));
playh = 0;
} else if (num < 20) {
snprintf(fn, sizeof(fn), "digits/%d", num);
num = 0;
} else if (num < 100) {
snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
num %= 10;
} else {
if (num < 1000){
snprintf(fn, sizeof(fn), "digits/%d", (num/100));
playh++;
num %= 100;
} else {
struct ast_str *fnrecurse = NULL;
if (num < 1000000) { /* 1,000,000 */
fnrecurse = get_number_str_en((num / 1000), lang);
if (!fnrecurse) {
ast_log(LOG_ERROR, "Couldn't get string for num\n");
} else {
fnr = ast_str_buffer(fnrecurse);
ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
}
num %= 1000;
snprintf(fn, sizeof(fn), "&digits/thousand");
} else {
if (num < 1000000000) { /* 1,000,000,000 */
fnrecurse = get_number_str_en((num / 1000000), lang);
if (!fnrecurse) {
ast_log(LOG_ERROR, "Couldn't get string for num\n");
} else {
fnr = ast_str_buffer(fnrecurse);
ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
}
num %= 1000000;
ast_copy_string(fn, "&digits/million", sizeof(fn));
} else {
if (num < INT_MAX) {
fnrecurse = get_number_str_en((num / 1000000000), lang);
if (!fnrecurse) {
ast_log(LOG_ERROR, "Couldn't get string for num\n");
} else {
fnr = ast_str_buffer(fnrecurse);
ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
}
num %= 1000000000;
ast_copy_string(fn, "&digits/billion", sizeof(fn));
} else {
ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
res = -1;
}
}
}
if (fnrecurse) {
ast_free(fnrecurse);
}
/* we already decided whether or not to add an &, don't add another one immediately */
loops = 0;
}
}
if (!res) {
ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
loops++;
}
}
return filenames;
}
/*! \brief ast_get_number_str: call language-specific functions */
struct ast_str* ast_get_number_str(int num, const char *lang)
{
if (!strncasecmp(lang, "en", 2)) { /* English syntax */
return get_number_str_en(num, lang);
}
ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
/* Default to english */
return get_number_str_en(num, lang);
}
static struct ast_str* get_ordinal_str_en(int num, const char *lang)
{
const char *fnr;
int loops = 0;
int res = 0;
int playh = 0;
char fn[256] = "";
struct ast_str *filenames;
if (!num) {
num = 0;
}
filenames = ast_str_create(20);
if (!filenames) {
return NULL;
}
ast_str_reset(filenames);
while (!res && (num || playh)) {
if (num < 0) {
ast_copy_string(fn, "digits/minus", sizeof(fn));
if ( num > INT_MIN ) {
num = -num;
} else {
num = 0;
}
} else if (playh) {
ast_copy_string(fn, (num % 100 == 0) ? "digits/h-hundred" : "digits/hundred", sizeof(fn));
playh = 0;
} else if (num < 20) {
if (num > 0) {
snprintf(fn, sizeof(fn), "digits/h-%d", num);
} else {
ast_log(LOG_ERROR, "Unsupported ordinal number: %d\n", num);
}
num = 0;
} else if (num < 100) {
int base = (num / 10) * 10;
if (base != num) {
snprintf(fn, sizeof(fn), "digits/%d", base);
} else {
snprintf(fn, sizeof(fn), "digits/h-%d", base);
}
num %= 10;
} else {
if (num < 1000){
snprintf(fn, sizeof(fn), "digits/%d", (num/100));
playh++;
num %= 100;
} else {
struct ast_str *fnrecurse = NULL;
if (num < 1000000) { /* 1,000,000 */
fnrecurse = get_number_str_en((num / 1000), lang);
if (!fnrecurse) {
ast_log(LOG_ERROR, "Couldn't get string for num\n");
} else {
fnr = ast_str_buffer(fnrecurse);
ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
}
num %= 1000;
snprintf(fn, sizeof(fn), (num % 1000 == 0) ? "&digits/h-thousand" : "&digits/thousand");
} else {
if (num < 1000000000) { /* 1,000,000,000 */
fnrecurse = get_number_str_en((num / 1000000), lang);
if (!fnrecurse) {
ast_log(LOG_ERROR, "Couldn't get string for num\n");
} else {
fnr = ast_str_buffer(fnrecurse);
ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
}
num %= 1000000;
ast_copy_string(fn, (num % 1000000 == 0) ? "&digits/h-million" : "&digits/million", sizeof(fn));
} else {
if (num < INT_MAX) {
fnrecurse = get_number_str_en((num / 1000000000), lang);
if (!fnrecurse) {
ast_log(LOG_ERROR, "Couldn't get string for num\n");
} else {
fnr = ast_str_buffer(fnrecurse);
ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fnr);
}
num %= 1000000000;
ast_copy_string(fn, (num % 1000000000 == 0) ? "&digits/h-billion" : "&digits/billion", sizeof(fn));
} else {
ast_log(LOG_WARNING, "Number '%d' is too big for me\n", num);
res = -1;
}
}
}
if (fnrecurse) {
ast_free(fnrecurse);
}
/* we already decided whether or not to add an &, don't add another one immediately */
loops = 0;
}
}
if (!res) {
ast_str_append(&filenames, 0, (loops == 0 ? "%s" : "&%s"), fn);
loops++;
}
}
return filenames;
}
/*! \brief ast_get_ordinal_str: call language-specific functions */
struct ast_str* ast_get_ordinal_str(int num, const char *lang)
{
if (!strncasecmp(lang, "en", 2)) { /* English syntax */
return get_ordinal_str_en(num, lang);
}
ast_log(LOG_WARNING, "Language %s not currently supported, defaulting to English\n", lang);
/* Default to english */
return get_ordinal_str_en(num, lang);
}
/* Forward declarations */
@@ -540,66 +902,15 @@ static int say_number_full(struct ast_channel *chan, int num, const char *ints,
\note This is the default syntax, if no other syntax defined in this file is used */
static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
{
int res = 0;
int playh = 0;
char fn[256] = "";
if (!num)
return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
struct ast_str *filenames = ast_get_number_str(num, language);
return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);
}
while (!res && (num || playh)) {
if (num < 0) {
ast_copy_string(fn, "digits/minus", sizeof(fn));
if ( num > INT_MIN ) {
num = -num;
} else {
num = 0;
}
} else if (playh) {
ast_copy_string(fn, "digits/hundred", sizeof(fn));
playh = 0;
} else if (num < 20) {
snprintf(fn, sizeof(fn), "digits/%d", num);
num = 0;
} else if (num < 100) {
snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
num %= 10;
} else {
if (num < 1000){
snprintf(fn, sizeof(fn), "digits/%d", (num/100));
playh++;
num %= 100;
} else {
if (num < 1000000) { /* 1,000,000 */
res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
if (res)
return res;
num %= 1000;
snprintf(fn, sizeof(fn), "digits/thousand");
} else {
if (num < 1000000000) { /* 1,000,000,000 */
res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
if (res)
return res;
num %= 1000000;
ast_copy_string(fn, "digits/million", sizeof(fn));
} else {
ast_debug(1, "Number '%d' is too big for me\n", num);
res = -1;
}
}
}
}
if (!res) {
if (!ast_streamfile(chan, fn, language)) {
if ((audiofd > -1) && (ctrlfd > -1))
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
else
res = ast_waitstream(chan, ints);
}
ast_stopstream(chan);
}
}
return res;
/*! \brief say_ordinal_full */
static int say_ordinal_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
{
struct ast_str *filenames = ast_get_ordinal_str(num, language);
return say_filenames(chan, ints, language, audiofd, ctrlfd, filenames);
}
static int exp10_int(int power)
@@ -9410,8 +9721,10 @@ int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adje
static void __attribute__((constructor)) __say_init(void)
{
ast_say_number_full = say_number_full;
ast_say_ordinal_full = say_ordinal_full;
ast_say_enumeration_full = say_enumeration_full;
ast_say_digit_str_full = say_digit_str_full;
ast_say_money_str_full = say_money_str_full;
ast_say_character_str_full = say_character_str_full;
ast_say_phonetic_str_full = say_phonetic_str_full;
ast_say_datetime = say_datetime;