Update of /usr/cvsroot/asterisk
In directory mongoose.digium.com:/tmp/cvs-serv13277

Modified Files:
        say.c 
Log Message:
add support for say_number in Hebrew (bug #4420)


Index: say.c
===================================================================
RCS file: /usr/cvsroot/asterisk/say.c,v
retrieving revision 1.64
retrieving revision 1.65
diff -u -d -r1.64 -r1.65
--- say.c       12 Jul 2005 02:36:53 -0000      1.64
+++ say.c       12 Jul 2005 03:00:49 -0000      1.65
@@ -34,6 +34,7 @@
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
 #include "asterisk/logger.h"
+#include "asterisk/options.h"
 #include "asterisk/say.h"
 #include "asterisk/lock.h"
 #include "asterisk/localtime.h"
@@ -273,6 +274,7 @@
       en_GB - English (British)
       es    - Spanish, Mexican
       fr    - French
+      he    - Hebrew
       it    - Italian
       nl    - Dutch
       no    - Norwegian
@@ -321,6 +323,7 @@
 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const 
char *ints, const char *language, int audiofd, int ctrlfd);
 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_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);
@@ -348,6 +351,7 @@
 static int ast_say_date_with_format_da(struct ast_channel *chan, time_t time, 
const char *ints, const char *lang, const char *format, const char *timezone);
 static int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, 
const char *ints, const char *lang, const char *format, const char *timezone);
 static int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, 
const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_he(struct ast_channel *chan, time_t time, 
const char *ints, const char *lang, const char *format, const char *timezone);
 static int ast_say_date_with_format_fr(struct ast_channel *chan, time_t time, 
const char *ints, const char *lang, const char *format, const char *timezone);
 static int ast_say_date_with_format_it(struct ast_channel *chan, time_t time, 
const char *ints, const char *lang, const char *format, const char *timezone);
 static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, 
const char *ints, const char *lang, const char *format, const char *timezone);
@@ -405,6 +409,8 @@
           return(ast_say_number_full_es(chan, num, ints, language, options, 
audiofd, ctrlfd));
        } else if (!strcasecmp(language, "fr") ) {      /* French syntax */
           return(ast_say_number_full_fr(chan, num, ints, language, options, 
audiofd, ctrlfd));
+       } else if (!strcasecmp(language, "he") ) {      /* Hebrew syntax */
+          return(ast_say_number_full_he(chan, num, ints, language, options, 
audiofd, ctrlfd));
        } else if (!strcasecmp(language, "it") ) {      /* Italian syntax */
           return(ast_say_number_full_it(chan, num, ints, language, audiofd, 
ctrlfd));
        } else if (!strcasecmp(language, "nl") ) {      /* Dutch syntax */
@@ -1140,6 +1146,152 @@
        return res;
 }
 
