Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Say numbers and dates (maybe words one day too)
*
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <time.h>
#include <asterisk/file.h>
#include <asterisk/channel.h>
#include <asterisk/logger.h>
#include <asterisk/say.h>
#include <asterisk/lock.h>
#include <asterisk/localtime.h>
#include "asterisk.h"
Mark Spencer
committed
/* Forward declaration */
static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang);
int ast_say_digit_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
char fn[256] = "";
int num = 0;
int res = 0;
while(fn2[num] && !res) {
switch (fn2[num]) {
case ('*'):
snprintf(fn, sizeof(fn), "digits/star");
break;
case ('#'):
snprintf(fn, sizeof(fn), "digits/pound");
break;
default:
if((fn2[num] >= '0') && (fn2[num] <= '9')){ /* Must be in {0-9} */
snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
}
if(!ast_strlen_zero(fn)){ /* if length == 0, then skip this digit as it is invalid */
res = ast_streamfile(chan, fn, lang);
res = ast_waitstream(chan, ints);
ast_stopstream(chan);
}
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
int ast_say_character_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
{
/* XXX Merge with full version? XXX */
char fn[256] = "";
char ltr;
int num = 0;
int res = 0;
while(fn2[num] && !res) {
fn[0] = '\0';
switch (fn2[num]) {
case ('*'):
snprintf(fn, sizeof(fn), "digits/star");
break;
case ('#'):
snprintf(fn, sizeof(fn), "digits/pound");
break;
case ('0'):
case ('1'):
case ('2'):
case ('3'):
case ('4'):
case ('5'):
case ('6'):
case ('7'):
case ('8'):
case ('9'):
snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
break;
case ('!'):
strncpy(fn, "letters/exclaimation-point", sizeof(fn));
break;
case ('@'):
strncpy(fn, "letters/at", sizeof(fn));
break;
case ('$'):
strncpy(fn, "letters/dollar", sizeof(fn));
break;
case ('-'):
strncpy(fn, "letters/dash", sizeof(fn));
break;
case ('.'):
strncpy(fn, "letters/dot", sizeof(fn));
break;
case ('='):
strncpy(fn, "letters/equals", sizeof(fn));
break;
case ('+'):
strncpy(fn, "letters/plus", sizeof(fn));
break;
case ('/'):
strncpy(fn, "letters/slash", sizeof(fn));
break;
case (' '):
strncpy(fn, "letters/space", sizeof(fn));
break;
default:
ltr = fn2[num];
if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
snprintf(fn, sizeof(fn), "letters/%c", ltr);
}
if(!ast_strlen_zero(fn)) { /* if length == 0, then skip this digit as it is invalid */
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
res = ast_streamfile(chan, fn, lang);
if (!res)
res = ast_waitstream(chan, ints);
} ast_stopstream(chan);
num++;
}
return res;
}
int ast_say_phonetic_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
{
/* XXX Merge with full version? XXX */
char fn[256] = "";
char ltr;
int num = 0;
int res = 0;
int temp;
int play;
char hex[3];
/* while(fn2[num] && !res) { */
while(fn2[num]) {
play=1;
switch (fn2[num]) {
case ('*'):
snprintf(fn, sizeof(fn), "digits/star");
break;
case ('#'):
snprintf(fn, sizeof(fn), "digits/pound");
break;
case ('0'):
case ('1'):
case ('2'):
case ('3'):
case ('4'):
case ('5'):
case ('6'):
case ('7'):
case ('8'):
snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
break;
case ('!'):
strncpy(fn, "exclaimation-point", sizeof(fn));
break;
case ('@'):
strncpy(fn, "at", sizeof(fn));
break;
case ('$'):
strncpy(fn, "dollar", sizeof(fn));
break;
case ('-'):
strncpy(fn, "dash", sizeof(fn));
break;
case ('.'):
strncpy(fn, "dot", sizeof(fn));
break;
case ('='):
strncpy(fn, "equals", sizeof(fn));
break;
case ('+'):
strncpy(fn, "plus", sizeof(fn));
break;
case ('/'):
strncpy(fn, "slash", sizeof(fn));
break;
case (' '):
strncpy(fn, "space", sizeof(fn));
break;
case ('%'):
play=0;
/* check if we have 2 chars after the % */
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
{
hex[0]=fn2[num+1];
hex[1]=fn2[num+2];
hex[2]='\0';
if (sscanf(hex,"%x", &temp))
{ /* Hex to char convertion successfull */
fn2[num+2]=temp;
num++;
if (temp==37)
{ /* If it is a percent, play it now */
strncpy(fn, "percent", sizeof(fn));
num++;
play=1;
}
/* check for invalid characters */
if ((temp<32) || (temp>126))
{
num++;
}
}
}
else
num++;
break;
default: /* '9' falls through to here, too */
ltr = tolower(fn2[num]);
snprintf(fn, sizeof(fn), "phonetic/%c_p", ltr);
}
if (play)
{
res = ast_streamfile(chan, fn, lang);
if (!res)
res = ast_waitstream(chan, ints);
ast_stopstream(chan);
}
num++;
}
return res;
}
int ast_say_digit_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
{
char fn[256] = "";
int num = 0;
int res = 0;
while(fn2[num] && !res) {
snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
res = ast_streamfile(chan, fn, lang);
if (!res)
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
ast_stopstream(chan);
num++;
}
return res;
}
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
int ast_say_character_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
{
char fn[256] = "";
char ltr;
int num = 0;
int res = 0;
while(fn2[num] && !res) {
switch (fn2[num]) {
case ('*'):
snprintf(fn, sizeof(fn), "digits/star");
break;
case ('#'):
snprintf(fn, sizeof(fn), "digits/pound");
break;
case ('0'):
case ('1'):
case ('2'):
case ('3'):
case ('4'):
case ('5'):
case ('6'):
case ('7'):
case ('8'):
case ('9'):
snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
break;
case ('!'):
strncpy(fn, "exclaimation-point", sizeof(fn));
break;
case ('@'):
strncpy(fn, "at", sizeof(fn));
break;
case ('$'):
strncpy(fn, "dollar", sizeof(fn));
break;
case ('-'):
strncpy(fn, "dash", sizeof(fn));
break;
case ('.'):
strncpy(fn, "dot", sizeof(fn));
break;
case ('='):
strncpy(fn, "equals", sizeof(fn));
break;
case ('+'):
strncpy(fn, "plus", sizeof(fn));
break;
case ('/'):
strncpy(fn, "slash", sizeof(fn));
break;
case (' '):
strncpy(fn, "space", sizeof(fn));
break;
default:
ltr = fn2[num];
if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
snprintf(fn, sizeof(fn), "letters/%c", ltr);
}
/* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
res = ast_streamfile(chan, fn, lang);
if (!res)
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
ast_stopstream(chan);
num++;
}
return res;
}
int ast_say_phonetic_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
{
char fn[256] = "";
char ltr;
int num = 0;
int res = 0;
while(fn2[num] && !res) {
switch (fn2[num]) {
case ('*'):
snprintf(fn, sizeof(fn), "digits/star");
break;
case ('#'):
snprintf(fn, sizeof(fn), "digits/pound");
break;
case ('0'):
case ('1'):
case ('2'):
case ('3'):
case ('4'):
case ('5'):
case ('6'):
case ('7'):
case ('8'):
snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
break;
case ('!'):
strncpy(fn, "exclaimation-point", sizeof(fn));
break;
case ('@'):
strncpy(fn, "at", sizeof(fn));
break;
case ('$'):
strncpy(fn, "dollar", sizeof(fn));
break;
case ('-'):
strncpy(fn, "dash", sizeof(fn));
break;
case ('.'):
strncpy(fn, "dot", sizeof(fn));
break;
case ('='):
strncpy(fn, "equals", sizeof(fn));
break;
case ('+'):
strncpy(fn, "plus", sizeof(fn));
break;
case ('/'):
strncpy(fn, "slash", sizeof(fn));
break;
case (' '):
strncpy(fn, "space", sizeof(fn));
break;
default: /* '9' falls here... */
ltr = fn2[num];
if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
snprintf(fn, sizeof(fn), "phonetic/%c", ltr);
}
/* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
res = ast_streamfile(chan, fn, lang);
if (!res)
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
ast_stopstream(chan);
num++;
}
return res;
}
int ast_say_digits(struct ast_channel *chan, int num, char *ints, char *lang)
/* XXX Should I be merged with say_digits_full XXX */
char fn2[256];
snprintf(fn2, sizeof(fn2), "%d", num);
int ast_say_digits_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd)
{
char fn2[256];
snprintf(fn2, sizeof(fn2), "%d", num);
return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd);
}
Mark Spencer
committed
/* Forward declarations */
/* Syntaxes supported, not really language codes.
da - Danish
de - German
es - Spanish, Mexican
Mark Spencer
committed
it - Italian
nl - Dutch
pl - Polish
Mark Spencer
committed
For Portuguese, French & Spanish, we're using m & f options to saynumber() to indicate if the gender is masculine or feminine.
For Danish, we're using c & n options to saynumber() to indicate if the gender is commune or neutrum.
This still needs to be implemented for German (although the option is passed to the function, it currently does nothing with it).
Date/Time functions currently have less languages supported than saynumber().
Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
See contrib/i18n.testsuite.conf for some examples of the different syntaxes
Mark Spencer
committed
Portuguese sound files needed for Time/Date functions:
pt-ah
pt-ao
pt-de
pt-e
pt-ora
pt-meianoite
pt-meiodia
pt-sss
Spanish sound files needed for Time/Date functions:
es-de
es-el
Mark Spencer
committed
*/
/* Forward declarations of language specific variants of ast_say_number_full */
Mark Spencer
committed
static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
Mark Spencer
committed
static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
static int ast_say_number_full_pl(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
static int ast_say_number_full_tw(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
/* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
static int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
static int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
static int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
static int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
static int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_time_tw(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang)
{
int res;
if ((res = ast_streamfile(chan, file, lang)))
ast_log(LOG_WARNING, "Unable to play message %s\n", file);
if (!res)
res = ast_waitstream(chan, ints);
return res;
}
Mark Spencer
committed
/*--- ast_say_number_full: call language-specific functions */
/* Called from AGI */
int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
Mark Spencer
committed
{
if (!strcasecmp(language,"en") ) { /* English syntax */
Mark Spencer
committed
return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
} else if (!strcasecmp(language, "da") ) { /* Danish syntax */
return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
} else if (!strcasecmp(language, "de") ) { /* German syntax */
return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
} else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
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, "it") ) { /* Italian syntax */
return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
} else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
Mark Spencer
committed
return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
} else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
} else if (!strcasecmp(language, "pt") ) { /* Portuguese syntax */
return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
} else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
} else if (!strcasecmp(language, "tw")) { /* Taiwanese syntax */
return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
Mark Spencer
committed
}
/* Default to english */
return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
}
/*--- ast_say_number: call language-specific functions without file descriptors */
int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language, char *options)
{
return(ast_say_number_full(chan, num, ints, language, options, -1, -1));
Mark Spencer
committed
}
/*--- ast_say_number_full_en: English syntax */
/* This is the default syntax, if no other syntax defined in this file is used */
static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
{
int res = 0;
int playh = 0;
char fn[256] = "";
if (!num)
return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
Mark Spencer
committed
while(!res && (num || playh)) {
if (playh) {
snprintf(fn, sizeof(fn), "digits/hundred");
playh = 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 -= ((num / 10) * 10);
} else {
if (num < 1000){
snprintf(fn, sizeof(fn), "digits/%d", (num/100));
playh++;
num -= ((num / 100) * 100);
} else {
if (num < 1000000) { /* 1,000,000 */
Mark Spencer
committed
res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
if (res)
return res;
num = num % 1000;
snprintf(fn, sizeof(fn), "digits/thousand");
} else {
if (num < 1000000000) { /* 1,000,000,000 */
Mark Spencer
committed
res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
if (res)
return res;
num = num % 1000000;
snprintf(fn, sizeof(fn), "digits/million");
} 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 && ctrlfd)
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
else
res = ast_waitstream(chan, ints);
}
ast_stopstream(chan);
Mark Spencer
committed
}
Mark Spencer
committed
/*--- ast_say_number_full_da: Danish syntax */
/* New files:
In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
*/
static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
{
int res = 0;
int playh = 0;
int playa = 0;
int cn = 1; /* +1 = Commune; -1 = Neutrum */
char fn[256] = "";
if (!num)
return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
if (options && !strncasecmp(options, "n",1)) cn = -1;
while(!res && (num || playh || playa )) {
/* The grammar for Danish numbers is the same as for English except
* for the following:
Mark Spencer
committed
* - 1 exists in both commune ("en", file "1N") and neutrum ("et", file "1")
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
* - numbers 20 through 99 are said in reverse order, i.e. 21 is
* "one-and twenty" and 68 is "eight-and sixty".
* - "million" is different in singular and plural form
* - numbers > 1000 with zero as the third digit from last have an
* "and" before the last two digits, i.e. 2034 is "two thousand and
* four-and thirty" and 1000012 is "one million and twelve".
*/
if (playh) {
snprintf(fn, sizeof(fn), "digits/hundred");
playh = 0;
} else if (playa) {
snprintf(fn, sizeof(fn), "digits/and");
playa = 0;
} else if (num == 1 && cn == -1) {
snprintf(fn, sizeof(fn), "digits/1N");
num = 0;
} else if (num < 20) {
snprintf(fn, sizeof(fn), "digits/%d", num);
num = 0;
} else if (num < 100) {
int ones = num % 10;
if (ones) {
snprintf(fn, sizeof(fn), "digits/%d-and", ones);
num -= ones;
} else {
snprintf(fn, sizeof(fn), "digits/%d", num);
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
}
} else {
if (num < 1000) {
int hundreds = num / 100;
if (hundreds == 1)
snprintf(fn, sizeof(fn), "digits/1N");
else
snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
playh++;
num -= 100 * hundreds;
if (num)
playa++;
} else {
if (num < 1000000) {
res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
if (res)
return res;
num = num % 1000;
snprintf(fn, sizeof(fn), "digits/thousand");
} else {
if (num < 1000000000) {
int millions = num / 1000000;
res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
if (res)
return res;
if (millions == 1)
snprintf(fn, sizeof(fn), "digits/million");
else
snprintf(fn, sizeof(fn), "digits/millions");
num = num % 1000000;
ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
res = -1;
}
if (num && num < 100)
playa++;
}
if (!res) {
if(!ast_streamfile(chan, fn, language)) {
if (audiofd && ctrlfd)
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
else
res = ast_waitstream(chan, ints);
}
ast_stopstream(chan);
}
Mark Spencer
committed
}
return res;
}
/*--- ast_say_number_full_de: German syntax */
/* New files:
In addition to English, the following sounds are required:
"millions"
"1-and" through "9-and"
"1F" (eine)
"1N" (ein)
NB "1" is recorded as 'eins'
Mark Spencer
committed
*/
static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
Mark Spencer
committed
{
int res = 0;
int playh = 0;
int t = 0;
int mf = 1; /* +1 = Male, Neutrum; -1 = Female */
Mark Spencer
committed
char fn[256] = "";
Mark Spencer
committed
if (!num)
return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
if (options && (!strncasecmp(options, "f",1)))
mf = -1;
while(!res && (num || playh)) {
/* The grammar for German numbers is the same as for English except
* for the following:
* - numbers 20 through 99 are said in reverse order, i.e. 21 is
* "one-and twenty" and 68 is "eight-and sixty".
* - "one" varies according to gender
* - 100 is 'hundert', however all other instances are 'ein hundert'
* - 1000 is 'tausend', however all other instances are 'ein tausend'
* - 1000000 is always 'ein million'
* - "million" is different in singular and plural form
*/
if (playh) {
snprintf(fn, sizeof(fn), "digits/hundred");
playh = 0;
} else if (num == 1 && mf == -1) {
snprintf(fn, sizeof(fn), "digits/%dF", num);
num = 0;
} else if (num < 20) {
snprintf(fn, sizeof(fn), "digits/%d", num);
num = 0;
} else if (num < 100) {
int ones = num % 10;
if (ones) {
snprintf(fn, sizeof(fn), "digits/%d-and", ones);
num -= ones;
} else {
snprintf(fn, sizeof(fn), "digits/%d", num);
num = 0;
}
} else if (num == 100) {
snprintf(fn, sizeof(fn), "digits/hundred");
num = num - 100;
} else if (num < 1000) {
int hundreds = num / 100;
if (hundreds == 1)
snprintf(fn, sizeof(fn), "digits/1N");
else
snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
playh++;
num -= 100 * hundreds;
} else if (num == 1000 && t == 0) {
snprintf(fn, sizeof(fn), "digits/thousand");
num = 0;
} else if (num < 1000000) {
int thousands = num / 1000;
if (thousands == 1) {
snprintf(fn, sizeof(fn), "digits/1N");
snprintf(fna, sizeof(fna), "digits/thousand");
} else {
res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
if (res)
return res;
snprintf(fn, sizeof(fn), "digits/thousand");
}
num = num % 1000;
} else if (num < 1000000000) {
int millions = num / 1000000;
if (millions == 1) {
snprintf(fn, sizeof(fn), "digits/1N");
snprintf(fna, sizeof(fna), "digits/million");
} else {
res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
if (res)
return res;
snprintf(fn, sizeof(fn), "digits/millions");
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 && ctrlfd)
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
else
res = ast_waitstream(chan, ints);
}
ast_stopstream(chan);
if(!ast_streamfile(chan, fna, language)) {
if (audiofd && ctrlfd)
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
else
res = ast_waitstream(chan, ints);
}
ast_stopstream(chan);
strcpy(fna, "");
}
Mark Spencer
committed
/*--- ast_say_number_full_es: Spanish syntax */
/* New files:
Requires a few new audios:
1F.gsm: feminine 'una'
21.gsm thru 29.gsm, cien.gsm, mil.gsm, millon.gsm, millones.gsm, 100.gsm, 200.gsm, 300.gsm, 400.gsm, 500.gsm, 600.gsm, 700.gsm, 800.gsm, 900.gsm, y.gsm
*/
static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
Mark Spencer
committed
{
int res = 0;
int playa = 0;
int mf = 1; /* +1 = Masculin; -1 = Feminin */
Mark Spencer
committed
char fn[256] = "";
if (!num)
return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
if (options && !strncasecmp(options, "f",1))
mf = -1;
while (!res && num) {
if (playa) {
snprintf(fn, sizeof(fn), "digits/y");
playa = 0;
} else if (num == 1) {
if (mf < 0)
snprintf(fn, sizeof(fn), "digits/%dF", num);
else
snprintf(fn, sizeof(fn), "digits/%d", num);
num = 0;
} else if (num < 31) {
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)*10);
if (num)
playa++;
} else if (num == 100) {
snprintf(fn, sizeof(fn), "digits/cien");
num = 0;
} else {
if (num < 1000) {
snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
num -= ((num/100)*100);
} else {
if (num < 1000000) {
res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
if (res)
return res;
num = num % 1000;
snprintf(fn, sizeof(fn), "digits/mil");
} else {
if (num < 2147483640) {
res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
if (res)
return res;
if ((num/1000000) == 1) {
snprintf(fn, sizeof(fn), "digits/millon");
} else {
snprintf(fn, sizeof(fn), "digits/millones");
}
num = num % 1000000;
} else {
ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
res = -1;
}
}
}
}
Mark Spencer
committed
if (!res) {
if(!ast_streamfile(chan, fn, language)) {
if (audiofd && ctrlfd)
res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
else
res = ast_waitstream(chan, ints);
}
ast_stopstream(chan);
Mark Spencer
committed
}
Mark Spencer
committed
}
return res;
}
/*--- ast_say_number_full_fr: French syntax */
/* Extra sounds needed:
1F: feminin 'une'
et: 'and' */
static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
{
int res = 0;
int playh = 0;
int playa = 0;
int mf = 1; /* +1 = Masculin; -1 = Feminin */
char fn[256] = "";
if (!num)
return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
if (options && !strncasecmp(options, "f",1))
mf = -1;
while(!res && (num || playh || playa)) {
if (playh) {
snprintf(fn, sizeof(fn), "digits/hundred");
playh = 0;
} else if (playa) {
snprintf(fn, sizeof(fn), "digits/et");
playa = 0;
} else if (num == 1) {
if (mf < 0)
snprintf(fn, sizeof(fn), "digits/%dF", num);
else
snprintf(fn, sizeof(fn), "digits/%d", num);
num = 0;
} else if (num < 21) {
snprintf(fn, sizeof(fn), "digits/%d", num);
num = 0;
} else if (num < 70) {
snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
if ((num % 10) == 1) playa++;
num = num % 10;
} else if (num < 80) {
snprintf(fn, sizeof(fn), "digits/60");
if ((num % 10) == 1) playa++;
num = num - 60;
} else if (num < 100) {
snprintf(fn, sizeof(fn), "digits/80");
num = num - 80;
} else if (num < 200) {
snprintf(fn, sizeof(fn), "digits/hundred");
num = num - 100;
} else if (num < 1000) {
snprintf(fn, sizeof(fn), "digits/%d", (num/100));
playh++;
num = num % 100;
} else if (num < 2000) {
snprintf(fn, sizeof(fn), "digits/thousand");
num = num - 1000;
} else if (num < 1000000) {
res = ast_say_number_full_fr(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_fr(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 && ctrlfd)
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 */
Mark Spencer
committed
static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
{
int res = 0;
int playh = 0;
int tempnum = 0;
char fn[256] = "";
Mark Spencer
committed
if (!num)
Mark Spencer
committed
return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
/*
Italian support
Like english, numbers up to 20 are a single 'word', and others
Mark Spencer
committed
compound, but with exceptions.
For example 21 is not twenty-one, but there is a single word in 'it'.
Mark Spencer
committed
Idem for 28 (ie when a the 2nd part of a compund number
starts with a vowel)
Mark Spencer
committed
There are exceptions also for hundred, thousand and million.
Mark Spencer
committed
In english 100 = one hundred, 200 is two hundred.
In italian 100 = cento , like to say hundred (without one),
200 and more are like english.