diff --git a/CHANGES b/CHANGES index 5efe3097d41e3977dcdd377cd5954a57b9b2cafd..1d5e0a83aa3025b418221587c80febedac47b62c 100644 --- a/CHANGES +++ b/CHANGES @@ -1028,6 +1028,8 @@ Say language. The 'language' parameter in say.conf now recognizes a setting of 'ja', which will enable Japanese language specific mechanisms for playing back numbers, dates, and other items. + * Counting, enumeration and dates now supports Icelandic grammar with the + 'language' parameter set to 'is'. SayCountPL ------------------ @@ -5181,6 +5183,7 @@ Voicemail Changes the message as urgent after he has recorded a voicemail by following the voice instructions. When listening to voicemails using VoiceMailMain urgent messages will be presented before other messages + * Added "is" language support Queue changes ------------- diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index 1d5b2dcb39cc5e8488e4acdc63112914c1e9e6e8..586086c55a934de22a4745a752404df4a3c5e491 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -8527,6 +8527,8 @@ static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *v res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL); } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q H 'digits/kai' M ", NULL); + } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */ + res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL); } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */ res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL); } else if (!strcasecmp(ast_channel_language(chan),"ja")) { /* Japanese syntax */ @@ -8753,6 +8755,12 @@ static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struc res = wait_file2(chan, vms, "vm-number"); res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f"); } + /* ICELANDIC syntax */ + } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { + res = wait_file2(chan, vms, "vm-message"); + if (vms->curmsg && (vms->curmsg != vms->lastmsg)) { + res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "n"); + } /* VIETNAMESE syntax */ } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { if (!vms->curmsg) { @@ -9492,6 +9500,75 @@ static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms) return res; } +/* ICELANDIC syntax */ +static int vm_intro_is(struct ast_channel *chan, struct vm_state *vms) +{ + int res; + + /* Introduce messages they have */ + res = ast_play_and_wait(chan, "vm-youhave"); + if (!res) { + if (vms->urgentmessages) { + /* Digits 1-4 are spoken in neutral and plural when talking about messages, + however, feminine is used for 1 as it is the same as the neutral for plural, + and singular neutral is the same after 1. */ + if (vms->urgentmessages < 5) { + char recname[16]; + if (vms->urgentmessages == 1) + snprintf(recname, sizeof(recname), "digits/1kvk"); + else + snprintf(recname, sizeof(recname), "digits/%dhk", vms->urgentmessages); + res = ast_play_and_wait(chan, recname); + } else if (!res) + res = ast_play_and_wait(chan, "vm-Urgent"); + if ((vms->oldmessages || vms->newmessages) && !res) { + res = ast_play_and_wait(chan, "vm-and"); + } else if (!res) + res = ast_play_and_wait(chan, "vm-messages"); + } + if (vms->newmessages) { + if (vms->newmessages < 5) { + char recname[16]; + if (vms->newmessages == 1) + snprintf(recname, sizeof(recname), "digits/1kvk"); + else + snprintf(recname, sizeof(recname), "digits/%dhk", vms->newmessages); + res = ast_play_and_wait(chan, recname); + } else + res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan)); + if (!res) + res = ast_play_and_wait(chan, "vm-INBOX"); + if (vms->oldmessages && !res) + res = ast_play_and_wait(chan, "vm-and"); + else if (!res) + res = ast_play_and_wait(chan, "vm-messages"); + } + if (!res && vms->oldmessages) { + if (vms->oldmessages < 5) { + char recname[16]; + if (vms->oldmessages == 1) + snprintf(recname, sizeof(recname), "digits/1kvk"); + else + snprintf(recname, sizeof(recname), "digits/%dhk", vms->oldmessages); + res = ast_play_and_wait(chan, recname); + } else + res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan)); + if (!res) + res = ast_play_and_wait(chan, "vm-Old"); + if (!res) + res = ast_play_and_wait(chan, "vm-messages"); + } + if (!res) { + if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) { + res = ast_play_and_wait(chan, "vm-no"); + if (!res) + res = ast_play_and_wait(chan, "vm-messages"); + } + } + } + return res; +} + /* ITALIAN syntax */ static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms) { @@ -10156,6 +10233,8 @@ static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm return vm_intro_gr(chan, vms); } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW syntax */ return vm_intro_he(chan, vms); + } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */ + return vm_intro_is(chan, vms); } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */ return vm_intro_it(chan, vms); } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE syntax */ diff --git a/main/say.c b/main/say.c index 95792932638cfef5091f3f47875ea3fdf7b58b95..6e51de20143412751642e4771d5a3a34a7703dc8 100644 --- a/main/say.c +++ b/main/say.c @@ -317,6 +317,7 @@ static int say_digit_str_full(struct ast_channel *chan, const char *str, const c \arg \b es - Spanish, Mexican \arg \b fr - French \arg \b he - Hebrew + \arg \b is - Icelandic \arg \b it - Italian \arg \b nl - Dutch \arg \b no - Norwegian @@ -373,6 +374,7 @@ static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const ch static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); static int ast_say_number_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); +static int ast_say_number_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd); static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd); static int ast_say_number_full_no(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); @@ -394,6 +396,7 @@ static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const static int ast_say_enumeration_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); static int ast_say_enumeration_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); static int ast_say_enumeration_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); +static int ast_say_enumeration_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd); /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */ @@ -409,6 +412,7 @@ static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang); static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang); static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang); +static int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang); static int ast_say_date_with_format_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone); static int ast_say_date_with_format_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone); @@ -416,6 +420,7 @@ static int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const static int ast_say_date_with_format_es(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone); static int ast_say_date_with_format_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone); static int ast_say_date_with_format_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone); +static int ast_say_date_with_format_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone); static int ast_say_date_with_format_it(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone); static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone); static int ast_say_date_with_format_pl(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone); @@ -511,6 +516,8 @@ static int say_number_full(struct ast_channel *chan, int num, const char *ints, return ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd); } else if (!strncasecmp(language, "hu", 2)) { /* Hungarian syntax */ return ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd); + } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */ + return ast_say_number_full_is(chan, num, ints, language, options, audiofd, ctrlfd); } else if (!strncasecmp(language, "it", 2)) { /* Italian syntax */ return ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd); } else if (!strncasecmp(language, "ka", 2)) { /* Georgian syntax */ @@ -1518,6 +1525,129 @@ static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char return res; } +/*! \brief ast_say_number_full_is: Icelandic syntax */ +/* New files: + In addition to American English, the following sounds are required: "hundreds", "millions", "1kvk", "1hk", "2kvk", "2hk", "3kvk", "3hk", "4kvk", "4hk" + */ +static int ast_say_number_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd) +{ + int res = 0; + int playh = 0; + int playa = 0; + int cn = 1; /* 1 = masc; 2 = fem; 3 = neut */ + char fn[256] = ""; + + if (!num) + return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd); + + if (options && !strncasecmp(options, "f", 1)) cn = 2; + if (options && !strncasecmp(options, "c", 1)) cn = 3; + /* It seems that sometimes people are using c and sometimes n. */ + if (options && !strncasecmp(options, "n", 1)) cn = 3; + + while (!res && (num || playh || playa )) { + if (num < 0) { + ast_copy_string(fn, "digits/minus", sizeof(fn)); + if ( num > INT_MIN ) { + num = -num; + } else { + num = 0; + } + } else if (playh) { + if (playh > 1) + ast_copy_string(fn, "digits/hundreds", sizeof(fn)); + else + ast_copy_string(fn, "digits/hundred", sizeof(fn)); + playh = 0; + } else if (playa) { + ast_copy_string(fn, "digits/and", sizeof(fn)); + playa = 0; + } else if (num < 5 && cn == 2) { + snprintf(fn, sizeof(fn), "digits/%dkvk", num); + num = 0; + } else if (num < 5 && cn == 3) { + snprintf(fn, sizeof(fn), "digits/%dhk", num); + num = 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; + if (num) + playa++; + } else if (num < 1000) { + int hundreds = num / 100; + /* The number prepending hundreds are in neutral */ + if (hundreds < 5) + snprintf(fn, sizeof(fn), "digits/%dhk", hundreds); + else + snprintf(fn, sizeof(fn), "digits/%d", (num / 100)); + + playh = hundreds; + num -= 100 * hundreds; + if (num && num < 20) + playa++; + /* The 'and' moves forward on even tens. */ + if (num && (num % 10) == 0) + playa++; + } else if (num < 1000000) { + res = ast_say_number_full_is(chan, num / 1000, ints, language, "n", audiofd, ctrlfd); + /* Play 'and' if it's an even hundred. */ + if ((num % 100) == 0 && (num % 1000 != 0)) { + playa++; + } + if (res) + return res; + ast_copy_string(fn, "digits/thousand", sizeof(fn)); + num %= 1000; + if (num && (num < 20 || (num % 10 == 0))) + playa++; + } else if (num < 1000000000) { + int millions = num / 1000000; + /* The number of millions is feminine */ + res = ast_say_number_full_is(chan, millions, ints, language, "f", audiofd, ctrlfd); + if (res) + return res; + if (millions > 1) + ast_copy_string(fn, "digits/millions", sizeof(fn)); + else + ast_copy_string(fn, "digits/million", sizeof(fn)); + num %= 1000000; + if (num && num < 100) + playa++; + } else if (num < INT_MAX) { + int milliards = num / 1000000000; + /* The number of milliards is masculine */ + res = ast_say_number_full_is(chan, milliards, ints, language, "m", audiofd, ctrlfd); + if (res) + return res; + if (milliards > 1) + ast_copy_string(fn, "digits/milliards", sizeof(fn)); + else + ast_copy_string(fn, "digits/milliard", sizeof(fn)); + num %= 1000000000; + if (num && num < 100) + playa++; + } 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 ast_say_number_full_it: Italian */ static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd) { @@ -2789,6 +2919,8 @@ static int say_enumeration_full(struct ast_channel *chan, int num, const char *i return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd); } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */ return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd); + } else if (!strncasecmp(language, "is", 2)) { /* Icelandic syntax */ + return ast_say_enumeration_full_is(chan, num, ints, language, options, audiofd, ctrlfd); } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */ return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd); } @@ -3329,6 +3461,182 @@ static int ast_say_enumeration_full_he(struct ast_channel *chan, int num, const return res; } +/*! \brief ast_say_enumeration_full_is: Icelandic syntax */ +static int ast_say_enumeration_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd) +{ + /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */ + int res = 0, t = 0; + char fn[256] = "", fna[256] = ""; + char *gender; + + if (options && !strncasecmp(options, "f", 1)) { + gender = "F"; + } else if (options && !strncasecmp(options, "n", 1)) { + gender = "N"; + } else { + gender = ""; + } + + if (!num) + return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd); + + while (!res && num) { + if (num < 0) { + ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */ + if ( num > INT_MIN ) { + num = -num; + } else { + num = 0; + } + } else if (num < 100 && t) { + ast_copy_string(fn, "digits/and", sizeof(fn)); + t = 0; + } else if (num < 20) { + snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender); + num = 0; + } else if (num < 100) { + int ones = num % 10; + if (ones) { + int tens = num - ones; + snprintf(fn, sizeof(fn), "digits/h-%d%s", tens, gender); + num = ones; + t++; + } + else if (t) { + snprintf(fn, sizeof(fn), "digits/and"); + t = 0; + } + else { + snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender); + num = 0; + } + + } else if (num == 100 && t == 0) { + snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender); + num = 0; + } else if (num < 1000) { + int hundreds = num / 100; + num = num % 100; + if (hundreds == 1) { + ast_copy_string(fn, "digits/1hk", sizeof(fn)); + } else { + snprintf(fn, sizeof(fn), "digits/%d", hundreds); + } + if (num) { + ast_copy_string(fna, "digits/hundred", sizeof(fna)); + } else { + snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender); + } + t = 1; + } else if (num < 1000000) { + int thousands = num / 1000; + num = num % 1000; + if (thousands == 1) { + if (num) { + /* Thousand is a neutral word, so use the neutral recording */ + ast_copy_string(fn, "digits/1hk", sizeof(fn)); + ast_copy_string(fna, "digits/thousand", sizeof(fna)); + } else { + if (t) { + ast_copy_string(fn, "digits/1hk", sizeof(fn)); + snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender); + } else { + snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender); + } + } + } else { + res = ast_say_number_full_is(chan, thousands, ints, language, options, audiofd, ctrlfd); + if (res) { + return res; + } + if (num) { + ast_copy_string(fn, "digits/thousand", sizeof(fn)); + } else { + snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender); + } + } + if (num) + t = 1; + } else if (num < 1000000000) { + int millions = num / 1000000; + num = num % 1000000; + if (millions == 1) { + if (num) { + /* Million is a feminine word, so use the female form */ + ast_copy_string(fn, "digits/1kvk", sizeof(fn)); + ast_copy_string(fna, "digits/million", sizeof(fna)); + } else { + ast_copy_string(fn, "digits/1hk", sizeof(fn)); + snprintf(fna, sizeof(fna), "digits/h-million%s", gender); + } + } else { + res = ast_say_number_full_is(chan, millions, ints, language, options, audiofd, ctrlfd); + if (res) { + return res; + } + if (num) { + ast_copy_string(fn, "digits/millions", sizeof(fn)); + } else { + snprintf(fn, sizeof(fn), "digits/h-million%s", gender); + } + } + if (num) + t = 1; + } else if (num < INT_MAX) { + int billions = num / 1000000000; + num = num % 1000000000; + if (billions == 1) { + if (num) { + ast_copy_string(fn, "digits/1", sizeof(fn)); + ast_copy_string(fna, "digits/milliard", sizeof(fna)); + } else { + ast_copy_string(fn, "digits/1hk", sizeof(fn)); + snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender); + } + } else { + res = ast_say_number_full_is(chan, billions, ints, language, options, audiofd, ctrlfd); + if (res) + return res; + if (num) { + ast_copy_string(fn, "digits/milliards", sizeof(fna)); + } else { + snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender); + } + } + if (num) + t = 1; + } else if (num == INT_MAX) { + snprintf(fn, sizeof(fn), "digits/h-last%s", gender); + num = 0; + } 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); + if (!res) { + if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) { + if ((audiofd > -1) && (ctrlfd > -1)) { + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + } else { + res = ast_waitstream(chan, ints); + } + } + ast_stopstream(chan); + strcpy(fna, ""); + } + } + } + return res; +} + static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang) { if (!strncasecmp(lang, "en", 2)) { /* English syntax */ @@ -3353,6 +3661,8 @@ static int say_date(struct ast_channel *chan, time_t t, const char *ints, const return ast_say_date_he(chan, t, ints, lang); } else if (!strncasecmp(lang, "hu", 2)) { /* Hungarian syntax */ return ast_say_date_hu(chan, t, ints, lang); + } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */ + return ast_say_date_is(chan, t, ints, lang); } else if (!strncasecmp(lang, "ka", 2)) { /* Georgian syntax */ return ast_say_date_ka(chan, t, ints, lang); } else if (!strncasecmp(lang, "nl", 2)) { /* Dutch syntax */ @@ -3682,6 +3992,55 @@ int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const return res; } +/* Icelandic syntax */ +int ast_say_date_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang) +{ + struct timeval when = { t, 0 }; + struct ast_tm tm; + char fn[256]; + int res = 0; + ast_localtime(&when, &tm, NULL); + if (!res) { + snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday); + res = ast_streamfile(chan, fn, lang); + if (!res) + res = ast_waitstream(chan, ints); + } + if (!res) + res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL); + if (!res) + res = ast_waitstream(chan, ints); + if (!res) { + snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon); + res = ast_streamfile(chan, fn, lang); + if (!res) + res = ast_waitstream(chan, ints); + } + if (!res) { + /* Year */ + int year = tm.tm_year + 1900; + if (year > 1999) { /* year 2000 and later */ + res = ast_say_number(chan, year, ints, lang, (char *) NULL); + } else { + if (year < 1100) { + /* I'm not going to handle 1100 and prior */ + /* We'll just be silent on the year, instead of bombing out. */ + } else { + /* year 1100 to 1999. will anybody need this?!? */ + snprintf(fn, sizeof(fn), "digits/%d", (year / 100)); + res = wait_file(chan, ints, fn, lang); + if (!res) { + res = wait_file(chan, ints, "digits/hundred", lang); + if (!res && year % 100 != 0) { + res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL); + } + } + } + } + } + return res; +} + static int say_date_with_format(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone) { if (!strncasecmp(lang, "en", 2)) { /* English syntax */ @@ -3698,6 +4057,8 @@ static int say_date_with_format(struct ast_channel *chan, time_t t, const char * return ast_say_date_with_format_fr(chan, t, ints, lang, format, tzone); } else if (!strncasecmp(lang, "gr", 2)) { /* Greek syntax */ return ast_say_date_with_format_gr(chan, t, ints, lang, format, tzone); + } else if (!strncasecmp(lang, "is", 2)) { /* Icelandic syntax */ + return ast_say_date_with_format_is(chan, t, ints, lang, format, tzone); } else if (!strncasecmp(lang, "ja", 2)) { /* Japanese syntax */ return ast_say_date_with_format_ja(chan, t, ints, lang, format, tzone); } else if (!strncasecmp(lang, "it", 2)) { /* Italian syntax */ @@ -4384,6 +4745,218 @@ int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const char * return res; } +/* Icelandic syntax */ +int ast_say_date_with_format_is(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone) +{ + struct timeval when = { t, 0 }; + struct ast_tm tm; + int res=0, offset, sndoffset; + char sndfile[256], nextmsg[256]; + + if (!format) + format = "A dBY HMS"; + + ast_localtime(&when, &tm, tzone); + + for (offset=0 ; format[offset] != '\0' ; offset++) { + ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format); + switch (format[offset]) { + /* NOTE: if you add more options here, please try to be consistent with strftime(3) */ + case '\'': + /* Literal name of a sound file */ + for (sndoffset = 0; !strchr("\'\0", format[++offset]) && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) { + sndfile[sndoffset] = format[offset]; + } + sndfile[sndoffset] = '\0'; + res = wait_file(chan, ints, sndfile, lang); + break; + case 'A': + case 'a': + /* Sunday - Saturday */ + snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday); + res = wait_file(chan, ints, nextmsg, lang); + break; + case 'B': + case 'b': + case 'h': + /* January - December */ + snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon); + res = wait_file(chan, ints, nextmsg, lang); + break; + case 'm': + /* Month enumerated */ + res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m"); + break; + case 'd': + case 'e': + /* First - Thirtyfirst */ + res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m"); + break; + case 'Y': + /* Year */ + { + int year = tm.tm_year + 1900; + if (year > 1999) { /* year 2000 and later */ + res = ast_say_number(chan, year, ints, lang, (char *) NULL); + } else { + if (year < 1100) { + /* I'm not going to handle 1100 and prior */ + /* We'll just be silent on the year, instead of bombing out. */ + } else { + /* year 1100 to 1999. will anybody need this?!? */ + /* say 1967 as 'nineteen hundred seven and sixty' */ + snprintf(nextmsg, sizeof(nextmsg), "digits/%d", (year / 100) ); + res = wait_file(chan, ints, nextmsg, lang); + if (!res) { + res = wait_file(chan, ints, "digits/hundred", lang); + if (!res && year % 100 != 0) { + res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL); + } + } + } + } + } + break; + case 'I': + case 'l': + /* 12-Hour */ + res = wait_file(chan, ints, "digits/oclock", lang); + if (tm.tm_hour == 0) + ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg)); + else if (tm.tm_hour > 12) + snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12); + else + snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour); + if (!res) { + res = wait_file(chan, ints, nextmsg, lang); + } + break; + case 'H': + /* 24-Hour, single digit hours preceeded by "oh" (0) */ + if (tm.tm_hour < 10 && tm.tm_hour > 0) { + res = wait_file(chan, ints, "digits/0", lang); + } + /* FALLTRHU */ + case 'k': + /* 24-Hour */ + res = ast_say_number(chan, tm.tm_hour, ints, lang, "n"); + break; + case 'M': + /* Minute */ + if (tm.tm_min > 0 || next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */ + if (tm.tm_min < 10) + res = wait_file(chan, ints, "digits/0", lang); + /* Gender depends on whether or not seconds follow */ + if (next_item(&format[offset + 1]) == 'S') + res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); + else + res = ast_say_number(chan, tm.tm_min, ints, lang, "n"); + } + if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */ + /* Say minute/minutes depending on whether minutes end in 1 */ + if ((tm.tm_min % 10 == 1) && (tm.tm_min != 11)) { + res = wait_file(chan, ints, "digits/minute", lang); + } else { + res = wait_file(chan, ints, "digits/minutes", lang); + } + } + break; + case 'P': + case 'p': + /* AM/PM */ + if (tm.tm_hour > 11) + ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg)); + else + ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg)); + res = wait_file(chan, ints, nextmsg, lang); + break; + case 'Q': + /* Shorthand for "Today", "Yesterday", or AdBY */ + /* XXX As emphasized elsewhere, this should the native way in your + * language to say the date, with changes in what you say, depending + * upon how recent the date is. XXX */ + { + struct timeval now = ast_tvnow(); + struct ast_tm tmnow; + time_t beg_today; + + ast_localtime(&now, &tmnow, tzone); + /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */ + /* In any case, it saves not having to do ast_mktime() */ + beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec); + if (beg_today < t) { + /* Today */ + res = wait_file(chan, ints, "digits/today", lang); + } else if (beg_today - 86400 < t) { + /* Yesterday */ + res = wait_file(chan, ints, "digits/yesterday", lang); + } else { + res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone); + } + } + break; + case 'q': + /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */ + /* XXX As emphasized elsewhere, this should the native way in your + * language to say the date, with changes in what you say, depending + * upon how recent the date is. XXX */ + { + struct timeval now = ast_tvnow(); + struct ast_tm tmnow; + time_t beg_today; + + ast_localtime(&now, &tmnow, tzone); + /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */ + /* In any case, it saves not having to do ast_mktime() */ + beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec); + if (beg_today < t) { + /* Today */ + } else if ((beg_today - 86400) < t) { + /* Yesterday */ + res = wait_file(chan, ints, "digits/yesterday", lang); + } else if (beg_today - 86400 * 6 < t) { + /* Within the last week */ + res = ast_say_date_with_format_is(chan, t, ints, lang, "A", tzone); + } else { + res = ast_say_date_with_format_is(chan, t, ints, lang, "AdBY", tzone); + } + } + break; + case 'R': + res = ast_say_date_with_format_is(chan, t, ints, lang, "HM", tzone); + break; + case 'S': + /* Seconds */ + res = wait_file(chan, ints, "digits/and", lang); + if (!res) { + res = ast_say_number(chan, tm.tm_sec, ints, lang, "f"); + /* Say minute/minutes depending on whether seconds end in 1 */ + if (!res && (tm.tm_sec % 10 == 1) && (tm.tm_sec != 11)) { + res = wait_file(chan, ints, "digits/second", lang); + } else { + res = wait_file(chan, ints, "digits/seconds", lang); + } + } + break; + case 'T': + res = ast_say_date_with_format_is(chan, t, ints, lang, "HMS", tzone); + break; + case ' ': + case ' ': + /* Just ignore spaces and tabs */ + break; + default: + /* Unknown character */ + ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset); + } + /* Jump out on DTMF */ + if (res) { + break; + } + } + return res; +} + /*! \brief Thai syntax */ int ast_say_date_with_format_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone) {