add support for say_number in Hebrew (bug #4420)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@6101 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Kevin P. Fleming
2005-07-12 03:00:49 +00:00
parent 2c79cc2fc6
commit 9985055fd7

359
say.c
View File

@@ -34,6 +34,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#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 @@ int ast_say_digits(struct ast_channel *chan, int num, const char *ints, const ch
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_de(struct ast_channel *chan, int num, const char
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_en(struct ast_channel *chan, time_t time, co
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 @@ int ast_say_number_full(struct ast_channel *chan, int num, const char *ints, con
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 @@ static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char
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 @@ int ast_say_date_with_format(struct ast_channel *chan, time_t time, const char *
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 @@ int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, const cha
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)
{