+
+
+/*--- ast_say_number_full_he: Hebrew syntax */
+/*     Extra sounds needed:
+       1F: feminin 'one'
+       ve: 'and'
+       2hundred: 2 hundred
+       2thousands: 2 thousand 
+       thousands: plural of 'thousand'
+       3sF 'Smichut forms (female)
+       4sF
+       5sF
+       6sF
+       7sF
+       8sF
+       9sF
+       3s 'Smichut' forms (male)
+       4s
+       5s
+       6s
+       7s
+       9s
+       10s
+       11s
+       12s
+       13s
+       14s
+       15s
+       16s
+       17s
+       18s
+       19s
+
+TODO: 've' should sometimed be 'hu':
+* before 'shtaym' (2, F)
+* before 'shnaym' (2, M)
+* before 'shlosha' (3, M)
+* before 'shmone' (8, M)
+* before 'shlosim' (30)
+* before 'shmonim' (80)
+
+What about:
+'sheva' (7, F)?
+'tesha' (9, F)?
+*/
+#define SAY_NUM_BUF_SIZE 256
+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)
+{
+       int res = 0;
+       int state = 0; /* no need to save anything */
+       int mf = 1;    /* +1 = Masculin; -1 = Feminin */
+       char fn[SAY_NUM_BUF_SIZE] = "";
+       ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. "
+               "num: %d, options=\"%s\"\n",
+               num, options
+       );
+       if (!num) 
+               return ast_say_digits_full(chan, 0,ints, language, audiofd, 
ctrlfd);
+       
+       if (options && !strncasecmp(options, "f",1))
+               mf = -1;
+
+       /* Do we have work to do? */
+       while(!res && (num || (state>0) ))  {
+               /* first type of work: play a second sound. In this loop
+                * we can only play one sound file at a time. Thus playing 
+                * a second one requires repeating the loop just for the 
+                * second file. The variable 'state' remembers where we were.
+                * state==0 is the normal mode and it means that we continue
+                * to check if the number num has yet anything left.
+                */
+               ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, "
+                       "state=%d, options=\"%s\", mf=%d\n",
+                       num, state, options, mf
+               );
+               if (state==1) {
+                       snprintf(fn, sizeof(fn), "digits/hundred");
+                       state = 0;
+               } else if (state==2) {
+                       snprintf(fn, sizeof(fn), "digits/ve");
+                       state = 0;
+               } else if (state==3) {
+                       snprintf(fn, sizeof(fn), "digits/thousands");
+                       state=0;
+               } else if (num <21) {
+                       if (mf < 0)
+                               snprintf(fn, sizeof(fn), "digits/%dF", num);
+                       else
+                               snprintf(fn, sizeof(fn), "digits/%d", num);
+                       num = 0;
+               } else if (num < 100) {
+                       snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
+                       num = num % 10;
+                       if (num>0) state=2;
+               } else if (num < 200) {
+                       snprintf(fn, sizeof(fn), "digits/hundred");
+                       num = num - 100;
+               } else if (num < 300) {
+                       snprintf(fn, sizeof(fn), "digits/hundred");
+                       num = num - 100;
+               } else if (num < 1000) {
+                       snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+                       state=1;
+                       num = num % 100;
+               } else if (num < 2000) {
+                       snprintf(fn, sizeof(fn), "digits/thousand");
+                       num = num - 1000;
+               } else if (num < 3000) {
+                       snprintf(fn, sizeof(fn), "digits/2thousand");
+                       num = num - 2000;
+                        if (num>0) state=2;
+               } else if (num < 20000) {
+                       snprintf(fn, sizeof(fn), "digits/%ds",(num/1000));
+                       num = num % 1000;
+                       state=3;
+               } else if (num < 1000000) {
+                       res = ast_say_number_full_he(chan, num / 1000, ints, 
language, options, audiofd, ctrlfd);
+                       if (res)
+                               return res;
+                       snprintf(fn, sizeof(fn), "digits/thousand");
+                       num = num % 1000;
+               } else  if (num < 1000000000) {
+                       res = ast_say_number_full_he(chan, num / 1000000, ints, 
language, options, audiofd, ctrlfd);
+                       if (res)
+                               return res;
+                       snprintf(fn, sizeof(fn), "digits/million");
+                       num = num % 1000000;
+               } else {
+                       ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", 
num);
+                       res = -1;
+               }
+               if (!res) {
+                       if(!ast_streamfile(chan, fn, language)) {
+                               if ((audiofd > -1) && (ctrlfd > -1))
+                                       res = ast_waitstream_full(chan, ints, 
audiofd, ctrlfd);
+                               else
+                                       res = ast_waitstream(chan, ints);
+                       }
+                       ast_stopstream(chan);
+               }
+       }
+       return res;
+}
+
 /*--- ast_say_number_full_it:  Italian */
 static int ast_say_number_full_it(struct ast_channel *chan, int num, const 
char *ints, const char *language, int audiofd, int ctrlfd)
 {
@@ -2674,6 +2826,8 @@
                return(ast_say_date_with_format_de(chan, time, ints, lang, 
format, timezone));
        } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) {        
/* Spanish syntax */
                return(ast_say_date_with_format_es(chan, time, ints, lang, 
format, timezone));
+       } else if (!strcasecmp(lang, "he")) {   /* Hebrew syntax */
+               return(ast_say_date_with_format_he(chan, time, ints, lang, 
format, timezone));
        } else if (!strcasecmp(lang, "fr") ) {  /* French syntax */
                return(ast_say_date_with_format_fr(chan, time, ints, lang, 
format, timezone));
        } else if (!strcasecmp(lang, "it") ) {  /* Italian syntax */
@@ -3339,6 +3493,211 @@
        return res;
 }
 
