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 698604a064
commit 11516e4b8e
6 changed files with 1056 additions and 95 deletions

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 */
@@ -564,66 +926,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)
@@ -9482,8 +9793,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;