2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
7 * George Konstantoulakis <gkon@inaccessnetworks.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
22 * \brief Say numbers and dates (maybe words one day too)
24 * \author Mark Spencer <markster@digium.com>
26 * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <gkon@inaccessnetworks.com>
28 * \note 2007-02-08 : Support for Georgian added by Alexander Shaduri <ashaduri@gmail.com>,
29 * Next Generation Networks (NGN).
30 * \note 2007-03-20 : Support for Thai added by Dome C. <dome@tel.co.th>,
31 * IP Crossing Co.,Ltd.
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include <netinet/in.h>
44 #include <iso/limits_iso.h>
47 #include "asterisk/file.h"
48 #include "asterisk/channel.h"
49 #include "asterisk/say.h"
50 #include "asterisk/lock.h"
51 #include "asterisk/localtime.h"
52 #include "asterisk/utils.h"
54 /* Forward declaration */
55 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
58 static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
61 char fnbuf[10], asciibuf[20] = "letters/ascii";
66 while (str[num] && !res) {
76 fn = "letters/exclaimation-point";
82 fn = "letters/dollar";
91 fn = "letters/equals";
100 fn = "letters/space";
112 strcpy(fnbuf, "digits/X");
118 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
119 strcpy(fnbuf, "letters/X");
123 if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
124 (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
125 res = ast_streamfile(chan, fn, lang);
127 if ((audiofd > -1) && (ctrlfd > -1))
128 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
130 res = ast_waitstream(chan, ints);
132 ast_stopstream(chan);
140 static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
148 while (str[num] && !res) {
158 fn = "letters/exclaimation-point";
164 fn = "letters/dollar";
173 fn = "letters/equals";
179 fn = "letters/slash";
182 fn = "letters/space";
193 strcpy(fnbuf, "digits/X");
197 default: /* '9' falls here... */
199 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
200 strcpy(fnbuf, "phonetic/X_p");
204 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
205 res = ast_streamfile(chan, fn, lang);
207 if ((audiofd > -1) && (ctrlfd > -1))
208 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
210 res = ast_waitstream(chan, ints);
212 ast_stopstream(chan);
220 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
227 while (str[num] && !res) {
249 strcpy(fnbuf, "digits/X");
254 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
255 res = ast_streamfile(chan, fn, lang);
257 if ((audiofd > -1) && (ctrlfd > -1))
258 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
260 res = ast_waitstream(chan, ints);
262 ast_stopstream(chan);
270 /* Forward declarations */
271 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
272 \note Not really language codes.
273 For these language codes, Asterisk will change the syntax when
274 saying numbers (and in some cases dates and voicemail messages
278 \arg \b en - English (US)
279 \arg \b en_GB - English (British)
280 \arg \b es - Spanish, Mexican
285 \arg \b no - Norwegian
287 \arg \b pt - Portuguese
288 \arg \b pt_BR - Portuguese (Brazil)
290 \arg \b tw - Taiwanese / Chinese
292 \arg \b ge - Georgian
293 \arg \b hu - Hungarian
296 For Some languages the numbers differ for gender and plural.
297 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
298 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
299 use the option argument 'p' for plural enumerations like in German
301 Date/Time functions currently have less languages supported than saynumber().
303 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
305 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
308 Portuguese sound files needed for Time/Date functions:
319 Spanish sound files needed for Time/Date functions:
324 Italian sound files needed for Time/Date functions:
330 /* Forward declarations of language specific variants of ast_say_number_full */
331 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
332 static int ast_say_number_full_cz(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
333 static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
334 static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
335 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
336 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);
337 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);
338 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);
339 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
340 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
341 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);
342 static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
343 static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
344 static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
345 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
346 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
347 static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
348 static int ast_say_number_full_ge(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
349 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
350 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
352 /* Forward declarations of language specific variants of ast_say_enumeration_full */
353 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
354 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);
355 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);
357 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
358 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
359 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
360 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
361 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
362 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
363 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
364 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
365 static int ast_say_date_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
366 static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
367 static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
369 static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
370 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);
371 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);
372 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);
373 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);
374 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);
375 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);
376 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);
377 static int ast_say_date_with_format_pl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
378 static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
379 static int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
380 static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
381 static int ast_say_date_with_format_th(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
383 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
384 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
385 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
386 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
387 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
388 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
389 static int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
390 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
391 static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
392 static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
393 static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
395 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
396 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
397 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
398 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
399 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
400 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
401 static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
402 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
403 static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
404 static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
405 static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
407 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
408 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
409 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
410 static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
412 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
415 if ((res = ast_streamfile(chan, file, lang)))
416 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
418 res = ast_waitstream(chan, ints);
422 /*! \brief ast_say_number_full: call language-specific functions */
423 /* Called from AGI */
424 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
426 if (!strcasecmp(language,"en") ) { /* English syntax */
427 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
428 } else if (!strcasecmp(language, "cz") ) { /* Czech syntax */
429 return(ast_say_number_full_cz(chan, num, ints, language, options, audiofd, ctrlfd));
430 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
431 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
432 } else if (!strcasecmp(language, "de") ) { /* German syntax */
433 return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
434 } else if (!strcasecmp(language, "en_GB") ) { /* British syntax */
435 return(ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd));
436 } else if (!strcasecmp(language, "no") ) { /* Norwegian syntax */
437 return(ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd));
438 } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
439 return(ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd));
440 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
441 return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
442 } else if (!strcasecmp(language, "he") ) { /* Hebrew syntax */
443 return(ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
444 } else if (!strcasecmp(language, "hu") ) { /* Hungarian syntax */
445 return(ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd));
446 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
447 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
448 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
449 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
450 } else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
451 return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
452 } else if (!strcasecmp(language, "pt") || !strcasecmp(language, "pt_BR")) { /* Portuguese syntax */
453 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
454 } else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
455 return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
456 } else if (!strcasecmp(language, "tw") || !strcasecmp(language, "zh") ) { /* Taiwanese / Chinese syntax */
457 return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
458 } else if (!strcasecmp(language, "gr") ) { /* Greek syntax */
459 return(ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd));
460 } else if (!strcasecmp(language, "ru") ) { /* Russian syntax */
461 return(ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd));
462 } else if (!strcasecmp(language, "th") ) { /* Thai syntax */
463 return(ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd));
464 } else if (!strcasecmp(language, "ge") ) { /* Georgian syntax */
465 return(ast_say_number_full_ge(chan, num, ints, language, options, audiofd, ctrlfd));
468 /* Default to english */
469 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
472 /*! \brief ast_say_number_full_en: English syntax */
473 /* This is the default syntax, if no other syntax defined in this file is used */
474 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
480 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
482 while (!res && (num || playh)) {
484 ast_copy_string(fn, "digits/minus", sizeof(fn));
485 if ( num > INT_MIN ) {
491 ast_copy_string(fn, "digits/hundred", sizeof(fn));
493 } else if (num < 20) {
494 snprintf(fn, sizeof(fn), "digits/%d", num);
496 } else if (num < 100) {
497 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
501 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
505 if (num < 1000000) { /* 1,000,000 */
506 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
510 snprintf(fn, sizeof(fn), "digits/thousand");
512 if (num < 1000000000) { /* 1,000,000,000 */
513 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
517 ast_copy_string(fn, "digits/million", sizeof(fn));
519 ast_debug(1, "Number '%d' is too big for me\n", num);
526 if (!ast_streamfile(chan, fn, language)) {
527 if ((audiofd > -1) && (ctrlfd > -1))
528 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
530 res = ast_waitstream(chan, ints);
532 ast_stopstream(chan);
538 static int exp10_int(int power)
541 for (x=0;x<power;x++)
546 /*! \brief ast_say_number_full_cz: Czech syntax */
548 * 1m,2m - gender male
549 * 1w,2w - gender female
553 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
555 * for each number 10^(3n + 3) exist 3 files represented as:
556 * 1 tousand = jeden tisic = 1_E3
557 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
558 * 5,6,... tousands = pet,sest,... tisic = 5_E3
564 * tousand, milion are gender male, so 1 and 2 is 1m 2m
565 * miliard is gender female, so 1 and 2 is 1w 2w
567 static int ast_say_number_full_cz(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
577 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
582 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
584 while (!res && (num || playh)) {
586 ast_copy_string(fn, "digits/minus", sizeof(fn));
587 if ( num > INT_MIN ) {
592 } else if (num < 3 ) {
593 snprintf(fn, sizeof(fn), "digits/%d%c",num,options[0]);
596 } else if (num < 20) {
597 snprintf(fn, sizeof(fn), "digits/%d",num);
600 } else if (num < 100) {
601 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
603 } else if (num < 1000) {
604 hundered = num / 100;
605 if ( hundered == 1 ) {
606 ast_copy_string(fn, "digits/1sto", sizeof(fn));
607 } else if ( hundered == 2 ) {
608 ast_copy_string(fn, "digits/2ste", sizeof(fn));
610 res = ast_say_number_full_cz(chan,hundered,ints,language,options,audiofd,ctrlfd);
613 if (hundered == 3 || hundered == 4) {
614 ast_copy_string(fn, "digits/sta", sizeof(fn));
615 } else if ( hundered > 4 ) {
616 ast_copy_string(fn, "digits/set", sizeof(fn));
619 num -= (hundered * 100);
620 } else { /* num > 1000 */
621 length = (int)log10(num)+1;
622 while ( (length % 3 ) != 1 ) {
625 left = num / (exp10_int(length-1));
628 case 9: options = "w"; /* 1,000,000,000 gender female */
630 default : options = "m"; /* others are male */
633 if ( left > 1 ) { /* we dont say "one thousand" but only thousand */
634 res = ast_say_number_full_cz(chan,left,ints,language,options,audiofd,ctrlfd);
638 if ( left >= 5 ) { /* >= 5 have the same declesion */
639 snprintf(fn, sizeof(fn), "digits/5_E%d",length-1);
640 } else if ( left >= 2 && left <= 4 ) {
641 snprintf(fn, sizeof(fn), "digits/2-4_E%d",length-1);
642 } else { /* left == 1 */
643 snprintf(fn, sizeof(fn), "digits/1_E%d",length-1);
645 num -= left * (exp10_int(length-1));
648 if (!ast_streamfile(chan, fn, language)) {
649 if ((audiofd > -1) && (ctrlfd > -1)) {
650 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
652 res = ast_waitstream(chan, ints);
655 ast_stopstream(chan);
661 /*! \brief ast_say_number_full_da: Danish syntax */
663 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
665 static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
670 int cn = 1; /* +1 = commune; -1 = neuter */
673 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
675 if (options && !strncasecmp(options, "n",1)) cn = -1;
677 while (!res && (num || playh || playa )) {
678 /* The grammar for Danish numbers is the same as for English except
680 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
681 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
682 * "one-and twenty" and 68 is "eight-and sixty".
683 * - "million" is different in singular and plural form
684 * - numbers > 1000 with zero as the third digit from last have an
685 * "and" before the last two digits, i.e. 2034 is "two thousand and
686 * four-and thirty" and 1000012 is "one million and twelve".
689 ast_copy_string(fn, "digits/minus", sizeof(fn));
690 if ( num > INT_MIN ) {
696 ast_copy_string(fn, "digits/hundred", sizeof(fn));
699 ast_copy_string(fn, "digits/and", sizeof(fn));
701 } else if (num == 1 && cn == -1) {
702 ast_copy_string(fn, "digits/1N", sizeof(fn));
704 } else if (num < 20) {
705 snprintf(fn, sizeof(fn), "digits/%d", num);
707 } else if (num < 100) {
710 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
713 snprintf(fn, sizeof(fn), "digits/%d", num);
718 int hundreds = num / 100;
720 ast_copy_string(fn, "digits/1N", sizeof(fn));
722 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
725 num -= 100 * hundreds;
731 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
735 ast_copy_string(fn, "digits/thousand", sizeof(fn));
737 if (num < 1000000000) {
738 int millions = num / 1000000;
739 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
743 ast_copy_string(fn, "digits/million", sizeof(fn));
745 ast_copy_string(fn, "digits/millions", sizeof(fn));
748 ast_debug(1, "Number '%d' is too big for me\n", num);
752 if (num && num < 100)
757 if (!ast_streamfile(chan, fn, language)) {
758 if ((audiofd > -1) && (ctrlfd > -1))
759 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
761 res = ast_waitstream(chan, ints);
763 ast_stopstream(chan);
769 /*! \brief ast_say_number_full_de: German syntax */
771 In addition to English, the following sounds are required:
773 "1-and" through "9-and"
776 NB "1" is recorded as 'eins'
778 static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
781 int mf = 1; /* +1 = male and neuter; -1 = female */
785 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
787 if (options && (!strncasecmp(options, "f",1)))
790 while (!res && num) {
791 /* The grammar for German numbers is the same as for English except
793 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
794 * "one-and twenty" and 68 is "eight-and sixty".
795 * - "one" varies according to gender
796 * - 100 is 'hundert', however all other instances are 'ein hundert'
797 * - 1000 is 'tausend', however all other instances are 'ein tausend'
798 * - 1000000 is always 'eine million'
799 * - "million" is different in singular and plural form
802 ast_copy_string(fn, "digits/minus", sizeof(fn));
803 if ( num > INT_MIN ) {
808 } else if (num < 100 && t) {
809 ast_copy_string(fn, "digits/and", sizeof(fn));
811 } else if (num == 1 && mf == -1) {
812 snprintf(fn, sizeof(fn), "digits/%dF", num);
814 } else if (num < 20) {
815 snprintf(fn, sizeof(fn), "digits/%d", num);
817 } else if (num < 100) {
820 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
823 snprintf(fn, sizeof(fn), "digits/%d", num);
826 } else if (num == 100 && t == 0) {
827 ast_copy_string(fn, "digits/hundred", sizeof(fn));
829 } else if (num < 1000) {
830 int hundreds = num / 100;
833 ast_copy_string(fn, "digits/1N", sizeof(fn));
835 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
837 ast_copy_string(fna, "digits/hundred", sizeof(fna));
839 } else if (num == 1000 && t == 0) {
840 ast_copy_string(fn, "digits/thousand", sizeof(fn));
842 } else if (num < 1000000) {
843 int thousands = num / 1000;
846 if (thousands == 1) {
847 ast_copy_string(fn, "digits/1N", sizeof(fn));
848 ast_copy_string(fna, "digits/thousand", sizeof(fna));
850 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
853 ast_copy_string(fn, "digits/thousand", sizeof(fn));
855 } else if (num < 1000000000) {
856 int millions = num / 1000000;
860 ast_copy_string(fn, "digits/1F", sizeof(fn));
861 ast_copy_string(fna, "digits/million", sizeof(fna));
863 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
866 ast_copy_string(fn, "digits/millions", sizeof(fn));
868 } else if (num <= INT_MAX) {
869 int billions = num / 1000000000;
870 num = num % 1000000000;
873 ast_copy_string(fn, "digits/1F", sizeof(fn));
874 ast_copy_string(fna, "digits/milliard", sizeof(fna));
876 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
880 ast_copy_string(fn, "digits/milliards", sizeof(fn));
883 ast_debug(1, "Number '%d' is too big for me\n", num);
887 if (!ast_streamfile(chan, fn, language)) {
888 if ((audiofd > -1) && (ctrlfd > -1))
889 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
891 res = ast_waitstream(chan, ints);
893 ast_stopstream(chan);
895 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
896 if ((audiofd > -1) && (ctrlfd > -1))
897 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
899 res = ast_waitstream(chan, ints);
901 ast_stopstream(chan);
909 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
911 In addition to American English, the following sounds are required: "and"
913 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
920 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
922 while (!res && (num || playh || playa )) {
924 ast_copy_string(fn, "digits/minus", sizeof(fn));
925 if ( num > INT_MIN ) {
931 ast_copy_string(fn, "digits/hundred", sizeof(fn));
934 ast_copy_string(fn, "digits/and", sizeof(fn));
936 } else if (num < 20) {
937 snprintf(fn, sizeof(fn), "digits/%d", num);
939 } else if (num < 100) {
940 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
942 } else if (num < 1000) {
943 int hundreds = num / 100;
944 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
947 num -= 100 * hundreds;
950 } else if (num < 1000000) {
951 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
954 ast_copy_string(fn, "digits/thousand", sizeof(fn));
956 if (num && num < 100)
958 } else if (num < 1000000000) {
959 int millions = num / 1000000;
960 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
963 ast_copy_string(fn, "digits/million", sizeof(fn));
965 if (num && num < 100)
968 ast_debug(1, "Number '%d' is too big for me\n", num);
973 if (!ast_streamfile(chan, fn, language)) {
974 if ((audiofd > -1) && (ctrlfd > -1))
975 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
977 res = ast_waitstream(chan, ints);
979 ast_stopstream(chan);
985 /*! \brief ast_say_number_full_es: Spanish syntax */
987 Requires a few new audios:
988 1F.gsm: feminine 'una'
989 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
991 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)
995 int mf = 0; /* +1 = male; -1 = female */
998 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1001 if (!strncasecmp(options, "f",1))
1003 else if (!strncasecmp(options, "m", 1))
1007 while (!res && num) {
1009 ast_copy_string(fn, "digits/minus", sizeof(fn));
1010 if ( num > INT_MIN ) {
1016 ast_copy_string(fn, "digits/and", sizeof(fn));
1018 } else if (num == 1) {
1020 snprintf(fn, sizeof(fn), "digits/%dF", num);
1022 snprintf(fn, sizeof(fn), "digits/%dM", num);
1024 snprintf(fn, sizeof(fn), "digits/%d", num);
1026 } else if (num < 31) {
1027 snprintf(fn, sizeof(fn), "digits/%d", num);
1029 } else if (num < 100) {
1030 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1034 } else if (num == 100) {
1035 ast_copy_string(fn, "digits/100", sizeof(fn));
1037 } else if (num < 200) {
1038 ast_copy_string(fn, "digits/100-and", sizeof(fn));
1042 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1044 } else if (num < 2000) {
1046 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1048 if (num < 1000000) {
1049 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1053 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1055 if (num < 2147483640) {
1056 if ((num/1000000) == 1) {
1057 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1060 ast_copy_string(fn, "digits/million", sizeof(fn));
1062 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1065 ast_copy_string(fn, "digits/millions", sizeof(fn));
1069 ast_debug(1, "Number '%d' is too big for me\n", num);
1077 if (!ast_streamfile(chan, fn, language)) {
1078 if ((audiofd > -1) && (ctrlfd > -1))
1079 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1081 res = ast_waitstream(chan, ints);
1083 ast_stopstream(chan);
1091 /*! \brief ast_say_number_full_fr: French syntax */
1092 /* Extra sounds needed:
1095 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)
1100 int mf = 1; /* +1 = male; -1 = female */
1103 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1105 if (options && !strncasecmp(options, "f",1))
1108 while (!res && (num || playh || playa)) {
1110 ast_copy_string(fn, "digits/minus", sizeof(fn));
1111 if ( num > INT_MIN ) {
1117 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1120 ast_copy_string(fn, "digits/et", sizeof(fn));
1122 } else if (num == 1) {
1124 snprintf(fn, sizeof(fn), "digits/%dF", num);
1126 snprintf(fn, sizeof(fn), "digits/%d", num);
1128 } else if (num < 21) {
1129 snprintf(fn, sizeof(fn), "digits/%d", num);
1131 } else if (num < 70) {
1132 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1133 if ((num % 10) == 1) playa++;
1135 } else if (num < 80) {
1136 ast_copy_string(fn, "digits/60", sizeof(fn));
1137 if ((num % 10) == 1) playa++;
1139 } else if (num < 100) {
1140 ast_copy_string(fn, "digits/80", sizeof(fn));
1142 } else if (num < 200) {
1143 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1145 } else if (num < 1000) {
1146 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1149 } else if (num < 2000) {
1150 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1152 } else if (num < 1000000) {
1153 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1156 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1158 } else if (num < 1000000000) {
1159 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1162 ast_copy_string(fn, "digits/million", sizeof(fn));
1163 num = num % 1000000;
1165 ast_debug(1, "Number '%d' is too big for me\n", num);
1169 if (!ast_streamfile(chan, fn, language)) {
1170 if ((audiofd > -1) && (ctrlfd > -1))
1171 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1173 res = ast_waitstream(chan, ints);
1175 ast_stopstream(chan);
1183 /*! \brief ast_say_number_full_he: Hebrew syntax */
1184 /* Extra sounds needed:
1188 2hundred: 2 hundreds
1189 2thousands: 2 thousand
1190 thousands: plural of 'thousand'
1191 3sF 'Smichut forms (female)
1198 3s 'Smichut' forms (male)
1215 TODO: 've' should sometimed be 'hu':
1216 * before 'shtaym' (2, F)
1217 * before 'shnaym' (2, M)
1218 * before 'shlosha' (3, M)
1219 * before 'shmone' (8, M)
1220 * before 'shlosim' (30)
1221 * before 'shmonim' (80)
1227 #define SAY_NUM_BUF_SIZE 256
1228 static int ast_say_number_full_he(struct ast_channel *chan, int num,
1229 const char *ints, const char *language, const char *options,
1230 int audiofd, int ctrlfd)
1233 int state = 0; /* no need to save anything */
1234 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1235 char fn[SAY_NUM_BUF_SIZE] = "";
1236 ast_verb(3, "ast_say_digits_full: started. "
1237 "num: %d, options=\"%s\"\n",
1241 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1243 if (options && !strncasecmp(options, "f",1))
1246 /* Do we have work to do? */
1247 while (!res && (num || (state>0) )) {
1248 /* first type of work: play a second sound. In this loop
1249 * we can only play one sound file at a time. Thus playing
1250 * a second one requires repeating the loop just for the
1251 * second file. The variable 'state' remembers where we were.
1252 * state==0 is the normal mode and it means that we continue
1253 * to check if the number num has yet anything left.
1255 ast_verb(3, "ast_say_digits_full: num: %d, "
1256 "state=%d, options=\"%s\", mf=%d\n",
1257 num, state, options, mf
1260 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1262 } else if (state==2) {
1263 ast_copy_string(fn, "digits/ve", sizeof(fn));
1265 } else if (state==3) {
1266 ast_copy_string(fn, "digits/thousands", sizeof(fn));
1268 } else if (num <21) {
1270 snprintf(fn, sizeof(fn), "digits/%dF", num);
1272 snprintf(fn, sizeof(fn), "digits/%d", num);
1274 } else if (num < 100) {
1275 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1278 } else if (num < 200) {
1279 ast_copy_string(fn, "digits/1hundred", sizeof(fn));
1282 } else if (num < 300) {
1283 ast_copy_string(fn, "digits/2hundred", sizeof(fn));
1286 } else if (num < 1000) {
1287 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1290 } else if (num < 2000) {
1291 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1293 } else if (num < 3000) {
1294 ast_copy_string(fn, "digits/2thousand", sizeof(fn));
1297 } else if (num < 20000) {
1298 snprintf(fn, sizeof(fn), "digits/%ds",(num/1000));
1301 } else if (num < 1000000) {
1302 res = ast_say_number_full_he(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1305 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1307 } else if (num < 1000000000) {
1308 res = ast_say_number_full_he(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1311 ast_copy_string(fn, "digits/million", sizeof(fn));
1312 num = num % 1000000;
1314 ast_debug(1, "Number '%d' is too big for me\n", num);
1318 if (!ast_streamfile(chan, fn, language)) {
1319 if ((audiofd > -1) && (ctrlfd > -1))
1320 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1322 res = ast_waitstream(chan, ints);
1324 ast_stopstream(chan);
1330 /*! \brief ast_say_number_full_hu: Hungarian syntax */
1331 /* Extra sounds need:
1335 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1341 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1345 like english, except numbers up to 29 are from 2 words.
1346 10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
1349 while(!res && (num || playh)) {
1351 ast_copy_string(fn, "digits/minus", sizeof(fn));
1352 if ( num > INT_MIN ) {
1358 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1360 } else if (num < 11 || num == 20) {
1361 snprintf(fn, sizeof(fn), "digits/%d", num);
1363 } else if (num < 20) {
1364 ast_copy_string(fn, "digits/10en", sizeof(fn));
1366 } else if (num < 30) {
1367 ast_copy_string(fn, "digits/20on", sizeof(fn));
1369 } else if (num < 100) {
1370 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1374 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1378 if (num < 1000000) { /* 1,000,000 */
1379 res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
1383 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1385 if (num < 1000000000) { /* 1,000,000,000 */
1386 res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1390 ast_copy_string(fn, "digits/million", sizeof(fn));
1392 ast_debug(1, "Number '%d' is too big for me\n", num);
1399 if(!ast_streamfile(chan, fn, language)) {
1400 if ((audiofd > -1) && (ctrlfd > -1))
1401 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1403 res = ast_waitstream(chan, ints);
1405 ast_stopstream(chan);
1411 /*! \brief ast_say_number_full_it: Italian */
1412 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1420 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1425 Like english, numbers up to 20 are a single 'word', and others
1426 compound, but with exceptions.
1427 For example 21 is not twenty-one, but there is a single word in 'it'.
1428 Idem for 28 (ie when a the 2nd part of a compund number
1429 starts with a vowel)
1431 There are exceptions also for hundred, thousand and million.
1432 In english 100 = one hundred, 200 is two hundred.
1433 In italian 100 = cento , like to say hundred (without one),
1434 200 and more are like english.
1436 Same applies for thousand:
1437 1000 is one thousand in en, 2000 is two thousand.
1438 In it we have 1000 = mille , 2000 = 2 mila
1440 For million(s) we use the plural, if more than one
1441 Also, one million is abbreviated in it, like on-million,
1442 or 'un milione', not 'uno milione'.
1443 So the right file is provided.
1446 while (!res && (num || playh)) {
1448 ast_copy_string(fn, "digits/minus", sizeof(fn));
1449 if ( num > INT_MIN ) {
1455 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1457 } else if (num < 20) {
1458 snprintf(fn, sizeof(fn), "digits/%d", num);
1460 } else if (num == 21) {
1461 snprintf(fn, sizeof(fn), "digits/%d", num);
1463 } else if (num == 28) {
1464 snprintf(fn, sizeof(fn), "digits/%d", num);
1466 } else if (num == 31) {
1467 snprintf(fn, sizeof(fn), "digits/%d", num);
1469 } else if (num == 38) {
1470 snprintf(fn, sizeof(fn), "digits/%d", num);
1472 } else if (num == 41) {
1473 snprintf(fn, sizeof(fn), "digits/%d", num);
1475 } else if (num == 48) {
1476 snprintf(fn, sizeof(fn), "digits/%d", num);
1478 } else if (num == 51) {
1479 snprintf(fn, sizeof(fn), "digits/%d", num);
1481 } else if (num == 58) {
1482 snprintf(fn, sizeof(fn), "digits/%d", num);
1484 } else if (num == 61) {
1485 snprintf(fn, sizeof(fn), "digits/%d", num);
1487 } else if (num == 68) {
1488 snprintf(fn, sizeof(fn), "digits/%d", num);
1490 } else if (num == 71) {
1491 snprintf(fn, sizeof(fn), "digits/%d", num);
1493 } else if (num == 78) {
1494 snprintf(fn, sizeof(fn), "digits/%d", num);
1496 } else if (num == 81) {
1497 snprintf(fn, sizeof(fn), "digits/%d", num);
1499 } else if (num == 88) {
1500 snprintf(fn, sizeof(fn), "digits/%d", num);
1502 } else if (num == 91) {
1503 snprintf(fn, sizeof(fn), "digits/%d", num);
1505 } else if (num == 98) {
1506 snprintf(fn, sizeof(fn), "digits/%d", num);
1508 } else if (num < 100) {
1509 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1513 if ((num / 100) > 1) {
1514 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1517 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1521 if (num < 1000000) { /* 1,000,000 */
1523 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1528 if ((tempnum / 1000) < 2)
1529 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1530 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1531 ast_copy_string(fn, "digits/thousands", sizeof(fn));
1533 if (num < 1000000000) { /* 1,000,000,000 */
1534 if ((num / 1000000) > 1)
1535 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1540 if ((tempnum / 1000000) < 2)
1541 ast_copy_string(fn, "digits/million", sizeof(fn));
1543 ast_copy_string(fn, "digits/millions", sizeof(fn));
1545 ast_debug(1, "Number '%d' is too big for me\n", num);
1552 if (!ast_streamfile(chan, fn, language)) {
1553 if ((audiofd > -1) && (ctrlfd > -1))
1554 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1556 res = ast_waitstream(chan, ints);
1558 ast_stopstream(chan);
1564 /*! \brief ast_say_number_full_nl: dutch syntax */
1565 /* New files: digits/nl-en
1567 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1574 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1575 while (!res && (num || playh )) {
1577 ast_copy_string(fn, "digits/minus", sizeof(fn));
1578 if ( num > INT_MIN ) {
1584 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1586 } else if (num < 20) {
1587 snprintf(fn, sizeof(fn), "digits/%d", num);
1589 } else if (num < 100) {
1592 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1596 ast_copy_string(fn, "digits/nl-en", sizeof(fn));
1598 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1601 } else if (num < 200) {
1602 /* hundred, not one-hundred */
1603 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1605 } else if (num < 1000) {
1606 snprintf(fn, sizeof(fn), "digits/%d", num / 100);
1611 /* thousand, not one-thousand */
1613 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1614 } else if (num < 10000) { /* 1,100 to 9,9999 */
1615 res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
1619 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1621 if (num < 1000000) { /* 1,000,000 */
1622 res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
1626 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1628 if (num < 1000000000) { /* 1,000,000,000 */
1629 res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1633 ast_copy_string(fn, "digits/million", sizeof(fn));
1635 ast_debug(1, "Number '%d' is too big for me\n", num);
1643 if (!ast_streamfile(chan, fn, language)) {
1644 if ((audiofd > -1) && (ctrlfd > -1))
1645 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1647 res = ast_waitstream(chan, ints);
1649 ast_stopstream(chan);
1655 /*! \brief ast_say_number_full_no: Norwegian syntax */
1657 In addition to American English, the following sounds are required: "and", "1N"
1659 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)
1664 int cn = 1; /* +1 = commune; -1 = neuter */
1668 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1670 if (options && !strncasecmp(options, "n",1)) cn = -1;
1672 while (!res && (num || playh || playa )) {
1673 /* The grammar for Norwegian numbers is the same as for English except
1674 * for the following:
1675 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1676 * "and" before the last two digits, i.e. 2034 is "two thousand and
1677 * thirty-four" and 1000012 is "one million and twelve".
1680 ast_copy_string(fn, "digits/minus", sizeof(fn));
1681 if ( num > INT_MIN ) {
1687 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1690 ast_copy_string(fn, "digits/and", sizeof(fn));
1692 } else if (num == 1 && cn == -1) {
1693 ast_copy_string(fn, "digits/1N", sizeof(fn));
1695 } else if (num < 20) {
1696 snprintf(fn, sizeof(fn), "digits/%d", num);
1698 } else if (num < 100) {
1699 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1701 } else if (num < 1000) {
1702 int hundreds = num / 100;
1704 ast_copy_string(fn, "digits/1N", sizeof(fn));
1706 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1709 num -= 100 * hundreds;
1712 } else if (num < 1000000) {
1713 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1716 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1718 if (num && num < 100)
1720 } else if (num < 1000000000) {
1721 int millions = num / 1000000;
1722 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1725 ast_copy_string(fn, "digits/million", sizeof(fn));
1727 if (num && num < 100)
1730 ast_debug(1, "Number '%d' is too big for me\n", num);
1735 if (!ast_streamfile(chan, fn, language)) {
1736 if ((audiofd > -1) && (ctrlfd > -1))
1737 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1739 res = ast_waitstream(chan, ints);
1741 ast_stopstream(chan);
1748 char *separator_dziesiatek;
1752 char *dziesiatki[10];
1757 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1763 return odm->rzedy[rzad - 1][0];
1764 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1765 return odm->rzedy[rzad - 1][1];
1767 return odm->rzedy[rzad - 1][2];
1770 static char* pl_append(char* buffer, char* str)
1772 strcpy(buffer, str);
1773 buffer += strlen(str);
1777 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1779 char file_name[255] = "digits/";
1780 strcat(file_name, fn);
1781 ast_debug(1, "Trying to play: %s\n", file_name);
1782 if (!ast_streamfile(chan, file_name, language)) {
1783 if ((audiofd > -1) && (ctrlfd > -1))
1784 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1786 ast_waitstream(chan, ints);
1788 ast_stopstream(chan);
1791 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1793 /* Initialise variables to allow compilation on Debian-stable, etc */
1803 if (i == 0 && rzad > 0) {
1807 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1811 m1000E6 = i % 1000000000;
1812 i1000E6 = i / 1000000000;
1814 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1816 m1000E3 = m1000E6 % 1000000;
1817 i1000E3 = m1000E6 / 1000000;
1819 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1821 m1000 = m1000E3 % 1000;
1822 i1000 = m1000E3 / 1000;
1824 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1830 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1832 if ( m100 > 0 && m100 <=9 ) {
1834 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1836 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1837 } else if (m100 % 10 == 0) {
1838 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1839 } else if (m100 <= 19 ) {
1840 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1841 } else if (m100 != 0) {
1842 if (odm->separator_dziesiatek[0]==' ') {
1843 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1844 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1848 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1849 b = pl_append(b, odm->separator_dziesiatek);
1850 b = pl_append(b, odm->cyfry2[m100 % 10]);
1851 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1856 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1860 /* ast_say_number_full_pl: Polish syntax */
1861 static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
1871 1000000000.2 miliardy
1872 1000000000.5 miliardow
1936 70m siedemdziesieciu
1948 90m dziewiedziesieciu
1950 and combinations of eg.: 20_1, 30m_3m, etc...
1954 char *zenski_cyfry[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1956 char *zenski_cyfry2[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1958 char *meski_cyfry[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1960 char *meski_cyfry2[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1962 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1964 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1966 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1968 char *nijaki_cyfry[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1970 char *nijaki_cyfry2[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1972 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1974 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1976 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1978 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1980 /* Initialise variables to allow compilation on Debian-stable, etc */
1983 static odmiana *odmiana_nieosobowa = NULL;
1984 static odmiana *odmiana_meska = NULL;
1985 static odmiana *odmiana_zenska = NULL;
1987 if (odmiana_nieosobowa == NULL) {
1988 odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
1990 odmiana_nieosobowa->separator_dziesiatek = " ";
1992 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
1993 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
1994 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
1995 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
1996 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
1997 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
2000 if (odmiana_zenska == NULL) {
2001 odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
2003 odmiana_zenska->separator_dziesiatek = " ";
2005 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
2006 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
2007 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
2008 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
2009 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
2010 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
2013 if (odmiana_meska == NULL) {
2014 odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
2016 odmiana_meska->separator_dziesiatek = " ";
2018 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
2019 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
2020 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
2021 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
2022 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
2023 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
2027 if (strncasecmp(options, "f", 1) == 0)
2029 else if (strncasecmp(options, "m", 1) == 0)
2032 o = odmiana_nieosobowa;
2034 o = odmiana_nieosobowa;
2036 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
2040 /* ast_say_number_full_pt: Portuguese syntax */
2041 /* Extra sounds needed: */
2042 /* For feminin all sound files end with F */
2043 /* 100E for 100+ something */
2044 /* 1000000S for plural */
2045 /* pt-e for 'and' */
2046 static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2050 int mf = 1; /* +1 = male; -1 = female */
2054 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2056 if (options && !strncasecmp(options, "f",1))
2059 while (!res && num ) {
2061 ast_copy_string(fn, "digits/minus", sizeof(fn));
2062 if ( num > INT_MIN ) {
2067 } else if (num < 20) {
2068 if ((num == 1 || num == 2) && (mf < 0))
2069 snprintf(fn, sizeof(fn), "digits/%dF", num);
2071 snprintf(fn, sizeof(fn), "digits/%d", num);
2073 } else if (num < 100) {
2074 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2078 } else if (num < 1000) {
2080 ast_copy_string(fn, "digits/100", sizeof(fn));
2082 ast_copy_string(fn, "digits/100E", sizeof(fn));
2084 if (mf < 0 && num > 199)
2085 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2087 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2092 } else if (num < 1000000) {
2094 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2098 ast_copy_string(fn, "digits/1000", sizeof(fn));
2099 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2102 } else if (num < 1000000000) {
2103 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2107 ast_copy_string(fn, "digits/1000000", sizeof(fn));
2109 ast_copy_string(fn, "digits/1000000S", sizeof(fn));
2111 if ((num % 1000000) &&
2113 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2114 /* no hundreds and below */
2115 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2117 num = num % 1000000;
2119 /* number is too big */
2120 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2124 if (!ast_streamfile(chan, fn, language)) {
2125 if ((audiofd > -1) && (ctrlfd > -1))
2126 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2128 res = ast_waitstream(chan, ints);
2130 ast_stopstream(chan);
2132 if (!res && playh) {
2133 res = wait_file(chan, ints, "digits/pt-e", language);
2134 ast_stopstream(chan);
2141 /*! \brief ast_say_number_full_se: Swedish syntax */
2142 static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2147 int cn = 1; /* +1 = commune; -1 = neuter */
2149 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2150 if (options && !strncasecmp(options, "n",1)) cn = -1;
2152 while (!res && (num || playh)) {
2154 ast_copy_string(fn, "digits/minus", sizeof(fn));
2155 if ( num > INT_MIN ) {
2161 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2163 } else if (num < 20) {
2164 snprintf(fn, sizeof(fn), "digits/%d", num);
2166 } else if (num < 100) {
2167 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2169 } else if (num == 1 && cn == -1) { /* En eller ett? */
2170 ast_copy_string(fn, "digits/1N", sizeof(fn));
2174 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2178 if (num < 1000000) { /* 1,000,000 */
2179 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
2184 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2186 if (num < 1000000000) { /* 1,000,000,000 */
2187 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
2192 ast_copy_string(fn, "digits/million", sizeof(fn));
2194 ast_debug(1, "Number '%d' is too big for me\n", num);
2201 if (!ast_streamfile(chan, fn, language)) {
2202 if ((audiofd > -1) && (ctrlfd > -1))
2203 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2205 res = ast_waitstream(chan, ints);
2206 ast_stopstream(chan);
2213 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2214 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2220 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2222 while (!res && (num || playh)) {
2224 ast_copy_string(fn, "digits/minus", sizeof(fn));
2225 if ( num > INT_MIN ) {
2231 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2233 } else if (num < 10) {
2234 snprintf(fn, sizeof(fn), "digits/%d", num);
2236 } else if (num < 100) {
2237 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2241 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2245 if (num < 1000000) { /* 1,000,000 */
2246 res = ast_say_number_full_tw(chan, num / 1000, ints, language, audiofd, ctrlfd);
2250 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2252 if (num < 1000000000) { /* 1,000,000,000 */
2253 res = ast_say_number_full_tw(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2257 ast_copy_string(fn, "digits/million", sizeof(fn));
2259 ast_debug(1, "Number '%d' is too big for me\n", num);
2266 if (!ast_streamfile(chan, fn, language)) {
2267 if ((audiofd > -1) && (ctrlfd > -1))
2268 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2270 res = ast_waitstream(chan, ints);
2272 ast_stopstream(chan);
2279 /*! \brief determine last digits for thousands/millions (ru) */
2280 static int get_lastdigits_ru(int num) {
2283 } else if (num < 100) {
2284 return get_lastdigits_ru(num % 10);
2285 } else if (num < 1000) {
2286 return get_lastdigits_ru(num % 100);
2288 return 0; /* number too big */
2292 /*! \brief ast_say_number_full_ru: Russian syntax */
2293 /*! \brief additional files:
2294 n00.gsm (one hundred, two hundred, ...)
2297 thousands-i.gsm (tisyachi)
2298 million-a.gsm (milliona)
2304 where 'n' from 1 to 9
2306 static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2312 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2314 while (!res && (num)) {
2316 ast_copy_string(fn, "digits/minus", sizeof(fn));
2317 if ( num > INT_MIN ) {
2322 } else if (num < 20) {
2323 if (options && strlen(options) == 1 && num < 3) {
2324 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2326 snprintf(fn, sizeof(fn), "digits/%d", num);
2329 } else if (num < 100) {
2330 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2332 } else if (num < 1000){
2333 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2335 } else if (num < 1000000) { /* 1,000,000 */
2336 lastdigits = get_lastdigits_ru(num / 1000);
2338 if (lastdigits < 3) {
2339 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2341 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2345 if (lastdigits == 1) {
2346 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2347 } else if (lastdigits > 1 && lastdigits < 5) {
2348 ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
2350 ast_copy_string(fn, "digits/thousands", sizeof(fn));
2353 } else if (num < 1000000000) { /* 1,000,000,000 */
2354 lastdigits = get_lastdigits_ru(num / 1000000);
2356 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2359 if (lastdigits == 1) {
2360 ast_copy_string(fn, "digits/million", sizeof(fn));
2361 } else if (lastdigits > 1 && lastdigits < 5) {
2362 ast_copy_string(fn, "digits/million-a", sizeof(fn));
2364 ast_copy_string(fn, "digits/millions", sizeof(fn));
2368 ast_debug(1, "Number '%d' is too big for me\n", num);
2372 if (!ast_streamfile(chan, fn, language)) {
2373 if ((audiofd > -1) && (ctrlfd > -1))
2374 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2376 res = ast_waitstream(chan, ints);
2378 ast_stopstream(chan);
2384 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2390 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2392 while(!res && (num || playh)) {
2394 ast_copy_string(fn, "digits/lop", sizeof(fn));
2395 if ( num > INT_MIN ) {
2401 ast_copy_string(fn, "digits/roi", sizeof(fn));
2403 } else if (num < 100) {
2404 if ((num <= 20) || ((num % 10) == 1)) {
2405 snprintf(fn, sizeof(fn), "digits/%d", num);
2408 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2411 } else if (num < 1000) {
2412 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2415 } else if (num < 10000) { /* 10,000 */
2416 res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
2420 ast_copy_string(fn, "digits/pan", sizeof(fn));
2421 } else if (num < 100000) { /* 100,000 */
2422 res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
2426 ast_copy_string(fn, "digits/muan", sizeof(fn));
2427 } else if (num < 1000000) { /* 1,000,000 */
2428 res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
2432 ast_copy_string(fn, "digits/san", sizeof(fn));
2434 res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2438 ast_copy_string(fn, "digits/larn", sizeof(fn));
2441 if(!ast_streamfile(chan, fn, language)) {
2442 if ((audiofd > -1) && (ctrlfd > -1))
2443 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2445 res = ast_waitstream(chan, ints);
2447 ast_stopstream(chan);
2453 /*! \brief ast_say_enumeration_full: call language-specific functions */
2454 /* Called from AGI */
2455 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2457 if (!strcasecmp(language,"en") ) { /* English syntax */
2458 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2459 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
2460 return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
2461 } else if (!strcasecmp(language, "de") ) { /* German syntax */
2462 return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
2465 /* Default to english */
2466 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2469 /*! \brief ast_say_enumeration_full_en: English syntax */
2470 /* This is the default syntax, if no other syntax defined in this file is used */
2471 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2476 while (!res && num) {
2478 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
2479 if ( num > INT_MIN ) {
2484 } else if (num < 20) {
2485 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2487 } else if (num < 100) {
2488 int tens = num / 10;
2491 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2493 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2495 } else if (num < 1000) {
2496 int hundreds = num / 100;
2498 if (hundreds > 1 || t == 1) {
2499 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2504 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2506 ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
2508 } else if (num < 1000000) {
2509 int thousands = num / 1000;
2511 if (thousands > 1 || t == 1) {
2512 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2517 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2519 ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
2522 } else if (num < 1000000000) {
2523 int millions = num / 1000000;
2524 num = num % 1000000;
2526 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2530 ast_copy_string(fn, "digits/million", sizeof(fn));
2532 ast_copy_string(fn, "digits/h-million", sizeof(fn));
2534 } else if (num < INT_MAX) {
2535 int billions = num / 1000000000;
2536 num = num % 1000000000;
2538 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2542 ast_copy_string(fn, "digits/billion", sizeof(fn));
2544 ast_copy_string(fn, "digits/h-billion", sizeof(fn));
2546 } else if (num == INT_MAX) {
2547 ast_copy_string(fn, "digits/h-last", sizeof(fn));
2550 ast_debug(1, "Number '%d' is too big for me\n", num);
2555 if (!ast_streamfile(chan, fn, language)) {
2556 if ((audiofd > -1) && (ctrlfd > -1)) {
2557 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2559 res = ast_waitstream(chan, ints);
2562 ast_stopstream(chan);
2568 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2569 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)
2571 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2573 char fn[256] = "", fna[256] = "";
2576 if (options && !strncasecmp(options, "f",1)) {
2578 } else if (options && !strncasecmp(options, "n",1)) {
2585 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2587 while (!res && num) {
2589 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
2590 if ( num > INT_MIN ) {
2595 } else if (num < 100 && t) {
2596 ast_copy_string(fn, "digits/and", sizeof(fn));
2598 } else if (num < 20) {
2599 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2601 } else if (num < 100) {
2602 int ones = num % 10;
2604 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2607 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2610 } else if (num == 100 && t == 0) {
2611 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2613 } else if (num < 1000) {
2614 int hundreds = num / 100;
2616 if (hundreds == 1) {
2617 ast_copy_string(fn, "digits/1N", sizeof(fn));
2619 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2622 ast_copy_string(fna, "digits/hundred", sizeof(fna));
2624 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2627 } else if (num < 1000000) {
2628 int thousands = num / 1000;
2630 if (thousands == 1) {
2632 ast_copy_string(fn, "digits/1N", sizeof(fn));
2633 ast_copy_string(fna, "digits/thousand", sizeof(fna));
2636 ast_copy_string(fn, "digits/1N", sizeof(fn));
2637 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2639 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2643 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2648 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2650 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2654 } else if (num < 1000000000) {
2655 int millions = num / 1000000;
2656 num = num % 1000000;
2657 if (millions == 1) {
2659 ast_copy_string(fn, "digits/1F", sizeof(fn));
2660 ast_copy_string(fna, "digits/million", sizeof(fna));
2662 ast_copy_string(fn, "digits/1N", sizeof(fn));
2663 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2666 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2671 ast_copy_string(fn, "digits/millions", sizeof(fn));
2673 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2677 } else if (num < INT_MAX) {
2678 int billions = num / 1000000000;
2679 num = num % 1000000000;
2680 if (billions == 1) {
2682 ast_copy_string(fn, "digits/1F", sizeof(fn));
2683 ast_copy_string(fna, "digits/milliard", sizeof(fna));
2685 ast_copy_string(fn, "digits/1N", sizeof(fn));
2686 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2689 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2693 ast_copy_string(fn, "digits/milliards", sizeof(fna));
2695 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2699 } else if (num == INT_MAX) {
2700 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2703 ast_debug(1, "Number '%d' is too big for me\n", num);
2708 if (!ast_streamfile(chan, fn, language)) {
2709 if ((audiofd > -1) && (ctrlfd > -1))
2710 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2712 res = ast_waitstream(chan, ints);
2714 ast_stopstream(chan);
2716 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2717 if ((audiofd > -1) && (ctrlfd > -1)) {
2718 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2720 res = ast_waitstream(chan, ints);
2723 ast_stopstream(chan);
2731 /*! \brief ast_say_enumeration_full_de: German syntax */
2732 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)
2734 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2736 char fn[256] = "", fna[256] = "";
2739 if (options && !strncasecmp(options, "f",1)) {
2741 } else if (options && !strncasecmp(options, "n",1)) {
2748 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2750 while (!res && num) {
2752 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
2753 if ( num > INT_MIN ) {
2758 } else if (num < 100 && t) {
2759 ast_copy_string(fn, "digits/and", sizeof(fn));
2761 } else if (num < 20) {
2762 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2764 } else if (num < 100) {
2765 int ones = num % 10;
2767 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2770 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2773 } else if (num == 100 && t == 0) {
2774 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2776 } else if (num < 1000) {
2777 int hundreds = num / 100;
2779 if (hundreds == 1) {
2780 ast_copy_string(fn, "digits/1N", sizeof(fn));
2782 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2785 ast_copy_string(fna, "digits/hundred", sizeof(fna));
2787 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2790 } else if (num < 1000000) {
2791 int thousands = num / 1000;
2793 if (thousands == 1) {
2795 ast_copy_string(fn, "digits/1N", sizeof(fn));
2796 ast_copy_string(fna, "digits/thousand", sizeof(fna));
2799 ast_copy_string(fn, "digits/1N", sizeof(fn));
2800 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2802 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2806 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2811 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2813 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2817 } else if (num < 1000000000) {
2818 int millions = num / 1000000;
2819 num = num % 1000000;
2820 if (millions == 1) {
2822 ast_copy_string(fn, "digits/1F", sizeof(fn));
2823 ast_copy_string(fna, "digits/million", sizeof(fna));
2825 ast_copy_string(fn, "digits/1N", sizeof(fn));
2826 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2829 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2834 ast_copy_string(fn, "digits/millions", sizeof(fn));
2836 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2840 } else if (num < INT_MAX) {
2841 int billions = num / 1000000000;
2842 num = num % 1000000000;
2843 if (billions == 1) {
2845 ast_copy_string(fn, "digits/1F", sizeof(fn));
2846 ast_copy_string(fna, "digits/milliard", sizeof(fna));
2848 ast_copy_string(fn, "digits/1N", sizeof(fn));
2849 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2852 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2856 ast_copy_string(fn, "digits/milliards", sizeof(fna));
2858 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2862 } else if (num == INT_MAX) {
2863 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2866 ast_debug(1, "Number '%d' is too big for me\n", num);
2871 if (!ast_streamfile(chan, fn, language)) {
2872 if ((audiofd > -1) && (ctrlfd > -1))
2873 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2875 res = ast_waitstream(chan, ints);
2877 ast_stopstream(chan);
2879 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2880 if ((audiofd > -1) && (ctrlfd > -1)) {
2881 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2883 res = ast_waitstream(chan, ints);
2886 ast_stopstream(chan);
2894 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2896 if (!strcasecmp(lang, "en") ) { /* English syntax */
2897 return(ast_say_date_en(chan, t, ints, lang));
2898 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2899 return(ast_say_date_da(chan, t, ints, lang));
2900 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2901 return(ast_say_date_de(chan, t, ints, lang));
2902 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2903 return(ast_say_date_fr(chan, t, ints, lang));
2904 } else if (!strcasecmp(lang, "hu") ) { /* Hungarian syntax */
2905 return(ast_say_date_hu(chan, t, ints, lang));
2906 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2907 return(ast_say_date_nl(chan, t, ints, lang));
2908 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
2909 return(ast_say_date_pt(chan, t, ints, lang));
2910 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2911 return(ast_say_date_gr(chan, t, ints, lang));
2912 } else if (!strcasecmp(lang, "th") ) { /* Thai syntax */
2913 return(ast_say_date_th(chan, t, ints, lang));
2914 } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
2915 return(ast_say_date_ge(chan, t, ints, lang));
2918 /* Default to English */
2919 return(ast_say_date_en(chan, t, ints, lang));
2922 /* English syntax */
2923 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2926 struct timeval tv = { t, 0 };
2929 ast_localtime(&tv, &tm, NULL);
2931 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2932 res = ast_streamfile(chan, fn, lang);
2934 res = ast_waitstream(chan, ints);
2937 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2938 res = ast_streamfile(chan, fn, lang);
2940 res = ast_waitstream(chan, ints);
2943 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2945 res = ast_waitstream(chan, ints);
2947 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2952 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2954 struct timeval tv = { t, 0 };
2958 ast_localtime(&tv, &tm, NULL);
2960 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2961 res = ast_streamfile(chan, fn, lang);
2963 res = ast_waitstream(chan, ints);
2966 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2968 res = ast_waitstream(chan, ints);
2970 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2971 res = ast_streamfile(chan, fn, lang);
2973 res = ast_waitstream(chan, ints);
2977 int year = tm.tm_year + 1900;
2978 if (year > 1999) { /* year 2000 and later */
2979 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2982 /* I'm not going to handle 1100 and prior */
2983 /* We'll just be silent on the year, instead of bombing out. */
2985 /* year 1100 to 1999. will anybody need this?!? */
2986 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2987 res = wait_file(chan, ints, fn, lang);
2989 res = wait_file(chan,ints, "digits/hundred", lang);
2990 if (!res && year % 100 != 0) {
2991 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3001 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3003 struct timeval tv = { t, 0 };
3007 ast_localtime(&tv, &tm, NULL);
3009 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3010 res = ast_streamfile(chan, fn, lang);
3012 res = ast_waitstream(chan, ints);
3015 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3017 res = ast_waitstream(chan, ints);
3019 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3020 res = ast_streamfile(chan, fn, lang);
3022 res = ast_waitstream(chan, ints);
3026 int year = tm.tm_year + 1900;
3027 if (year > 1999) { /* year 2000 and later */
3028 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
3031 /* I'm not going to handle 1100 and prior */
3032 /* We'll just be silent on the year, instead of bombing out. */
3034 /* year 1100 to 1999. will anybody need this?!? */
3035 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3036 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
3037 res = wait_file(chan, ints, fn, lang);
3039 res = wait_file(chan,ints, "digits/hundred", lang);
3040 if (!res && year % 100 != 0) {
3041 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
3050 /* Hungarian syntax */
3051 int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3053 struct timeval tv = { t, 0 };
3057 ast_localtime(&tv, &tm, NULL);
3060 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3062 res = ast_waitstream(chan, ints);
3064 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3065 res = ast_streamfile(chan, fn, lang);
3067 res = ast_waitstream(chan, ints);
3070 ast_say_number(chan, tm.tm_mday , ints, lang, (char *) NULL);
3072 res = ast_waitstream(chan, ints);
3074 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3075 res = ast_streamfile(chan, fn, lang);
3077 res = ast_waitstream(chan, ints);
3083 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3085 struct timeval tv = { t, 0 };
3089 ast_localtime(&tv, &tm, NULL);
3091 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3092 res = ast_streamfile(chan, fn, lang);
3094 res = ast_waitstream(chan, ints);
3097 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3099 res = ast_waitstream(chan, ints);
3101 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3102 res = ast_streamfile(chan, fn, lang);
3104 res = ast_waitstream(chan, ints);
3107 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3112 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
3114 struct timeval tv = { t, 0 };
3118 ast_localtime(&tv, &tm, NULL);
3120 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
3121 res = ast_streamfile(chan, fn, lang);
3123 res = ast_waitstream(chan, ints);
3126 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
3128 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
3129 res = ast_streamfile(chan, fn, lang);
3131 res = ast_waitstream(chan, ints);
3134 res = ast_waitstream(chan, ints);
3136 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3141 int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)