+/* TODO: this probably is not the correct format for doxygen remarks */
+
+/** ast_say_date_with_format_he Say formmated date in Hebrew
+ *
+ * @seealso ast_say_date_with_format_en for the details of the options 
+ *
+ * Changes from the English version: 
+ *
+ * * don't replicate in here the logic of ast_say_number_full_he
+ *
+ * * year is always 4-digit (because it's simpler)
+ *
+ * * added c, x, and X. Mainly for my tests
+ *
+ * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
+ *
+ * TODO: 
+ * * A "ha" is missing in the standard date format, before the 'd'.
+ * * The numbers of 3000--19000 are not handled well
+ **/
+#define IL_DATE_STR "AdBY"
+#define IL_TIME_STR "IMp"
+#define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
+int ast_say_date_with_format_he(struct ast_channel *chan, time_t time, 
+    const char *ints, const char *lang, const char *format, 
+    const char *timezone)
+{
+       /* TODO: This whole function is cut&paste from 
+        * ast_say_date_with_format_en . Is that considered acceptable?
+        **/
+       struct tm tm;
+       int res=0, offset, sndoffset;
+       char sndfile[256], nextmsg[256];
+
+       ast_localtime(&time,&tm,timezone);
+
+       for (offset=0 ; format[offset] != '\0' ; offset++) {
+               ast_log(LOG_DEBUG, "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 */
+                               sndoffset=0;
+                               for (sndoffset=0 ; (format[++offset] != '\'') 
&& (sndoffset < 256) ; 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 'd':
+                       case 'e': /* Day of the month */
+                                /* I'm not sure exactly what the parameters 
+                                 * audiofd and ctrlfd to 
+                                 * ast_say_number_full_he mean, but it seems
+                                 * safe to pass -1 there. 
+                                 *
+                                 * At least in one of the pathes :-( 
+                                 */
+                               res = ast_say_number_full_he(chan, tm.tm_mday,
+                                       ints, lang, "m", -1, -1
+                               );
+                               break;
+                       case 'Y': /* Year */
+                               res = ast_say_number_full_he(chan, 
tm.tm_year+1900,
+                                       ints, lang, "f", -1, -1
+                               );
+                               break;
+                       case 'I':
+                       case 'l': /* 12-Hour */
+                               {
+                                       int hour = tm.tm_hour;
+                                       hour = hour%12;
+                                       if (hour == 0) hour=12;
+                               
+                                       res = ast_say_number_full_he(chan, hour,
+                                               ints, lang, "f", -1, -1
+                                       );
+                               }
+                               break;
+                       case 'H':
+                       case 'k': /* 24-Hour */
+                               /* With 'H' there is an 'oh' after a single-
+                                * digit hour */
+                               if ((format[offset] == 'H') && 
+                                   (tm.tm_hour <10)&&(tm.tm_hour>0)
+                               ) { /* e.g. oh-eight */
+                                       res = wait_file(chan,ints, 
"digits/oh",lang);
+                               }
+                               
+                               res = ast_say_number_full_he(chan, tm.tm_hour,
+                                       ints, lang, "f", -1, -1
+                               );
+                               break;
+                       case 'M': /* Minute */
+                               res = ast_say_number_full_he(chan, tm.tm_min, 
+                                       ints, lang,"f", -1, -1
+                               );
+                               break;
+                       case 'P':
+                       case 'p':
+                               /* AM/PM */
+                               if (tm.tm_hour > 11)
+                                       snprintf(nextmsg,sizeof(nextmsg), 
"digits/p-m");
+                               else
+                                       snprintf(nextmsg,sizeof(nextmsg), 
"digits/a-m");
+                               res = wait_file(chan,ints,nextmsg,lang);
+                               break;
+                       case 'Q':
+                               /* Shorthand for "Today", "Yesterday", or 
"date" */
+                       case 'q':
+                               /* Shorthand for "" (today), "Yesterday", A 
+                                 * (weekday), or "date" */
+                               {
+                                       char todo='q';/* The letter to format*/
+                                       if (format[offset] == 'Q') {
+                                               todo='Q';
+                                       }
+                                       struct timeval now;
+                                       struct tm tmnow;
+                                       time_t beg_today;
+
+                                       gettimeofday(&now,NULL);
+                                       
ast_localtime(&now.tv_sec,&tmnow,timezone);
+                                       /* 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 < time) {
+                                               /* Today */
+                                               if (todo == 'Q') {
+                                                       res = wait_file(chan,
+                                                               ints, 
+                                                               "digits/today",
+                                                               lang);
+                                               }
+                                       } else if (beg_today - 86400 < time) {
+                                               /* Yesterday */
+                                               res = wait_file(chan,ints, 
"digits/yesterday",lang);
+                                       } else if ((todo != 'Q') &&
+                                               (beg_today - 86400 * 6 < time))
+                                       {
+                                               /* Within the last week */
+                                               res = 
ast_say_date_with_format_he(
+                                                       chan, time, ints, lang, 
+                                                       "A", timezone);
+                                       } else {
+                                               res = 
ast_say_date_with_format_he(
+                                                       chan, time, ints, lang, 
+                                                       IL_DATE_STR, timezone);
+                                       }
+                               }
+                               break;
+                       case 'R':
+                               res = ast_say_date_with_format(chan, time, 
ints, lang, "HM", timezone);
+                               break;
+                       case 'S': /* Seconds */
+                               res = ast_say_number_full_he(chan, tm.tm_sec,
+                                       ints, lang, "f", -1, -1
+                               );
+                               break;
+                       case 'T':
+                               res = ast_say_date_with_format(chan, time, 
ints, lang, "HMS", timezone);
+                               break;
+                       /* c, x, and X seem useful for testing. Not sure
+                         * if thiey're good for the general public */
+                        case 'c':
+                               res = ast_say_date_with_format_he(chan, time, 
+                                    ints, lang, IL_DATE_STR_FULL, timezone);
+                               break;
+                        case 'x':
+                               res = ast_say_date_with_format_he(chan, time, 
+                                    ints, lang, IL_DATE_STR, timezone);
+                               break;
+                        case 'X': /* Currently not locale-dependent...*/
+                               res = ast_say_date_with_format_he(chan, time, 
+                                    ints, lang, IL_TIME_STR, timezone);
+                               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;
+}
+
+
 /* Spanish syntax */
 int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, const 
char *ints, const char *lang, const char *format, const char *timezone)
 {

_______________________________________________
Asterisk-Cvs mailing list
[email protected]
http://lists.digium.com/mailman/listinfo/asterisk-cvs

Reply via email to