json: Add conditionals to avoid locking if Jansson is thread safe.
[asterisk/asterisk.git] / main / say.c
index 9579293..a44ba85 100644 (file)
@@ -37,8 +37,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_REGISTER_FILE()
-
 #include <netinet/in.h>
 #include <time.h>
 #include <ctype.h>
@@ -317,6 +315,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 +372,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 +394,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 +410,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 +418,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 +514,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 */
@@ -993,7 +998,7 @@ static int ast_say_number_full_de(struct ast_channel *chan, int num, const char
 
 /*! \brief  ast_say_number_full_en_GB: British syntax
  New files:
-  - In addition to American English, the following sounds are required:  "and"
+  - In addition to American English, the following sounds are required:  "vm-and"
  */
 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
 {
@@ -1016,7 +1021,7 @@ static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const ch
                        ast_copy_string(fn, "digits/hundred", sizeof(fn));
                        playh = 0;
                } else if (playa) {
-                       ast_copy_string(fn, "digits/and", sizeof(fn));
+                       ast_copy_string(fn, "vm-and", sizeof(fn));
                        playa = 0;
                } else if (num < 20) {
                        snprintf(fn, sizeof(fn), "digits/%d", num);
@@ -1518,6 +1523,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 +2917,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 +3459,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 +3659,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 +3990,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 +4055,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 */
@@ -4082,9 +4441,9 @@ int ast_say_date_with_format_da(struct ast_channel *chan, time_t t, const char *
                                }
                                if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
                                        if (tm.tm_min == 1) {
-                                               res = wait_file(chan, ints, "digits/minute", lang);
+                                               res = wait_file(chan, ints, "minute", lang);
                                        } else {
-                                               res = wait_file(chan, ints, "digits/minutes", lang);
+                                               res = wait_file(chan, ints, "minutes", lang);
                                        }
                                }
                                break;
@@ -4158,7 +4517,7 @@ int ast_say_date_with_format_da(struct ast_channel *chan, time_t t, const char *
                                if (!res) {
                                        res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
                                        if (!res) {
-                                               res = wait_file(chan, ints, "digits/seconds", lang);
+                                               res = wait_file(chan, ints, "seconds", lang);
                                        }
                                }
                                break;
@@ -4278,16 +4637,16 @@ int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const char *
                        case 'M':
                                /* Minute */
                                if (next_item(&format[offset + 1]) == 'S') { /* zero 'digits/0' only if seconds follow */
-                                       res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); /* female only if we say digits/minutes */
+                                       res = ast_say_number(chan, tm.tm_min, ints, lang, "f"); /* female only if we say minutes */
                                } else if (tm.tm_min > 0) {
                                        res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
                                }
 
                                if (!res && next_item(&format[offset + 1]) == 'S') { /* minutes only if seconds follow */
                                        if (tm.tm_min == 1) {
-                                               res = wait_file(chan, ints, "digits/minute", lang);
+                                               res = wait_file(chan, ints, "minute", lang);
                                        } else {
-                                               res = wait_file(chan, ints, "digits/minutes", lang);
+                                               res = wait_file(chan, ints, "minutes", lang);
                                        }
                                }
                                break;
@@ -4361,7 +4720,7 @@ int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const char *
                                if (!res) {
                                        res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
                                        if (!res) {
-                                               res = wait_file(chan, ints, tm.tm_sec == 1 ? "digits/second" : "digits/seconds", lang);
+                                               res = wait_file(chan, ints, tm.tm_sec == 1 ? "second" : "seconds", lang);
                                        }
                                }
                                break;
@@ -4384,6 +4743,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, "minute", lang);
+                                       } else {
+                                               res = wait_file(chan, ints, "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, "second", lang);
+                                       } else {
+                                               res = wait_file(chan, ints, "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)
 {
@@ -5081,7 +5652,7 @@ int ast_say_date_with_format_fr(struct ast_channel *chan, time_t t, const char *
                                /* Seconds */
                                res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
                                if (!res) {
-                                       res = wait_file(chan, ints, "digits/second", lang);
+                                       res = wait_file(chan, ints, "second", lang);
                                }
                                break;
                        case 'T':
@@ -5201,13 +5772,14 @@ int ast_say_date_with_format_it(struct ast_channel *chan, time_t t, const char *
                        case 'I':
                        case 'l':
                                /* 12-Hour */
-                               if (tm.tm_hour == 0)
+                               if (tm.tm_hour == 0) {
                                        ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
-                               else if (tm.tm_hour > 12)
+                               } else if (tm.tm_hour > 12) {
                                        snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
-                               else
+                               } else {
                                        snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
-                                       res = wait_file(chan, ints, nextmsg, lang);
+                               }
+                               res = wait_file(chan, ints, nextmsg, lang);
                                break;
                        case 'H':
                        case 'k':
@@ -5227,11 +5799,12 @@ int ast_say_date_with_format_it(struct ast_channel *chan, time_t t, const char *
                        case 'P':
                        case 'p':
                                /* AM/PM */
-                               if (tm.tm_hour > 11)
+                               if (tm.tm_hour > 11) {
                                        ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
-                               else
+                               } else {
                                        ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
-                                       res = wait_file(chan, ints, nextmsg, lang);
+                               }
+                               res = wait_file(chan, ints, nextmsg, lang);
                                break;
                        case 'Q':
                                /* Shorthand for "Today", "Yesterday", or ABdY */
@@ -5730,9 +6303,9 @@ int ast_say_date_with_format_pl(struct ast_channel *chan, time_t thetime, const
                                                        one = tm.tm_sec % 10;
 
                                                        if (one > 1 && one < 5 && ten != 1)
-                                                               res = wait_file(chan, ints, "digits/seconds", lang);
+                                                               res = wait_file(chan, ints, "seconds", lang);
                                                        else
-                                                               res = wait_file(chan, ints, "digits/second", lang);
+                                                               res = wait_file(chan, ints, "second", lang);
                                                }
                                        }
                                }
@@ -5896,9 +6469,9 @@ int ast_say_date_with_format_pt(struct ast_channel *chan, time_t t, const char *
                                        res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
                                        if (!res) {
                                                if (tm.tm_min > 1) {
-                                                       res = wait_file(chan, ints, "digits/minutes", lang);
+                                                       res = wait_file(chan, ints, "minutes", lang);
                                                } else {
-                                                       res = wait_file(chan, ints, "digits/minute", lang);
+                                                       res = wait_file(chan, ints, "minute", lang);
                                                }
                                        }
                                } else {
@@ -5994,9 +6567,9 @@ int ast_say_date_with_format_pt(struct ast_channel *chan, time_t t, const char *
                                        res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
                                        if (!res) {
                                                if (tm.tm_sec > 1) {
-                                                       res = wait_file(chan, ints, "digits/seconds", lang);
+                                                       res = wait_file(chan, ints, "seconds", lang);
                                                } else {
-                                                       res = wait_file(chan, ints, "digits/second", lang);
+                                                       res = wait_file(chan, ints, "second", lang);
                                                }
                                        }
                                } else {
@@ -6210,7 +6783,7 @@ int ast_say_date_with_format_zh(struct ast_channel *chan, time_t t, const char *
                                        }
                                }
                                if (!res) {
-                                       res = wait_file(chan, ints, "digits/minute", lang);
+                                       res = wait_file(chan, ints, "minute", lang);
                                }
                                break;
                        case 'P':
@@ -6294,7 +6867,7 @@ int ast_say_date_with_format_zh(struct ast_channel *chan, time_t t, const char *
                                        }
                                }
                                if (!res) {
-                                       res = wait_file(chan, ints, "digits/second", lang);
+                                       res = wait_file(chan, ints, "second", lang);
                                }
                                break;
                        case 'T':
@@ -6449,7 +7022,7 @@ int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const
            if (tm.tm_min > 0) {
                        res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
                        if (!res)
-                               res = ast_streamfile(chan, "digits/minute", lang);
+                               res = ast_streamfile(chan, "minute", lang);
                }
        return res;
 }
@@ -6544,9 +7117,9 @@ int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, con
                        res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
                if (!res) {
                        if (tm.tm_min > 1)
-                               res = wait_file(chan, ints, "digits/minutes", lang);
+                               res = wait_file(chan, ints, "minutes", lang);
                        else
-                               res = wait_file(chan, ints, "digits/minute", lang);
+                               res = wait_file(chan, ints, "minute", lang);
                }
        }
        return res;
@@ -6606,7 +7179,7 @@ int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const
        if (!res)
                res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
        if (!res)
-               res = ast_streamfile(chan, "digits/minute", lang);
+               res = ast_streamfile(chan, "minute", lang);
        if (!res)
                res = ast_waitstream(chan, ints);
        return res;
@@ -7029,7 +7602,7 @@ int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, co
        if (!res)
                res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
        if (!res)
-               res = ast_streamfile(chan, "digits/minute", lang);
+               res = ast_streamfile(chan, "minute", lang);
        if (!res)
                res = ast_waitstream(chan, ints);
        return res;
@@ -7907,7 +8480,7 @@ static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t t, const
                        if (!res)
                                res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
                        if (!res)
-                               ast_copy_string(nextmsg, "digits/seconds", sizeof(nextmsg));
+                               ast_copy_string(nextmsg, "seconds", sizeof(nextmsg));
                        res = wait_file(chan, ints, nextmsg, lang);
                        break;
                case 'T':
@@ -7948,9 +8521,9 @@ int ast_say_date_with_format_ja(struct ast_channel *chan, time_t time, const cha
                      /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
                      case '\'':
                              /* Literal name of a sound file */
-                             sndoffset=0;
-                             for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+                             for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
                                      sndfile[sndoffset] = format[offset];
+                             }
                              sndfile[sndoffset] = '\0';
                              res = wait_file(chan,ints,sndfile,lang);
                              break;