diff --git a/apps/app_meetme.c b/apps/app_meetme.c index 79506a6148d83f1fa71ac9d96da49b21415e0422..c7538c3554e6da6a16c26d8d0a0ef6a1250a0291 100755 --- a/apps/app_meetme.c +++ b/apps/app_meetme.c @@ -621,7 +621,7 @@ static int count_exec(struct ast_channel *chan, void *data) } else { if (chan->_state != AST_STATE_UP) ast_answer(chan); - res = ast_say_number(chan, count, "", chan->language); + res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */ } LOCAL_USER_REMOVE(u); return res; diff --git a/apps/app_queue.c b/apps/app_queue.c index 611b1f2c97a8267d514beaa92e72e3651574d2cc..a9f365dd4c97057b588bc9b49d113ba1b2caa24d 100755 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -370,7 +370,7 @@ static int say_position(struct queue_ent *qe) goto posout; } else { res += play_file(qe->chan, qe->parent->sound_thereare); - res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language); + res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */ res += play_file(qe->chan, qe->parent->sound_calls); } @@ -383,7 +383,7 @@ static int say_position(struct queue_ent *qe) supposed to be only once and we have already said it, say it */ if (avgholdmins > 1 && (qe->parent->announceholdtime) && (!(qe->parent->announceholdtime==1 && qe->last_pos)) ) { res += play_file(qe->chan, qe->parent->sound_holdtime); - res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language); + res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL); res += play_file(qe->chan, qe->parent->sound_minutes); } diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index 800758794348a50eddd35a97faa3e517992d64ab..3d7a9780fdb9dfccae5537e42f64a3f351b8af0d 100755 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -1706,7 +1706,7 @@ static int count_messages(char *dir) static int say_and_wait(struct ast_channel *chan, int num) { int d; - d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language); + d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language, (char *) NULL); return d; } @@ -2270,7 +2270,7 @@ static int get_folder(struct ast_channel *chan, int start) if (d) return d; for (x = start; x< 5; x++) { - if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language))) + if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL))) return d; d = play_and_wait(chan, "vm-for"); if (d) @@ -2634,7 +2634,7 @@ static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struc res = wait_file2(chan, vms, "vm-message"); if (msg && (msg != vms->lastmsg)) { if (!res) - res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language); + res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL); } } diff --git a/apps/app_zapscan.c b/apps/app_zapscan.c index 780ffe3876905ed8109938f371542cfbf1f04ae0..ab87f592c99d5f4c044a184c68d27a021e4f3769 100755 --- a/apps/app_zapscan.c +++ b/apps/app_zapscan.c @@ -322,7 +322,7 @@ static int conf_exec(struct ast_channel *chan, void *data) } confno = atoi(strchr(confstr,'/') + 1); ast_stopstream(chan); - ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language); + ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL); res = conf_run(chan, confno, confflags); if (res<0) break; input = res; diff --git a/channel.c b/channel.c index 94938ca582d4e92156c4a58d7e605002d92bc4f4..a466dc707a8016fcc7731700f4f126f8db23afe9 100755 --- a/channel.c +++ b/channel.c @@ -2350,12 +2350,12 @@ static void bridge_playfile(struct ast_channel *chan,char *sound,int remain) { res=ast_streamfile(chan,"vm-youhave",chan->language); res = ast_waitstream(chan, ""); if(min) { - res = ast_say_number(chan,min, AST_DIGIT_ANY, chan->language); + res = ast_say_number(chan,min, AST_DIGIT_ANY, chan->language, (char *) NULL); res=ast_streamfile(chan,"minutes",chan->language); res = ast_waitstream(chan, ""); } if(sec) { - res = ast_say_number(chan,sec, AST_DIGIT_ANY, chan->language); + res = ast_say_number(chan,sec, AST_DIGIT_ANY, chan->language, (char *) NULL); res=ast_streamfile(chan,"seconds",chan->language); res = ast_waitstream(chan, ""); } diff --git a/contrib/i18n.testsuite.conf b/contrib/i18n.testsuite.conf new file mode 100755 index 0000000000000000000000000000000000000000..7be99dbe0528141b9887f93bca318ab54488c72e --- /dev/null +++ b/contrib/i18n.testsuite.conf @@ -0,0 +1,66 @@ +; Test Internationalisation of SayNumber() +; #include this into a suitable context +; English +exten => 841,1,Answer +exten => 841,2,Wait,1 ; Allow VoIP sessions time to initialise +exten => 841,3,SetLanguage(en) +exten => 841,4,SayNumber(183) ; one hundred eighty three (NB This sounds wrong to me!) +; French +exten => 842,1,Answer +exten => 842,2,Wait,1 ; Allow VoIP sessions time to initialise +exten => 842,3,SetLanguage(fr) +exten => 842,4,SayNumber(183) ; hundred eighty three +exten => 842,5,SayNumber(283) ; two hundred eighty three +exten => 842,6,SayNumber(1063) ; thousand sixty three +exten => 842,7,SayNumber(2063) ; two thousand sixty three +; Spanish +exten => 843,1,Answer +exten => 843,2,Wait,1 ; Allow VoIP sessions time to initialise +exten => 843,3,SetLanguage(es) +exten => 843,4,SayNumber(21) ; "twentyone" +exten => 843,5,SayNumber(200) ; "twohundred" +exten => 843,6,SayNumber(1000000) ; one million +exten => 843,7,SayNumber(2000000) ; two millions +; Portuguese +exten => 844,1,Answer +exten => 844,2,Wait,1 ; Allow VoIP sessions time to initialise +exten => 844,3,SetLanguage(pt) +exten => 844,4,SayNumber(1) ; one +exten => 844,5,SayNumber(1,f) ; one (feminine) +exten => 844,6,SayNumber(2) ; two +exten => 844,7,SayNumber(2,f) ; two (feminine) +exten => 844,8,SayNumber(183) ; hundred& eighty three +; Italian +exten => 845,1,Answer +exten => 845,2,Wait,1 ; Allow VoIP sessions time to initialise +exten => 845,3,SetLanguage(it) +exten => 845,4,SayNumber(21) ; "twentyone" +exten => 845,5,SayNumber(183) ; hundred eighty three +exten => 845,6,SayNumber(283) ; two hundred eighty three +exten => 845,7,SayNumber(1063) ; thousand sixty three +exten => 845,8,SayNumber(2063) ; two thousands sixty three +; Dutch +exten => 846,1,Answer +exten => 846,2,Wait,1 ; Allow VoIP sessions time to initialise +exten => 846,3,SetLanguage(nl) +; Danish +exten => 847,1,Answer +exten => 847,2,Wait,1 ; Allow VoIP sessions time to initialise +exten => 847,3,SetLanguage(da) +exten => 847,4,SayNumber(68) ; eight-& sixty +exten => 847,5,SayNumber(2034) ; two thousand & four-& thirty +exten => 847,6,SayNumber(1000000) ; one million +exten => 847,7,SayNumber(2000000) ; two millions +; German (not yet implemented) +exten => 848,1,Answer +exten => 848,2,Wait,1 ; Allow VoIP sessions time to initialise +exten => 848,3,SetLanguage(de) +exten => 848,4,SayNumber(68) ; eight-& sixty +exten => 848,5,SayNumber(2034) ; two thousand & four-& thirty +exten => 848,6,SayNumber(1000000) ; one million +exten => 848,7,SayNumber(2000000) ; two millions +; Swedish +exten => 849,1,Answer +exten => 849,2,Wait,1 ; Allow VoIP sessions time to initialise +exten => 849,3,SetLanguage(se) + diff --git a/include/asterisk/say.h b/include/asterisk/say.h index c1bfdeac3c62cfca2b568fc18dbf8cbeda28c13c..019d7cc76a38cb19580e966eb2bd4151757001a5 100755 --- a/include/asterisk/say.h +++ b/include/asterisk/say.h @@ -29,10 +29,11 @@ extern "C" { * \param num number to say on the channel * \param ints which dtmf to interrupt on * \param lang language to speak the number + * \param options set to 'f' for female, 'm' for masculine (used in portuguese) * Vocally says a number on a given channel * Returns 0 on success, DTMF digit on interrupt, -1 on failure */ -int ast_say_number(struct ast_channel *chan, int num, char *ints, char *lang); +int ast_say_number(struct ast_channel *chan, int num, char *ints, char *lang, char *options); /* Same as above with audiofd for received audio and returns 1 on ctrlfd being readable */ int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd); diff --git a/pbx.c b/pbx.c index 01aa0f6c21484abee4431c8a4dfd813e9baf1667..85e5e0103f0eb74b4e9a758faa11421f51ac584c 100755 --- a/pbx.c +++ b/pbx.c @@ -287,7 +287,7 @@ static struct pbx_builtin { { "SayNumber", pbx_builtin_saynumber, "Say Number", -" SayNumber(digits): Says the passed number\n" }, +" SayNumber(digits[,gender]): Says the passed number\n" }, { "SayDigits", pbx_builtin_saydigits, "Say Digits", @@ -4568,9 +4568,29 @@ static int pbx_builtin_gotoif(struct ast_channel *chan, void *data) static int pbx_builtin_saynumber(struct ast_channel *chan, void *data) { int res = 0; - if (data && atoi((char *)data) ) - res = ast_say_number(chan, atoi((char *)data), "", chan->language); - return res; + char tmp[256]; + char *number = (char *) NULL; + char *options = (char *) NULL; + + + if (!data || !strlen((char *)data)) { + ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n"); + return -1; + } + strncpy(tmp, (char *)data, sizeof(tmp)-1); + number=tmp; + strsep(&number, "|"); + options = strsep(&number, "|"); + if (options) { + if ( strcasecmp(options, "f") && strcasecmp(options,"m") && + strcasecmp(options, "c") && strcasecmp(options, "n") ) { + ast_log(LOG_WARNING, "SayNumber gender option is either 'f', 'm', 'c' or 'n'\n"); + return -1; + } + } + + + return res = ast_say_number(chan, atoi((char *) tmp), "", chan->language, options); } static int pbx_builtin_saydigits(struct ast_channel *chan, void *data) diff --git a/say.c b/say.c index 46ef8c01fbc60e97e9237809aceb06669bbd5b5f..68f01dafa02d77ff975be40490ea272d1b6c6678 100755 --- a/say.c +++ b/say.c @@ -25,6 +25,10 @@ #include "asterisk.h" #include <stdio.h> + +/* Forward declaration */ +static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang); + int ast_say_digit_str(struct ast_channel *chan, char *fn2, char *ints, char *lang) { /* XXX Merge with full version? XXX */ @@ -87,22 +91,95 @@ int ast_say_digits_full(struct ast_channel *chan, int num, char *ints, char *lan return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd); } +/* Forward declarations */ +/* Syntaxes supported, not really language codes. + en - English, Swedish, Norwegian + fr - French + da - Danish (maybe German - please check) + pt - Portuguese + es - Spanish + it - Italian + nl - Dutch + + For portuguese, we're using an option to saynumber() to indicate if the gender is male of female + This should also be implemented in _full version, really. + + OEJ 2004-04-25 +*/ + +static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd); +static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd); +static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd); +static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd); +static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd); +static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd); +static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd); +static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd); + +/*--- ast_say_number_full: call language-specific functions */ +/* Called from AGI */ int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd) +{ + char *options=(char *) NULL; /* While waiting for a general hack for agi */ + + if (!strcasecmp(language, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) { + return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd)); + } else if (!strcasecmp(language, "fr") ) { /* French syntax */ + return(ast_say_number_full_fr(chan, num, ints, language, audiofd, ctrlfd)); + } else if (!strcasecmp(language, "de") ) { /* German syntax */ + return(ast_say_number_full_de(chan, num, ints, language, audiofd, ctrlfd)); + } else if (!strcasecmp(language, "da") ) { /* Danish syntax */ + return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd)); + } else if (!strcasecmp(language, "it") ) { /* Italian syntax */ + return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd)); + } else if (!strcasecmp(language, "pt") ) { /* Portuguese syntax */ + return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd)); + } else if (!strcasecmp(language, "es") ) { /* Spanish syntax */ + return(ast_say_number_full_es(chan, num, ints, language, audiofd, ctrlfd)); + } else if (!strcasecmp(language, "nl") ) { /* Spanish syntax */ + return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd)); + } + + /* Default to english */ + return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd)); +} + +/*--- ast_say_number: call language-specific functions without file descriptors */ +int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language, char *options) +{ + if (!strcasecmp(language, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) { + return(ast_say_number_full_en(chan, num, ints, language, -1, -1)); + } + /* French */ + if (!strcasecmp(language, "fr")) { /* French syntax */ + return(ast_say_number_full_fr(chan, num, ints, language, -1, -1)); + } else if (!strcasecmp(language, "da")) { /* Danish syntax */ + return(ast_say_number_full_da(chan, num, ints, language, options, -1, -1)); + } else if (!strcasecmp(language, "it")) { /* Italian syntax */ + return(ast_say_number_full_it(chan, num, ints, language, -1, -1)); + } else if (!strcasecmp(language, "pt")) { /* Portuguese syntax */ + return(ast_say_number_full_pt(chan, num, ints, language, options, -1, -1)); + } else if (!strcasecmp(language, "nl")) { /* Spanish syntax */ + return(ast_say_number_full_nl(chan, num, ints, language, -1, -1)); + } else if (!strcasecmp(language, "es")) { /* Spanish syntax */ + return(ast_say_number_full_es(chan, num, ints, language, -1, -1)); + } + + /* Default to english */ + return(ast_say_number_full_en(chan, num, ints, language, NULL, NULL)); +} + +/*--- ast_say_number_full_en: English syntax */ +/* 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, char *ints, 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); - if (0) { - /* XXX Only works for english XXX */ - } else { - /* Use english numbers if a given language is supported. */ - /* As a special case, Norwegian has the same numerical grammar - as English */ - if (strcasecmp(language, "no")) - language = "en"; - while(!res && (num || playh)) { + + while(!res && (num || playh)) { if (playh) { snprintf(fn, sizeof(fn), "digits/hundred"); playh = 0; @@ -121,14 +198,14 @@ int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *lan num -= ((num / 100) * 100); } else { if (num < 1000000) { /* 1,000,000 */ - res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd); + res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd); if (res) return res; num = num % 1000; snprintf(fn, sizeof(fn), "digits/thousand"); } else { if (num < 1000000000) { /* 1,000,000,000 */ - res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd); + res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd); if (res) return res; num = num % 1000000; @@ -140,80 +217,723 @@ int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *lan } } } - if (!res) { - res = ast_streamfile(chan, fn, language); - if (!res) - res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); - ast_stopstream(chan); - } + if (!res) { + if(!ast_streamfile(chan, fn, language)) { + if (audiofd && ctrlfd) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + else + res = ast_waitstream(chan, ints); + } + ast_stopstream(chan); + + } - } } return res; } -int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language) + +/*--- ast_say_number_full_fr: French syntax */ +static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd) { - /* XXX Should I be merged with ast_say_number_full XXX */ int res = 0; int playh = 0; + int playa = 0; /* For french */ char fn[256] = ""; if (!num) - return ast_say_digits(chan, 0,ints, language); - if (0) { - /* XXX Only works for english XXX */ - } else { - /* Use english numbers */ - language = "en"; - while(!res && (num || playh)) { - if (playh) { - snprintf(fn, sizeof(fn), "digits/hundred"); - playh = 0; - } else - if (num < 20) { - snprintf(fn, sizeof(fn), "digits/%d", num); + return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd); + while(!res && (num || playh || playa)) { + if (playh) { + snprintf(fn, sizeof(fn), "digits/hundred"); + playh = 0; + } else + if (playa) { + snprintf(fn, sizeof(fn), "digits/et"); + playa = 0; + } else + if (num < 21) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num < 70) { + snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10); + if ((num % 10) == 1) playa++; + num = num % 10; + } else + if (num < 80) { + snprintf(fn, sizeof(fn), "digits/60"); + if ((num % 10) == 1) playa++; + num = num - 60; + } else + if (num < 100) { + snprintf(fn, sizeof(fn), "digits/80"); + num = num - 80; + } else + if (num < 200) { + snprintf(fn, sizeof(fn), "digits/hundred"); + num = num - 100; + } else + if (num < 1000) { + snprintf(fn, sizeof(fn), "digits/%d", (num/100)); + playh++; + num = num % 100; + } else + if (num < 2000) { + snprintf(fn, sizeof(fn), "digits/thousand"); + num = num - 1000; + } else + if (num < 1000000) { + res = ast_say_number_full_fr(chan, num / 1000, ints, language, audiofd, ctrlfd); + if (res) return res; + snprintf(fn, sizeof(fn), "digits/thousand"); + num = num % 1000; + } else + if (num < 1000000000) { + res = ast_say_number_full_fr(chan, num / 1000000, ints, language, audiofd, ctrlfd); + if (res) return res; + snprintf(fn, sizeof(fn), "digits/million"); + num = num % 1000000; + } else { + ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num); + res = -1; + } + if (!res) { + if(!ast_streamfile(chan, fn, language)) { + if (audiofd && ctrlfd) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + else + res = ast_waitstream(chan, ints); + } + ast_stopstream(chan); + + } + + + } + return res; +} + +/*--- ast_say_number_full_da: Danish syntax */ +/* New files: + In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and" + */ +static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd) +{ + int res = 0; + int playh = 0; + int playa = 0; + int cn = 1; /* +1 = Commune; -1 = Neutrum */ + char fn[256] = ""; + if (!num) + return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd); + + if (options && !strncasecmp(options, "n",1)) cn = -1; + + while(!res && (num || playh || playa )) { + /* The grammer for Danish numbers is the same as for English except + * for the following: + * - 1 exists in both commune ("en", file "1N") and neutrum ("et", file "1") + * - numbers 20 through 99 are said in reverse order, i.e. 21 is + * "one-and twenty" and 68 is "eight-and sixty". + * - "million" is different in singular and plural form + * - numbers > 1000 with zero as the third digit from last have an + * "and" before the last two digits, i.e. 2034 is "two thousand and + * four-and thirty" and 1000012 is "one million and twelve". + */ + if (playh) { + snprintf(fn, sizeof(fn), "digits/hundred"); + playh = 0; + } else + if (playa) { + snprintf(fn, sizeof(fn), "digits/and"); + playa = 0; + } else + if (num == 1 && cn == -1) { + snprintf(fn, sizeof(fn), "digits/1N"); num = 0; - } else - if (num < 100) { - snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10); - num -= ((num / 10) * 10); - } else { - if (num < 1000){ - snprintf(fn, sizeof(fn), "digits/%d", (num/100)); - playh++; - num -= ((num / 100) * 100); - } else { - if (num < 1000000) { - res = ast_say_number(chan, num / 1000, ints, language); - if (res) - return res; - num = num % 1000; - snprintf(fn, sizeof(fn), "digits/thousand"); + } else + if (num < 20) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num < 100) { + int ones = num % 10; + if (ones) { + snprintf(fn, sizeof(fn), "digits/%d-and", ones); + num -= ones; + } else { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } + } else { + if (num < 1000) { + int hundreds = num / 100; + if (hundreds == 1) + snprintf(fn, sizeof(fn), "digits/1N"); + else + snprintf(fn, sizeof(fn), "digits/%d", (num / 100)); + + playh++; + num -= 100 * hundreds; + if (num) + playa++; + + } else { + if (num < 1000000) { + res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd); + if (res) + return res; + num = num % 1000; + snprintf(fn, sizeof(fn), "digits/thousand"); + } else { + if (num < 1000000000) { + int millions = num / 1000000; + res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd); + if (res) + return res; + if (millions == 1) + snprintf(fn, sizeof(fn), "digits/million"); + else + snprintf(fn, sizeof(fn), "digits/millions"); + num = num % 1000000; } else { - if (num < 1000000000) { - res = ast_say_number(chan, num / 1000000, ints, language); - if (res) - return res; - num = num % 1000000; - snprintf(fn, sizeof(fn), "digits/million"); - } else { - ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num); - res = -1; - } - } + ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num); + res = -1; + } + } + if (num && num < 100) + playa++; + } + } + if (!res) { + if(!ast_streamfile(chan, fn, language)) { + if (audiofd && ctrlfd) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + else + res = ast_waitstream(chan, ints); } + ast_stopstream(chan); + } + + } + return res; +} + +/*--- ast_say_number_full_de: German syntax */ +/* New files: + In addition to English, the following sounds are required: "millions", "and" and "1-and" through "9-and" + */ +static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd) +{ + int res = 0; + int playh = 0; + int playa = 0; + char fn[256] = ""; + if (!num) + return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd); + + while(!res && (num || playh || playa )) { + /* The grammer for German numbers is the same as for English except + * for the following: + * - numbers 20 through 99 are said in reverse order, i.e. 21 is + * "one-and twenty" and 68 is "eight-and sixty". + * - "million" is different in singular and plural form + * - numbers > 1000 with zero as the third digit from last have an + * "and" before the last two digits, i.e. 2034 is "two thousand and + * four-and thirty" and 1000012 is "one million and twelve". + */ + if (playh) { + snprintf(fn, sizeof(fn), "digits/hundred"); + playh = 0; + } else + if (playa) { + snprintf(fn, sizeof(fn), "digits/and"); + playa = 0; + } else + if (num < 20) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num < 100) { + int ones = num % 10; + if (ones) { + snprintf(fn, sizeof(fn), "digits/%d-and", ones); + num -= ones; + } else { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } + } else { + if (num < 1000) { + int hundreds = num / 100; + if (hundreds == 1) + snprintf(fn, sizeof(fn), "digits/1N"); + else + snprintf(fn, sizeof(fn), "digits/%d", (num / 100)); + + playh++; + num -= 100 * hundreds; + if (num) + playa++; + + } else { + if (num < 1000000) { + res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd); + if (res) + return res; + num = num % 1000; + snprintf(fn, sizeof(fn), "digits/thousand"); + } else { + if (num < 1000000000) { + int millions = num / 1000000; + res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd); + if (res) + return res; + if (millions == 1) + snprintf(fn, sizeof(fn), "digits/million"); + else + snprintf(fn, sizeof(fn), "digits/millions"); + num = num % 1000000; + } else { + ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num); + res = -1; + } + } + if (num && num < 100) + playa++; + } + } if (!res) { - res = ast_streamfile(chan, fn, language); - if (!res) - res = ast_waitstream(chan, ints); + if(!ast_streamfile(chan, fn, language)) { + if (audiofd && ctrlfd) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + else + res = ast_waitstream(chan, ints); + } ast_stopstream(chan); + } - } } return res; } + +/*------------ Portuguese ----------------------*/ +/* ast_say_number_full_pt: Portuguese syntax */ +/* For feminin all sound files end with F */ +/* 100E for 100+ something */ +/* What is "pt-e" */ +static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd) +{ + int res = 0; + int playh = 0; + int mf = 1; /* +1 = Masculin; -1 = Feminin */ + + char fn[256] = ""; + + if (!num) + return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd); + + if (options && !strncasecmp(options, "f",1)) mf = -1; + + while(!res && num ) { + + if (num < 20) { + if ((num == 1 || num == 2) && (mf < 0)) + snprintf(fn, sizeof(fn), "digits/%dF", num); + else + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num < 100) { + snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10); + if (num % 10) + playh = 1; + num = num % 10; + } else + if (num < 1000) { + if (num == 100) + snprintf(fn, sizeof(fn), "digits/100"); + else if (num < 200) + snprintf(fn, sizeof(fn), "digits/100E"); + else { + if (mf < 0 && num > 199) + snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100); + else + snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100); + if (num % 100) + playh = 1; + } + num = num % 100; + } else + if (num < 1000000) { + if (num > 1999) { + res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd); + if (res) + return res; + } + snprintf(fn, sizeof(fn), "digits/1000"); + if ((num % 1000) && ((num % 1000) < 100 || !(num % 100))) + playh = 1; + num = num % 1000; + } else + if (num < 1000000000) { + res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd ); + if (res) + return res; + + if (num < 2000000) + snprintf(fn, sizeof(fn), "digits/1000000"); + else + snprintf(fn, sizeof(fn), "digits/1000000S"); + + if ((num % 1000000) && + // no thousands + ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) || + // no hundreds and below + (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) ) + playh = 1; + num = num % 1000000; + } + if (!res && playh) { + res = wait_file(chan, ints, "digits/pt-e", language); + ast_stopstream(chan); + playh = 0; + } + if (!res) { + if(!ast_streamfile(chan, fn, language)) { + if (audiofd && ctrlfd) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + else + res = ast_waitstream(chan, ints); + } + ast_stopstream(chan); + + } + + } + return res; +} + + +/*--- ast_say_number_full_it: italian */ +static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd) +{ + int res = 0; + int playh = 0; + int tempnum = 0; + char fn[256] = ""; + + if (!num) + return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd); + + /* + Italian support + + Like english, numbers till 20 are a single 'word', and other + compound, but with exceptions. + For example 21 is not twenty-one, but is a single word in it. + Idem for 28 (ie when a the 2nd part of a compund number + starts with a wovel) + + There're exceptions also for hundred, thounsand and million. + In english 100 = one hundred, 200 is two hundred. + In italian 100 = cento , like to say hundred (without one), + 200 and more are like english. + + Same apply for thousand: + 1000 is one thousand in en, 2000 is two thousand. + In it we have 1000 = mille , 2000 = 2 mila + + For million(s) we use the plural, if more than one + Also, one million is abbreviated in it, like on-million, + or 'un milione', not 'uno milione'. + So the right file is provided. + */ + + while(!res && (num || playh)) { + if (playh) { + snprintf(fn, sizeof(fn), "digits/hundred"); + playh = 0; + } else + if (num < 20) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 21) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 28) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 31) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 38) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 41) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 48) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 51) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 58) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 61) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 68) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 71) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 78) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 81) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 88) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 91) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num == 98) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num < 100) { + snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10); + num -= ((num / 10) * 10); + } else { + if (num < 1000){ + if ((num / 100) > 1) { + snprintf(fn, sizeof(fn), "digits/%d", (num/100)); + playh++; + } + else { + snprintf(fn, sizeof(fn), "digits/hundred"); + } + num -= ((num / 100) * 100); + } else { + if (num < 1000000) { /* 1,000,000 */ + if ((num/1000) > 1) + res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd); + if (res) + return res; + tempnum = num; + num = num % 1000; + if ((tempnum / 1000) < 2) + snprintf(fn, sizeof(fn), "digits/thousand"); + else /* for 1000 it says mille, for >1000 (eg 2000) says mila */ + snprintf(fn, sizeof(fn), "digits/thousands"); + } else { + if (num < 1000000000) { /* 1,000,000,000 */ + if ((num / 1000000) > 1) + res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd); + if (res) + return res; + tempnum = num; + num = num % 1000000; + if ((tempnum / 1000000) < 2) + snprintf(fn, sizeof(fn), "digits/million"); + else + snprintf(fn, sizeof(fn), "digits/millions"); + } else { + ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num); + res = -1; + } + } + } + } + if (!res) { + if(!ast_streamfile(chan, fn, language)) { + if (audiofd && ctrlfd) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + else + res = ast_waitstream(chan, ints); + } + ast_stopstream(chan); + + } + } + return res; +} + + +/*--- ast_say_number_full_es: spanish syntax */ +/* New files: + Requires a few new audios: + 21.gsm thru 29.gsm, cien.gsm, mil.gsm, millon.gsm, millones.gsm, 100.gsm, 200.gsm, 300.gsm, 400.gsm, 500.gsm, 600.gsm, 700.gsm, 800.gsm, 900.gsm, y.gsm + */ +static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd) +{ + int res = 0; + int playa = 0; + char fn[256] = ""; + if (!num) + return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd); + while (!res && num) { + if (playa) { + snprintf(fn, sizeof(fn), "digits/y"); + playa = 0; + } else + if (num < 31) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num < 100) { + snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10); + num -= ((num/10)*10); + if (num) + playa++; + } else + if (num == 100) { + snprintf(fn, sizeof(fn), "digits/cien"); + num = 0; + } else { + if (num < 1000) { + snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100); + num -= ((num/100)*100); + } else { + if (num < 1000000) { + res = ast_say_number_full_es(chan, num / 1000, ints, language, audiofd, ctrlfd); + if (res) + return res; + num = num % 1000; + snprintf(fn, sizeof(fn), "digits/mil"); + } else { + if (num < 2147483640) { + res = ast_say_number_full_es(chan, num / 1000000, ints, language, audiofd, ctrlfd); + if (res) + return res; + if ((num/1000000) == 1) { + snprintf(fn, sizeof(fn), "digits/millon"); + } else { + snprintf(fn, sizeof(fn), "digits/millones"); + } + num = num % 1000000; + } else { + ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num); + res = -1; + } + } + } + } + + if (!res) { + if(!ast_streamfile(chan, fn, language)) { + if (audiofd && ctrlfd) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + else + res = ast_waitstream(chan, ints); + } + ast_stopstream(chan); + + } + + } + return res; +} + +/*--- ast_say_number_full_nl: dutch syntax */ +/* New files: ??? + */ +static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd) +{ + int res = 0; + int playh = 0; + int units = 0; + char fn[256] = ""; + if (!num) + return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd); + while (!res && (num || playh )) { + if (playh) { + snprintf(fn, sizeof(fn), "digits/hundred"); + playh = 0; + } else + if (num < 20) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else + if (num < 100) { + units = num % 10; + if (units > 0) { + res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd); + if (res) + return res; + num = num - units; + snprintf(fn, sizeof(fn), "digits/nl-en"); + } else { + snprintf(fn, sizeof(fn), "digits/%d", num - units); + num = 0; + } + } else { + if (num < 1000){ + snprintf(fn, sizeof(fn), "digits/%d", (num/100)); + playh++; + num -= ((num / 100) * 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 = 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 = num % 1000000; + snprintf(fn, sizeof(fn), "digits/million"); + } else { + ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num); + res = -1; + } + } + } + } + + if (!res) { + if(!ast_streamfile(chan, fn, language)) { + if (audiofd && ctrlfd) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + else + res = ast_waitstream(chan, ints); + } + ast_stopstream(chan); + + } + + } + return res; +} + + int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang) { struct tm tm; @@ -233,12 +953,13 @@ int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang) res = ast_waitstream(chan, ints); } if (!res) - res = ast_say_number(chan, tm.tm_mday, ints, lang); + res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL); + /* Should portuguese add a gender here? Defaults to masculin */ if (!res) res = ast_waitstream(chan, ints); if (!res) - res = ast_say_number(chan, tm.tm_year + 1900, ints, lang); + res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL); return res; } @@ -548,21 +1269,21 @@ int ast_say_time(struct ast_channel *chan, time_t t, char *ints, char *lang) pm = 1; } if (!res) - res = ast_say_number(chan, hour, ints, lang); + res = ast_say_number(chan, hour, ints, lang, (char *) NULL); if (tm.tm_min > 9) { if (!res) - res = ast_say_number(chan, tm.tm_min, ints, lang); + res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL); } else if (tm.tm_min) { if (!res) - res = ast_streamfile(chan, "digits/oh", lang); + res = ast_streamfile(chan, "digits/oh", lang); /* This is very english ! */ if (!res) res = ast_waitstream(chan, ints); if (!res) - res = ast_say_number(chan, tm.tm_min, ints, lang); + res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL); } else { if (!res) - res = ast_streamfile(chan, "digits/oclock", lang); + res = ast_streamfile(chan, "digits/oclock", lang); /* This is very english ! */ if (!res) res = ast_waitstream(chan, ints); } @@ -598,7 +1319,7 @@ int ast_say_datetime(struct ast_channel *chan, time_t t, char *ints, char *lang) res = ast_waitstream(chan, ints); } if (!res) - res = ast_say_number(chan, tm.tm_mday, ints, lang); + res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL); hour = tm.tm_hour; if (!hour) @@ -610,18 +1331,18 @@ int ast_say_datetime(struct ast_channel *chan, time_t t, char *ints, char *lang) pm = 1; } if (!res) - res = ast_say_number(chan, hour, ints, lang); + res = ast_say_number(chan, hour, ints, lang, (char *) NULL); if (tm.tm_min > 9) { if (!res) - res = ast_say_number(chan, tm.tm_min, ints, lang); + res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL); } else if (tm.tm_min) { if (!res) res = ast_streamfile(chan, "digits/oh", lang); if (!res) res = ast_waitstream(chan, ints); if (!res) - res = ast_say_number(chan, tm.tm_min, ints, lang); + res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL); } else { if (!res) res = ast_streamfile(chan, "digits/oclock", lang); @@ -638,7 +1359,7 @@ int ast_say_datetime(struct ast_channel *chan, time_t t, char *ints, char *lang) if (!res) res = ast_waitstream(chan, ints); if (!res) - res = ast_say_number(chan, tm.tm_year + 1900, ints, lang); + res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL); return res; } @@ -665,7 +1386,7 @@ int ast_say_datetime_from_now(struct ast_channel *chan, time_t t, char *ints, ch res = ast_waitstream(chan, ints); } if (!res) - res = ast_say_number(chan, tm.tm_mday, ints, lang); + res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL); } else if (daydiff) { /* Just what day of the week */