[Asterisk-cvs] asterisk say.c,1.64,1.65

kpfleming at lists.digium.com kpfleming at lists.digium.com
Mon Jul 11 22:52:37 CDT 2005


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)
 {




More information about the svn-commits mailing list