[Asterisk-code-review] apps/app voicemail.c and main/say.c: Add support for Iceland... (asterisk[master])

Örn Arnarson asteriskteam at digium.com
Mon Jun 6 11:18:55 CDT 2016


Örn Arnarson has uploaded a new change for review.

  https://gerrit.asterisk.org/2946

Change subject: apps/app_voicemail.c and main/say.c: Add support for Icelandic language
......................................................................

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
---
M apps/app_voicemail.c
M main/say.c
2 files changed, 652 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/46/2946/1

diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index 1d5b2dc..586086c 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -8527,6 +8527,8 @@
 		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 */
@@ -8752,6 +8754,12 @@
 				res = wait_file2(chan, vms, "vm-message");
 				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)) {
@@ -9492,6 +9500,75 @@
 	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 @@
 		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 9579293..6e51de2 100644
--- a/main/say.c
+++ b/main/say.c
@@ -317,6 +317,7 @@
       \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_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_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_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_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 @@
 	   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 */
@@ -1517,6 +1524,129 @@
 	}
 	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 @@
 	   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 @@
 	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 @@
 		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 @@
 	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 @@
 		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 @@
 	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)
 {

-- 
To view, visit https://gerrit.asterisk.org/2946
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib7d8bd7b0fede5767921ed821315b5b508c0e665
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Örn Arnarson <orn at arnarson.net>



More information about the asterisk-code-review mailing list