summaryrefslogtreecommitdiff
path: root/main/say.c
diff options
context:
space:
mode:
authorÖrn Arnarson <orn@arnarson.net>2016-06-06 16:13:01 +0000
committerÖrn Arnarson <orn@arnarson.net>2016-06-07 11:36:48 +0000
commit60caebc738d96c092ca1f1f51881ed41e4a52192 (patch)
treede6f837c6b1ffc957c2489e28b72bc27ae47528f /main/say.c
parent3e8d523d889351c69b21fdc563cb98d18a7bdb66 (diff)
apps/app_voicemail.c and main/say.c: Add support for Icelandic language
Icelandic has some weird grammar rules when dealing with dates and numbers. There are different genders used depending on which number you're dealing with, and only a handful of numbers do change depending on the gender. There is also an implied gender in several cases. This patch was originally written for asterisk 1.6, and has been in use for several years without crashes. I cleaned it up a bit and rewrote what was necessary for Asterisk 13. The functions were copied from other similar languages and modified where appropriate. If i recall correctly, the German and Danish functions were used as a base. ASTERISK-26087 Reported by: Örn Arnarson Tested by: Örn Arnarson Change-Id: Ib7d8bd7b0fede5767921ed821315b5b508c0e665
Diffstat (limited to 'main/say.c')
-rw-r--r--main/say.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/main/say.c b/main/say.c
index 957929326..6e51de201 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)
{