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>
30 #include <sys/types.h>
33 #include <netinet/in.h>
40 #include <iso/limits_iso.h>
45 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
47 #include "asterisk/file.h"
48 #include "asterisk/channel.h"
49 #include "asterisk/logger.h"
50 #include "asterisk/options.h"
51 #include "asterisk/say.h"
52 #include "asterisk/lock.h"
53 #include "asterisk/localtime.h"
54 #include "asterisk/utils.h"
56 /* Forward declaration */
57 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
60 static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
78 fn = "letters/exclaimation-point";
84 fn = "letters/dollar";
93 fn = "letters/equals";
102 fn = "letters/space";
114 strcpy(fnbuf, "digits/X");
120 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
121 strcpy(fnbuf, "letters/X");
125 res = ast_streamfile(chan, fn, lang);
127 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
128 ast_stopstream(chan);
135 static int say_character_str(struct ast_channel *chan, const char *str, const char *ints, const char *lang)
137 return ast_say_character_str_full(chan, str, ints, lang, -1, -1);
140 static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
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 res = ast_streamfile(chan, fn, lang);
206 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
207 ast_stopstream(chan);
214 static int say_phonetic_str(struct ast_channel *chan, const char *str, const char *ints, const char *lang)
216 return ast_say_phonetic_str_full(chan, str, ints, lang, -1, -1);
219 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
226 while (str[num] && !res) {
248 strcpy(fnbuf, "digits/X");
254 res = ast_streamfile(chan, fn, lang);
256 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
257 ast_stopstream(chan);
265 static int say_digit_str(struct ast_channel *chan, const char *str, const char *ints, const char *lang)
267 return ast_say_digit_str_full(chan, str, ints, lang, -1, -1);
270 static int say_digits_full(struct ast_channel *chan, int num, const char *ints, const char *lang, int audiofd, int ctrlfd)
274 snprintf(fn2, sizeof(fn2), "%d", num);
275 return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd);
278 static int say_digits(struct ast_channel *chan, int num, const char *ints, const char *lang)
280 return ast_say_digits_full(chan, num, ints, lang, -1, -1);
283 /* Forward declarations */
284 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
285 \note Not really language codes.
286 For these language codes, Asterisk will change the syntax when
287 saying numbers (and in some cases dates and voicemail messages
291 \arg \b en - English (US)
292 \arg \b en_GB - English (British)
293 \arg \b es - Spanish, Mexican
298 \arg \b no - Norwegian
300 \arg \b pt - Portuguese
302 \arg \b tw - Taiwanese / Chinese
306 For Some languages the numbers differ for gender and plural.
307 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
308 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
309 use the option argument 'p' for plural enumerations like in German
311 Date/Time functions currently have less languages supported than saynumber().
313 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
315 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
318 Portuguese sound files needed for Time/Date functions:
329 Spanish sound files needed for Time/Date functions:
334 Italian sound files needed for Time/Date functions:
340 /* Forward declarations of language specific variants of ast_say_number_full */
341 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
342 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);
343 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);
344 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);
345 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
346 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);
347 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);
348 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);
349 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
350 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
351 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);
352 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);
353 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);
354 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);
355 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
356 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
357 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);
359 /* Forward declarations of language specific variants of ast_say_enumeration_full */
360 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
361 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);
362 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);
364 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
365 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
366 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
367 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
368 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
369 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
370 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
371 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
373 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);
374 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);
375 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);
376 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);
377 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);
378 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);
379 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);
380 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);
381 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);
382 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);
383 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);
385 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
386 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
387 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
388 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
389 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
390 static int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
391 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
393 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
394 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
395 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
396 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
397 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
398 static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
399 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
401 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
402 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
403 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
405 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
408 if ((res = ast_streamfile(chan, file, lang)))
409 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
411 res = ast_waitstream(chan, ints);
415 /*! \brief ast_say_number_full: call language-specific functions */
416 /* Called from AGI */
417 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
419 if (!strcasecmp(language,"en") ) { /* English syntax */
420 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
421 } else if (!strcasecmp(language, "cz") ) { /* Czech syntax */
422 return(ast_say_number_full_cz(chan, num, ints, language, options, audiofd, ctrlfd));
423 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
424 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
425 } else if (!strcasecmp(language, "de") ) { /* German syntax */
426 return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
427 } else if (!strcasecmp(language, "en_GB") ) { /* British syntax */
428 return(ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd));
429 } else if (!strcasecmp(language, "no") ) { /* Norwegian syntax */
430 return(ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd));
431 } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
432 return(ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd));
433 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
434 return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
435 } else if (!strcasecmp(language, "he") ) { /* Hebrew syntax */
436 return(ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
437 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
438 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
439 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
440 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
441 } else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
442 return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
443 } else if (!strcasecmp(language, "pt") ) { /* Portuguese syntax */
444 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
445 } else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
446 return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
447 } else if (!strcasecmp(language, "tw") || !strcasecmp(language, "zh") ) { /* Taiwanese / Chinese syntax */
448 return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
449 } else if (!strcasecmp(language, "gr") ) { /* Greek syntax */
450 return(ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd));
451 } else if (!strcasecmp(language, "ru") ) { /* Russian syntax */
452 return(ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd));
455 /* Default to english */
456 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
459 /*! \brief ast_say_number: call language-specific functions without file descriptors */
460 static int say_number(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options)
462 return(ast_say_number_full(chan, num, ints, language, options, -1, -1));
465 /*! \brief ast_say_number_full_en: English syntax */
466 /* This is the default syntax, if no other syntax defined in this file is used */
467 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
473 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
475 while(!res && (num || playh)) {
477 snprintf(fn, sizeof(fn), "digits/minus");
478 if ( num > INT_MIN ) {
484 snprintf(fn, sizeof(fn), "digits/hundred");
486 } else if (num < 20) {
487 snprintf(fn, sizeof(fn), "digits/%d", num);
489 } else if (num < 100) {
490 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
491 num -= ((num / 10) * 10);
494 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
496 num -= ((num / 100) * 100);
498 if (num < 1000000) { /* 1,000,000 */
499 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
503 snprintf(fn, sizeof(fn), "digits/thousand");
505 if (num < 1000000000) { /* 1,000,000,000 */
506 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
510 snprintf(fn, sizeof(fn), "digits/million");
512 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
519 if(!ast_streamfile(chan, fn, language)) {
520 if ((audiofd > -1) && (ctrlfd > -1))
521 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
523 res = ast_waitstream(chan, ints);
525 ast_stopstream(chan);
531 static int exp10_int(int power)
534 for (x=0;x<power;x++)
539 /*! \brief ast_say_number_full_cz: Czech syntax */
541 * 1m,2m - gender male
542 * 1w,2w - gender female
546 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
548 * for each number 10^(3n + 3) exist 3 files represented as:
549 * 1 tousand = jeden tisic = 1_E3
550 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
551 * 5,6,... tousands = pet,sest,... tisic = 5_E3
557 * tousand, milion are gender male, so 1 and 2 is 1m 2m
558 * miliard is gender female, so 1 and 2 is 1w 2w
560 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)
570 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
575 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
577 while(!res && (num || playh)) {
579 snprintf(fn, sizeof(fn), "digits/minus");
580 if ( num > INT_MIN ) {
585 } else if (num < 3 ) {
586 snprintf(fn, sizeof(fn), "digits/%d%c",num,options[0]);
589 } else if (num < 20) {
590 snprintf(fn, sizeof(fn), "digits/%d",num);
593 } else if (num < 100) {
594 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
595 num -= ((num / 10) * 10);
596 } else if (num < 1000) {
597 hundered = num / 100;
598 if ( hundered == 1 ) {
599 snprintf(fn, sizeof(fn), "digits/1sto");
600 } else if ( hundered == 2 ) {
601 snprintf(fn, sizeof(fn), "digits/2ste");
603 res = ast_say_number_full_cz(chan,hundered,ints,language,options,audiofd,ctrlfd);
606 if (hundered == 3 || hundered == 4) {
607 snprintf(fn, sizeof(fn), "digits/sta");
608 } else if ( hundered > 4 ) {
609 snprintf(fn, sizeof(fn), "digits/set");
612 num -= (hundered * 100);
613 } else { /* num > 1000 */
614 length = (int)log10(num)+1;
615 while ( (length % 3 ) != 1 ) {
618 left = num / (exp10_int(length-1));
621 case 9: options = "w"; /* 1,000,000,000 gender female */
623 default : options = "m"; /* others are male */
626 if ( left > 1 ) { /* we dont say "one thousand" but only thousand */
627 res = ast_say_number_full_cz(chan,left,ints,language,options,audiofd,ctrlfd);
631 if ( left >= 5 ) { /* >= 5 have the same declesion */
632 snprintf(fn, sizeof(fn), "digits/5_E%d",length-1);
633 } else if ( left >= 2 && left <= 4 ) {
634 snprintf(fn, sizeof(fn), "digits/2-4_E%d",length-1);
635 } else { /* left == 1 */
636 snprintf(fn, sizeof(fn), "digits/1_E%d",length-1);
638 num -= left * (exp10_int(length-1));
641 if(!ast_streamfile(chan, fn, language)) {
642 if ((audiofd > -1) && (ctrlfd > -1)) {
643 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
645 res = ast_waitstream(chan, ints);
648 ast_stopstream(chan);
654 /*! \brief ast_say_number_full_da: Danish syntax */
656 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
658 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)
663 int cn = 1; /* +1 = commune; -1 = neuter */
666 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
668 if (options && !strncasecmp(options, "n",1)) cn = -1;
670 while(!res && (num || playh || playa )) {
671 /* The grammar for Danish numbers is the same as for English except
673 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
674 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
675 * "one-and twenty" and 68 is "eight-and sixty".
676 * - "million" is different in singular and plural form
677 * - numbers > 1000 with zero as the third digit from last have an
678 * "and" before the last two digits, i.e. 2034 is "two thousand and
679 * four-and thirty" and 1000012 is "one million and twelve".
682 snprintf(fn, sizeof(fn), "digits/minus");
683 if ( num > INT_MIN ) {
689 snprintf(fn, sizeof(fn), "digits/hundred");
692 snprintf(fn, sizeof(fn), "digits/and");
694 } else if (num == 1 && cn == -1) {
695 snprintf(fn, sizeof(fn), "digits/1N");
697 } else if (num < 20) {
698 snprintf(fn, sizeof(fn), "digits/%d", num);
700 } else if (num < 100) {
703 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
706 snprintf(fn, sizeof(fn), "digits/%d", num);
711 int hundreds = num / 100;
713 snprintf(fn, sizeof(fn), "digits/1N");
715 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
718 num -= 100 * hundreds;
724 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
728 snprintf(fn, sizeof(fn), "digits/thousand");
730 if (num < 1000000000) {
731 int millions = num / 1000000;
732 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
736 snprintf(fn, sizeof(fn), "digits/million");
738 snprintf(fn, sizeof(fn), "digits/millions");
741 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
745 if (num && num < 100)
750 if(!ast_streamfile(chan, fn, language)) {
751 if ((audiofd > -1) && (ctrlfd > -1))
752 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
754 res = ast_waitstream(chan, ints);
756 ast_stopstream(chan);
762 /*! \brief ast_say_number_full_de: German syntax */
764 In addition to English, the following sounds are required:
766 "1-and" through "9-and"
769 NB "1" is recorded as 'eins'
771 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)
774 int mf = 1; /* +1 = male and neuter; -1 = female */
778 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
780 if (options && (!strncasecmp(options, "f",1)))
784 /* The grammar for German numbers is the same as for English except
786 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
787 * "one-and twenty" and 68 is "eight-and sixty".
788 * - "one" varies according to gender
789 * - 100 is 'hundert', however all other instances are 'ein hundert'
790 * - 1000 is 'tausend', however all other instances are 'ein tausend'
791 * - 1000000 is always 'eine million'
792 * - "million" is different in singular and plural form
795 snprintf(fn, sizeof(fn), "digits/minus");
796 if ( num > INT_MIN ) {
801 } else if (num < 100 && t) {
802 snprintf(fn, sizeof(fn), "digits/and");
804 } else if (num == 1 && mf == -1) {
805 snprintf(fn, sizeof(fn), "digits/%dF", num);
807 } else if (num < 20) {
808 snprintf(fn, sizeof(fn), "digits/%d", num);
810 } else if (num < 100) {
813 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
816 snprintf(fn, sizeof(fn), "digits/%d", num);
819 } else if (num == 100 && t == 0) {
820 snprintf(fn, sizeof(fn), "digits/hundred");
822 } else if (num < 1000) {
823 int hundreds = num / 100;
826 snprintf(fn, sizeof(fn), "digits/1N");
828 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
830 snprintf(fna, sizeof(fna), "digits/hundred");
832 } else if (num == 1000 && t == 0) {
833 snprintf(fn, sizeof(fn), "digits/thousand");
835 } else if (num < 1000000) {
836 int thousands = num / 1000;
839 if (thousands == 1) {
840 snprintf(fn, sizeof(fn), "digits/1N");
841 snprintf(fna, sizeof(fna), "digits/thousand");
843 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
846 snprintf(fn, sizeof(fn), "digits/thousand");
848 } else if (num < 1000000000) {
849 int millions = num / 1000000;
853 snprintf(fn, sizeof(fn), "digits/1F");
854 snprintf(fna, sizeof(fna), "digits/million");
856 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
859 snprintf(fn, sizeof(fn), "digits/millions");
861 } else if (num <= INT_MAX) {
862 int billions = num / 1000000000;
863 num = num % 1000000000;
866 snprintf(fn, sizeof(fn), "digits/1F");
867 snprintf(fna, sizeof(fna), "digits/milliard");
869 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
873 snprintf(fn, sizeof(fn), "digits/milliards");
876 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
880 if(!ast_streamfile(chan, fn, language)) {
881 if ((audiofd > -1) && (ctrlfd > -1))
882 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
884 res = ast_waitstream(chan, ints);
886 ast_stopstream(chan);
888 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
889 if ((audiofd > -1) && (ctrlfd > -1))
890 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
892 res = ast_waitstream(chan, ints);
894 ast_stopstream(chan);
902 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
904 In addition to American English, the following sounds are required: "and"
906 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
913 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
915 while(!res && (num || playh || playa )) {
917 snprintf(fn, sizeof(fn), "digits/minus");
918 if ( num > INT_MIN ) {
924 snprintf(fn, sizeof(fn), "digits/hundred");
927 snprintf(fn, sizeof(fn), "digits/and");
929 } else if (num < 20) {
930 snprintf(fn, sizeof(fn), "digits/%d", num);
932 } else if (num < 100) {
933 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
934 num -= ((num / 10) * 10);
935 } else if (num < 1000) {
936 int hundreds = num / 100;
937 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
940 num -= 100 * hundreds;
943 } else if (num < 1000000) {
944 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
947 snprintf(fn, sizeof(fn), "digits/thousand");
949 if (num && num < 100)
951 } else if (num < 1000000000) {
952 int millions = num / 1000000;
953 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
956 snprintf(fn, sizeof(fn), "digits/million");
958 if (num && num < 100)
961 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
966 if(!ast_streamfile(chan, fn, language)) {
967 if ((audiofd > -1) && (ctrlfd > -1))
968 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
970 res = ast_waitstream(chan, ints);
972 ast_stopstream(chan);
978 /*! \brief ast_say_number_full_es: Spanish syntax */
980 Requires a few new audios:
981 1F.gsm: feminine 'una'
982 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
984 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)
988 int mf = 0; /* +1 = male; -1 = female */
991 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
994 if (!strncasecmp(options, "f",1))
996 else if (!strncasecmp(options, "m", 1))
1000 while (!res && num) {
1002 snprintf(fn, sizeof(fn), "digits/minus");
1003 if ( num > INT_MIN ) {
1009 snprintf(fn, sizeof(fn), "digits/and");
1011 } else if (num == 1) {
1013 snprintf(fn, sizeof(fn), "digits/%dF", num);
1015 snprintf(fn, sizeof(fn), "digits/%dM", num);
1017 snprintf(fn, sizeof(fn), "digits/%d", num);
1019 } else if (num < 31) {
1020 snprintf(fn, sizeof(fn), "digits/%d", num);
1022 } else if (num < 100) {
1023 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1024 num -= ((num/10)*10);
1027 } else if (num == 100) {
1028 snprintf(fn, sizeof(fn), "digits/100");
1030 } else if (num < 200) {
1031 snprintf(fn, sizeof(fn), "digits/100-and");
1035 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1036 num -= ((num/100)*100);
1037 } else if (num < 2000) {
1039 snprintf(fn, sizeof(fn), "digits/thousand");
1041 if (num < 1000000) {
1042 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1046 snprintf(fn, sizeof(fn), "digits/thousand");
1048 if (num < 2147483640) {
1049 if ((num/1000000) == 1) {
1050 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1053 snprintf(fn, sizeof(fn), "digits/million");
1055 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1058 snprintf(fn, sizeof(fn), "digits/millions");
1060 num = num % 1000000;
1062 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1070 if(!ast_streamfile(chan, fn, language)) {
1071 if ((audiofd > -1) && (ctrlfd > -1))
1072 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1074 res = ast_waitstream(chan, ints);
1076 ast_stopstream(chan);
1084 /*! \brief ast_say_number_full_fr: French syntax */
1085 /* Extra sounds needed:
1088 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)
1093 int mf = 1; /* +1 = male; -1 = female */
1096 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1098 if (options && !strncasecmp(options, "f",1))
1101 while(!res && (num || playh || playa)) {
1103 snprintf(fn, sizeof(fn), "digits/minus");
1104 if ( num > INT_MIN ) {
1110 snprintf(fn, sizeof(fn), "digits/hundred");
1113 snprintf(fn, sizeof(fn), "digits/et");
1115 } else if (num == 1) {
1117 snprintf(fn, sizeof(fn), "digits/%dF", num);
1119 snprintf(fn, sizeof(fn), "digits/%d", num);
1121 } else if (num < 21) {
1122 snprintf(fn, sizeof(fn), "digits/%d", num);
1124 } else if (num < 70) {
1125 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1126 if ((num % 10) == 1) playa++;
1128 } else if (num < 80) {
1129 snprintf(fn, sizeof(fn), "digits/60");
1130 if ((num % 10) == 1) playa++;
1132 } else if (num < 100) {
1133 snprintf(fn, sizeof(fn), "digits/80");
1135 } else if (num < 200) {
1136 snprintf(fn, sizeof(fn), "digits/hundred");
1138 } else if (num < 1000) {
1139 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1142 } else if (num < 2000) {
1143 snprintf(fn, sizeof(fn), "digits/thousand");
1145 } else if (num < 1000000) {
1146 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1149 snprintf(fn, sizeof(fn), "digits/thousand");
1151 } else if (num < 1000000000) {
1152 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1155 snprintf(fn, sizeof(fn), "digits/million");
1156 num = num % 1000000;
1158 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1162 if(!ast_streamfile(chan, fn, language)) {
1163 if ((audiofd > -1) && (ctrlfd > -1))
1164 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1166 res = ast_waitstream(chan, ints);
1168 ast_stopstream(chan);
1176 /*! \brief ast_say_number_full_he: Hebrew syntax */
1177 /* Extra sounds needed:
1181 2thousands: 2 thousand
1182 thousands: plural of 'thousand'
1183 3sF 'Smichut forms (female)
1190 3s 'Smichut' forms (male)
1207 TODO: 've' should sometimed be 'hu':
1208 * before 'shtaym' (2, F)
1209 * before 'shnaym' (2, M)
1210 * before 'shlosha' (3, M)
1211 * before 'shmone' (8, M)
1212 * before 'shlosim' (30)
1213 * before 'shmonim' (80)
1219 #define SAY_NUM_BUF_SIZE 256
1220 static int ast_say_number_full_he(struct ast_channel *chan, int num,
1221 const char *ints, const char *language, const char *options,
1222 int audiofd, int ctrlfd)
1225 int state = 0; /* no need to save anything */
1226 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1227 char fn[SAY_NUM_BUF_SIZE] = "";
1228 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. "
1229 "num: %d, options=\"%s\"\n",
1233 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1235 if (options && !strncasecmp(options, "f",1))
1238 /* Do we have work to do? */
1239 while(!res && (num || (state>0) )) {
1240 /* first type of work: play a second sound. In this loop
1241 * we can only play one sound file at a time. Thus playing
1242 * a second one requires repeating the loop just for the
1243 * second file. The variable 'state' remembers where we were.
1244 * state==0 is the normal mode and it means that we continue
1245 * to check if the number num has yet anything left.
1247 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, "
1248 "state=%d, options=\"%s\", mf=%d\n",
1249 num, state, options, mf
1252 snprintf(fn, sizeof(fn), "digits/hundred");
1254 } else if (state==2) {
1255 snprintf(fn, sizeof(fn), "digits/ve");
1257 } else if (state==3) {
1258 snprintf(fn, sizeof(fn), "digits/thousands");
1260 } else if (num <21) {
1262 snprintf(fn, sizeof(fn), "digits/%dF", num);
1264 snprintf(fn, sizeof(fn), "digits/%d", num);
1266 } else if (num < 100) {
1267 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1270 } else if (num < 200) {
1271 snprintf(fn, sizeof(fn), "digits/hundred");
1273 } else if (num < 300) {
1274 snprintf(fn, sizeof(fn), "digits/hundred");
1276 } else if (num < 1000) {
1277 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1280 } else if (num < 2000) {
1281 snprintf(fn, sizeof(fn), "digits/thousand");
1283 } else if (num < 3000) {
1284 snprintf(fn, sizeof(fn), "digits/2thousand");
1287 } else if (num < 20000) {
1288 snprintf(fn, sizeof(fn), "digits/%ds",(num/1000));
1291 } else if (num < 1000000) {
1292 res = ast_say_number_full_he(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1295 snprintf(fn, sizeof(fn), "digits/thousand");
1297 } else if (num < 1000000000) {
1298 res = ast_say_number_full_he(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1301 snprintf(fn, sizeof(fn), "digits/million");
1302 num = num % 1000000;
1304 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1308 if(!ast_streamfile(chan, fn, language)) {
1309 if ((audiofd > -1) && (ctrlfd > -1))
1310 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1312 res = ast_waitstream(chan, ints);
1314 ast_stopstream(chan);
1320 /*! \brief ast_say_number_full_it: Italian */
1321 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1329 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1334 Like english, numbers up to 20 are a single 'word', and others
1335 compound, but with exceptions.
1336 For example 21 is not twenty-one, but there is a single word in 'it'.
1337 Idem for 28 (ie when a the 2nd part of a compund number
1338 starts with a vowel)
1340 There are exceptions also for hundred, thousand and million.
1341 In english 100 = one hundred, 200 is two hundred.
1342 In italian 100 = cento , like to say hundred (without one),
1343 200 and more are like english.
1345 Same applies for thousand:
1346 1000 is one thousand in en, 2000 is two thousand.
1347 In it we have 1000 = mille , 2000 = 2 mila
1349 For million(s) we use the plural, if more than one
1350 Also, one million is abbreviated in it, like on-million,
1351 or 'un milione', not 'uno milione'.
1352 So the right file is provided.
1355 while(!res && (num || playh)) {
1357 snprintf(fn, sizeof(fn), "digits/minus");
1358 if ( num > INT_MIN ) {
1364 snprintf(fn, sizeof(fn), "digits/hundred");
1366 } else if (num < 20) {
1367 snprintf(fn, sizeof(fn), "digits/%d", num);
1369 } else if (num == 21) {
1370 snprintf(fn, sizeof(fn), "digits/%d", num);
1372 } else if (num == 28) {
1373 snprintf(fn, sizeof(fn), "digits/%d", num);
1375 } else if (num == 31) {
1376 snprintf(fn, sizeof(fn), "digits/%d", num);
1378 } else if (num == 38) {
1379 snprintf(fn, sizeof(fn), "digits/%d", num);
1381 } else if (num == 41) {
1382 snprintf(fn, sizeof(fn), "digits/%d", num);
1384 } else if (num == 48) {
1385 snprintf(fn, sizeof(fn), "digits/%d", num);
1387 } else if (num == 51) {
1388 snprintf(fn, sizeof(fn), "digits/%d", num);
1390 } else if (num == 58) {
1391 snprintf(fn, sizeof(fn), "digits/%d", num);
1393 } else if (num == 61) {
1394 snprintf(fn, sizeof(fn), "digits/%d", num);
1396 } else if (num == 68) {
1397 snprintf(fn, sizeof(fn), "digits/%d", num);
1399 } else if (num == 71) {
1400 snprintf(fn, sizeof(fn), "digits/%d", num);
1402 } else if (num == 78) {
1403 snprintf(fn, sizeof(fn), "digits/%d", num);
1405 } else if (num == 81) {
1406 snprintf(fn, sizeof(fn), "digits/%d", num);
1408 } else if (num == 88) {
1409 snprintf(fn, sizeof(fn), "digits/%d", num);
1411 } else if (num == 91) {
1412 snprintf(fn, sizeof(fn), "digits/%d", num);
1414 } else if (num == 98) {
1415 snprintf(fn, sizeof(fn), "digits/%d", num);
1417 } else if (num < 100) {
1418 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1419 num -= ((num / 10) * 10);
1422 if ((num / 100) > 1) {
1423 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1426 snprintf(fn, sizeof(fn), "digits/hundred");
1428 num -= ((num / 100) * 100);
1430 if (num < 1000000) { /* 1,000,000 */
1432 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1437 if ((tempnum / 1000) < 2)
1438 snprintf(fn, sizeof(fn), "digits/thousand");
1439 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1440 snprintf(fn, sizeof(fn), "digits/thousands");
1442 if (num < 1000000000) { /* 1,000,000,000 */
1443 if ((num / 1000000) > 1)
1444 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1448 num = num % 1000000;
1449 if ((tempnum / 1000000) < 2)
1450 snprintf(fn, sizeof(fn), "digits/million");
1452 snprintf(fn, sizeof(fn), "digits/millions");
1454 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1461 if(!ast_streamfile(chan, fn, language)) {
1462 if ((audiofd > -1) && (ctrlfd > -1))
1463 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1465 res = ast_waitstream(chan, ints);
1467 ast_stopstream(chan);
1473 /*! \brief ast_say_number_full_nl: dutch syntax */
1474 /* New files: digits/nl-en
1476 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1483 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1484 while (!res && (num || playh )) {
1486 snprintf(fn, sizeof(fn), "digits/minus");
1487 if ( num > INT_MIN ) {
1493 snprintf(fn, sizeof(fn), "digits/hundred");
1495 } else if (num < 20) {
1496 snprintf(fn, sizeof(fn), "digits/%d", num);
1498 } else if (num < 100) {
1501 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1505 snprintf(fn, sizeof(fn), "digits/nl-en");
1507 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1512 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1514 num -= ((num / 100) * 100);
1516 if (num < 1000000) { /* 1,000,000 */
1517 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1521 snprintf(fn, sizeof(fn), "digits/thousand");
1523 if (num < 1000000000) { /* 1,000,000,000 */
1524 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1527 num = num % 1000000;
1528 snprintf(fn, sizeof(fn), "digits/million");
1530 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1538 if(!ast_streamfile(chan, fn, language)) {
1539 if ((audiofd > -1) && (ctrlfd > -1))
1540 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1542 res = ast_waitstream(chan, ints);
1544 ast_stopstream(chan);
1550 /*! \brief ast_say_number_full_no: Norwegian syntax */
1552 In addition to American English, the following sounds are required: "and", "1N"
1554 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)
1559 int cn = 1; /* +1 = commune; -1 = neuter */
1563 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1565 if (options && !strncasecmp(options, "n",1)) cn = -1;
1567 while(!res && (num || playh || playa )) {
1568 /* The grammar for Norwegian numbers is the same as for English except
1569 * for the following:
1570 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1571 * "and" before the last two digits, i.e. 2034 is "two thousand and
1572 * thirty-four" and 1000012 is "one million and twelve".
1575 snprintf(fn, sizeof(fn), "digits/minus");
1576 if ( num > INT_MIN ) {
1582 snprintf(fn, sizeof(fn), "digits/hundred");
1585 snprintf(fn, sizeof(fn), "digits/and");
1587 } else if (num == 1 && cn == -1) {
1588 snprintf(fn, sizeof(fn), "digits/1N");
1590 } else if (num < 20) {
1591 snprintf(fn, sizeof(fn), "digits/%d", num);
1593 } else if (num < 100) {
1594 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1595 num -= ((num / 10) * 10);
1596 } else if (num < 1000) {
1597 int hundreds = num / 100;
1599 snprintf(fn, sizeof(fn), "digits/1N");
1601 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1604 num -= 100 * hundreds;
1607 } else if (num < 1000000) {
1608 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1611 snprintf(fn, sizeof(fn), "digits/thousand");
1613 if (num && num < 100)
1615 } else if (num < 1000000000) {
1616 int millions = num / 1000000;
1617 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1620 snprintf(fn, sizeof(fn), "digits/million");
1621 num = num % 1000000;
1622 if (num && num < 100)
1625 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1630 if(!ast_streamfile(chan, fn, language)) {
1631 if ((audiofd > -1) && (ctrlfd > -1))
1632 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1634 res = ast_waitstream(chan, ints);
1636 ast_stopstream(chan);
1643 char *separator_dziesiatek;
1647 char *dziesiatki[10];
1652 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1658 return odm->rzedy[rzad - 1][0];
1659 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1660 return odm->rzedy[rzad - 1][1];
1662 return odm->rzedy[rzad - 1][2];
1665 static char* pl_append(char* buffer, char* str)
1667 strcpy(buffer, str);
1668 buffer += strlen(str);
1672 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1674 char file_name[255] = "digits/";
1675 strcat(file_name, fn);
1676 ast_log(LOG_DEBUG, "Trying to play: %s\n", file_name);
1677 if (!ast_streamfile(chan, file_name, language)) {
1678 if ((audiofd > -1) && (ctrlfd > -1))
1679 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1681 ast_waitstream(chan, ints);
1683 ast_stopstream(chan);
1686 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1688 /* Initialise variables to allow compilation on Debian-stable, etc */
1698 if (i == 0 && rzad > 0) {
1702 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1705 m1000E6 = i % 1000000000;
1706 i1000E6 = i / 1000000000;
1708 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1710 m1000E3 = m1000E6 % 1000000;
1711 i1000E3 = m1000E6 / 1000000;
1713 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1715 m1000 = m1000E3 % 1000;
1716 i1000 = m1000E3 / 1000;
1718 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1724 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1726 if ( m100 > 0 && m100 <=9 ) {
1728 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1730 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1731 } else if (m100 % 10 == 0) {
1732 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1733 } else if (m100 <= 19 ) {
1734 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1735 } else if (m100 != 0) {
1736 if (odm->separator_dziesiatek[0]==' ') {
1737 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1738 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1742 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1743 b = pl_append(b, odm->separator_dziesiatek);
1744 b = pl_append(b, odm->cyfry2[m100 % 10]);
1745 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1750 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1754 /* ast_say_number_full_pl: Polish syntax */
1755 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)
1765 1000000000.2 miliardy
1766 1000000000.5 miliardow
1830 70m siedemdziesieciu
1842 90m dziewiedziesieciu
1844 and combinations of eg.: 20_1, 30m_3m, etc...
1848 char *zenski_cyfry[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1850 char *zenski_cyfry2[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1852 char *meski_cyfry[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1854 char *meski_cyfry2[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1856 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1858 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1860 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1862 char *nijaki_cyfry[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1864 char *nijaki_cyfry2[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1866 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1868 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1870 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1872 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1874 /* Initialise variables to allow compilation on Debian-stable, etc */
1877 static odmiana *odmiana_nieosobowa = NULL;
1878 static odmiana *odmiana_meska = NULL;
1879 static odmiana *odmiana_zenska = NULL;
1881 if (odmiana_nieosobowa == NULL) {
1882 odmiana_nieosobowa = (odmiana *) malloc(sizeof(odmiana));
1884 odmiana_nieosobowa->separator_dziesiatek = "_";
1886 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
1887 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
1888 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
1889 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
1890 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
1891 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
1894 if (odmiana_zenska == NULL) {
1895 odmiana_zenska = (odmiana *) malloc(sizeof(odmiana));
1897 odmiana_zenska->separator_dziesiatek = " ";
1899 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
1900 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
1901 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
1902 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
1903 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
1904 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
1907 if (odmiana_meska == NULL) {
1908 odmiana_meska = (odmiana *) malloc(sizeof(odmiana));
1910 odmiana_meska->separator_dziesiatek = " ";
1912 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
1913 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
1914 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
1915 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
1916 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
1917 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
1921 if (strncasecmp(options, "f", 1) == 0)
1923 else if (strncasecmp(options, "m", 1) == 0)
1926 o = odmiana_nieosobowa;
1928 o = odmiana_nieosobowa;
1930 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
1934 /* ast_say_number_full_pt: Portuguese syntax */
1935 /* Extra sounds needed: */
1936 /* For feminin all sound files end with F */
1937 /* 100E for 100+ something */
1938 /* 1000000S for plural */
1939 /* pt-e for 'and' */
1940 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)
1944 int mf = 1; /* +1 = male; -1 = female */
1948 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1950 if (options && !strncasecmp(options, "f",1))
1953 while(!res && num ) {
1955 snprintf(fn, sizeof(fn), "digits/minus");
1956 if ( num > INT_MIN ) {
1961 } else if (num < 20) {
1962 if ((num == 1 || num == 2) && (mf < 0))
1963 snprintf(fn, sizeof(fn), "digits/%dF", num);
1965 snprintf(fn, sizeof(fn), "digits/%d", num);
1967 } else if (num < 100) {
1968 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1972 } else if (num < 1000) {
1974 snprintf(fn, sizeof(fn), "digits/100");
1976 snprintf(fn, sizeof(fn), "digits/100E");
1978 if (mf < 0 && num > 199)
1979 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1981 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1986 } else if (num < 1000000) {
1988 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1992 snprintf(fn, sizeof(fn), "digits/1000");
1993 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
1996 } else if (num < 1000000000) {
1997 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2001 snprintf(fn, sizeof(fn), "digits/1000000");
2003 snprintf(fn, sizeof(fn), "digits/1000000S");
2005 if ((num % 1000000) &&
2007 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2008 /* no hundreds and below */
2009 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2011 num = num % 1000000;
2014 if (!ast_streamfile(chan, fn, language)) {
2015 if ((audiofd > -1) && (ctrlfd > -1))
2016 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2018 res = ast_waitstream(chan, ints);
2020 ast_stopstream(chan);
2022 if (!res && playh) {
2023 res = wait_file(chan, ints, "digits/pt-e", language);
2024 ast_stopstream(chan);
2031 /*! \brief ast_say_number_full_se: Swedish syntax */
2032 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)
2037 int cn = 1; /* +1 = commune; -1 = neuter */
2039 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2040 if (options && !strncasecmp(options, "n",1)) cn = -1;
2042 while(!res && (num || playh)) {
2044 snprintf(fn, sizeof(fn), "digits/minus");
2045 if ( num > INT_MIN ) {
2051 snprintf(fn, sizeof(fn), "digits/hundred");
2053 } else if (num < 20) {
2054 snprintf(fn, sizeof(fn), "digits/%d", num);
2056 } else if (num < 100) {
2057 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2058 num -= ((num / 10) * 10);
2059 } else if (num == 1 && cn == -1) { /* En eller ett? */
2060 snprintf(fn, sizeof(fn), "digits/1N");
2064 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2066 num -= ((num / 100) * 100);
2068 if (num < 1000000) { /* 1,000,000 */
2069 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
2074 snprintf(fn, sizeof(fn), "digits/thousand");
2076 if (num < 1000000000) { /* 1,000,000,000 */
2077 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
2081 num = num % 1000000;
2082 snprintf(fn, sizeof(fn), "digits/million");
2084 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2091 if(!ast_streamfile(chan, fn, language)) {
2092 if ((audiofd > -1) && (ctrlfd > -1))
2093 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2095 res = ast_waitstream(chan, ints);
2096 ast_stopstream(chan);
2103 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2104 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2110 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2112 while(!res && (num || playh)) {
2114 snprintf(fn, sizeof(fn), "digits/minus");
2115 if ( num > INT_MIN ) {
2121 snprintf(fn, sizeof(fn), "digits/hundred");
2123 } else if (num < 10) {
2124 snprintf(fn, sizeof(fn), "digits/%d", num);
2126 } else if (num < 100) {
2127 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2128 num -= ((num / 10) * 10);
2131 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2133 num -= ((num / 100) * 100);
2135 if (num < 1000000) { /* 1,000,000 */
2136 res = ast_say_number_full_tw(chan, num / 1000, ints, language, audiofd, ctrlfd);
2140 snprintf(fn, sizeof(fn), "digits/thousand");
2142 if (num < 1000000000) { /* 1,000,000,000 */
2143 res = ast_say_number_full_tw(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2146 num = num % 1000000;
2147 snprintf(fn, sizeof(fn), "digits/million");
2149 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2156 if(!ast_streamfile(chan, fn, language)) {
2157 if ((audiofd > -1) && (ctrlfd > -1))
2158 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2160 res = ast_waitstream(chan, ints);
2162 ast_stopstream(chan);
2169 /*! \brief determine last digits for thousands/millions (ru) */
2170 static int get_lastdigits_ru(int num) {
2173 } else if (num < 100) {
2174 return get_lastdigits_ru(num % 10);
2175 } else if (num < 1000) {
2176 return get_lastdigits_ru(num % 100);
2178 return 0; /* number too big */
2182 /*! \brief ast_say_number_full_ru: Russian syntax */
2183 /*! \brief additional files:
2184 n00.gsm (one hundred, two hundred, ...)
2187 thousands-i.gsm (tisyachi)
2188 million-a.gsm (milliona)
2194 where 'n' from 1 to 9
2196 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)
2202 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2204 while(!res && (num)) {
2206 snprintf(fn, sizeof(fn), "digits/minus");
2207 if ( num > INT_MIN ) {
2212 } else if (num < 20) {
2213 if(options && strlen(options) == 1 && num < 3) {
2214 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2216 snprintf(fn, sizeof(fn), "digits/%d", num);
2219 } else if (num < 100) {
2220 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2222 } else if (num < 1000){
2223 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2225 } else if (num < 1000000) { /* 1,000,000 */
2226 lastdigits = get_lastdigits_ru(num / 1000);
2228 if (lastdigits < 3) {
2229 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2231 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2235 if (lastdigits == 1) {
2236 snprintf(fn, sizeof(fn), "digits/thousand");
2237 } else if (lastdigits > 1 && lastdigits < 5) {
2238 snprintf(fn, sizeof(fn), "digits/thousands-i");
2240 snprintf(fn, sizeof(fn), "digits/thousands");
2243 } else if (num < 1000000000) { /* 1,000,000,000 */
2244 lastdigits = get_lastdigits_ru(num / 1000000);
2246 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2249 if (lastdigits == 1) {
2250 snprintf(fn, sizeof(fn), "digits/million");
2251 } else if (lastdigits > 1 && lastdigits < 5) {
2252 snprintf(fn, sizeof(fn), "digits/million-a");
2254 snprintf(fn, sizeof(fn), "digits/millions");
2258 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2262 if (!ast_streamfile(chan, fn, language)) {
2263 if ((audiofd > -1) && (ctrlfd > -1))
2264 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2266 res = ast_waitstream(chan, ints);
2268 ast_stopstream(chan);
2275 /*! \brief ast_say_enumeration_full: call language-specific functions */
2276 /* Called from AGI */
2277 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2279 if (!strcasecmp(language,"en") ) { /* English syntax */
2280 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2281 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
2282 return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
2283 } else if (!strcasecmp(language, "de") ) { /* German syntax */
2284 return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
2287 /* Default to english */
2288 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2291 /*! \brief ast_say_enumeration: call language-specific functions without file descriptors */
2292 static int say_enumeration(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options)
2294 return(ast_say_enumeration_full(chan, num, ints, language, options, -1, -1));
2297 /*! \brief ast_say_enumeration_full_en: English syntax */
2298 /* This is the default syntax, if no other syntax defined in this file is used */
2299 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2304 while(!res && num) {
2306 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2307 if ( num > INT_MIN ) {
2312 } else if (num < 20) {
2313 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2315 } else if (num < 100) {
2316 int tens = num / 10;
2319 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2321 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2323 } else if (num < 1000) {
2324 int hundreds = num / 100;
2326 if (hundreds > 1 || t == 1) {
2327 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2332 snprintf(fn, sizeof(fn), "digits/hundred");
2334 snprintf(fn, sizeof(fn), "digits/h-hundred");
2336 } else if (num < 1000000) {
2337 int thousands = num / 1000;
2339 if (thousands > 1 || t == 1) {
2340 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2345 snprintf(fn, sizeof(fn), "digits/thousand");
2347 snprintf(fn, sizeof(fn), "digits/h-thousand");
2350 } else if (num < 1000000000) {
2351 int millions = num / 1000000;
2352 num = num % 1000000;
2354 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2358 snprintf(fn, sizeof(fn), "digits/million");
2360 snprintf(fn, sizeof(fn), "digits/h-million");
2362 } else if (num < INT_MAX) {
2363 int billions = num / 1000000000;
2364 num = num % 1000000000;
2366 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2370 snprintf(fn, sizeof(fn), "digits/billion");
2372 snprintf(fn, sizeof(fn), "digits/h-billion");
2374 } else if (num == INT_MAX) {
2375 snprintf(fn, sizeof(fn), "digits/h-last");
2378 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2383 if (!ast_streamfile(chan, fn, language)) {
2384 if ((audiofd > -1) && (ctrlfd > -1)) {
2385 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2387 res = ast_waitstream(chan, ints);
2390 ast_stopstream(chan);
2396 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2397 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)
2399 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2401 char fn[256] = "", fna[256] = "";
2404 if (options && !strncasecmp(options, "f",1)) {
2406 } else if (options && !strncasecmp(options, "n",1)) {
2413 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2415 while(!res && num) {
2417 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2418 if ( num > INT_MIN ) {
2423 } else if (num < 100 && t) {
2424 snprintf(fn, sizeof(fn), "digits/and");
2426 } else if (num < 20) {
2427 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2429 } else if (num < 100) {
2430 int ones = num % 10;
2432 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2435 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2438 } else if (num == 100 && t == 0) {
2439 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2441 } else if (num < 1000) {
2442 int hundreds = num / 100;
2444 if (hundreds == 1) {
2445 snprintf(fn, sizeof(fn), "digits/1N");
2447 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2450 snprintf(fna, sizeof(fna), "digits/hundred");
2452 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2455 } else if (num < 1000000) {
2456 int thousands = num / 1000;
2458 if (thousands == 1) {
2460 snprintf(fn, sizeof(fn), "digits/1N");
2461 snprintf(fna, sizeof(fna), "digits/thousand");
2464 snprintf(fn, sizeof(fn), "digits/1N");
2465 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2467 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2471 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2476 snprintf(fn, sizeof(fn), "digits/thousand");
2478 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2482 } else if (num < 1000000000) {
2483 int millions = num / 1000000;
2484 num = num % 1000000;
2485 if (millions == 1) {
2487 snprintf(fn, sizeof(fn), "digits/1F");
2488 snprintf(fna, sizeof(fna), "digits/million");
2490 snprintf(fn, sizeof(fn), "digits/1N");
2491 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2494 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2499 snprintf(fn, sizeof(fn), "digits/millions");
2501 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2505 } else if (num < INT_MAX) {
2506 int billions = num / 1000000000;
2507 num = num % 1000000000;
2508 if (billions == 1) {
2510 snprintf(fn, sizeof(fn), "digits/1F");
2511 snprintf(fna, sizeof(fna), "digits/milliard");
2513 snprintf(fn, sizeof(fn), "digits/1N");
2514 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2517 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2521 snprintf(fn, sizeof(fna), "digits/milliards");
2523 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2527 } else if (num == INT_MAX) {
2528 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2531 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2536 if (!ast_streamfile(chan, fn, language)) {
2537 if ((audiofd > -1) && (ctrlfd > -1))
2538 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2540 res = ast_waitstream(chan, ints);
2542 ast_stopstream(chan);
2544 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2545 if ((audiofd > -1) && (ctrlfd > -1)) {
2546 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2548 res = ast_waitstream(chan, ints);
2551 ast_stopstream(chan);
2559 /*! \brief ast_say_enumeration_full_de: German syntax */
2560 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)
2562 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2564 char fn[256] = "", fna[256] = "";
2567 if (options && !strncasecmp(options, "f",1)) {
2569 } else if (options && !strncasecmp(options, "n",1)) {
2576 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2578 while(!res && num) {
2580 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2581 if ( num > INT_MIN ) {
2586 } else if (num < 100 && t) {
2587 snprintf(fn, sizeof(fn), "digits/and");
2589 } else if (num < 20) {
2590 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2592 } else if (num < 100) {
2593 int ones = num % 10;
2595 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2598 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2601 } else if (num == 100 && t == 0) {
2602 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2604 } else if (num < 1000) {
2605 int hundreds = num / 100;
2607 if (hundreds == 1) {
2608 snprintf(fn, sizeof(fn), "digits/1N");
2610 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2613 snprintf(fna, sizeof(fna), "digits/hundred");
2615 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2618 } else if (num < 1000000) {
2619 int thousands = num / 1000;
2621 if (thousands == 1) {
2623 snprintf(fn, sizeof(fn), "digits/1N");
2624 snprintf(fna, sizeof(fna), "digits/thousand");
2627 snprintf(fn, sizeof(fn), "digits/1N");
2628 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2630 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2634 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2639 snprintf(fn, sizeof(fn), "digits/thousand");
2641 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2645 } else if (num < 1000000000) {
2646 int millions = num / 1000000;
2647 num = num % 1000000;
2648 if (millions == 1) {
2650 snprintf(fn, sizeof(fn), "digits/1F");
2651 snprintf(fna, sizeof(fna), "digits/million");
2653 snprintf(fn, sizeof(fn), "digits/1N");
2654 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2657 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2662 snprintf(fn, sizeof(fn), "digits/millions");
2664 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2668 } else if (num < INT_MAX) {
2669 int billions = num / 1000000000;
2670 num = num % 1000000000;
2671 if (billions == 1) {
2673 snprintf(fn, sizeof(fn), "digits/1F");
2674 snprintf(fna, sizeof(fna), "digits/milliard");
2676 snprintf(fn, sizeof(fn), "digits/1N");
2677 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2680 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2684 snprintf(fn, sizeof(fna), "digits/milliards");
2686 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2690 } else if (num == INT_MAX) {
2691 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2694 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2699 if (!ast_streamfile(chan, fn, language)) {
2700 if ((audiofd > -1) && (ctrlfd > -1))
2701 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2703 res = ast_waitstream(chan, ints);
2705 ast_stopstream(chan);
2707 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2708 if ((audiofd > -1) && (ctrlfd > -1)) {
2709 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2711 res = ast_waitstream(chan, ints);
2714 ast_stopstream(chan);
2722 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2724 if (!strcasecmp(lang, "en") ) { /* English syntax */
2725 return(ast_say_date_en(chan, t, ints, lang));
2726 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2727 return(ast_say_date_da(chan, t, ints, lang));
2728 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2729 return(ast_say_date_de(chan, t, ints, lang));
2730 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2731 return(ast_say_date_fr(chan, t, ints, lang));
2732 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2733 return(ast_say_date_nl(chan, t, ints, lang));
2734 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2735 return(ast_say_date_pt(chan, t, ints, lang));
2736 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2737 return(ast_say_date_gr(chan, t, ints, lang));
2740 /* Default to English */
2741 return(ast_say_date_en(chan, t, ints, lang));
2744 /* English syntax */
2745 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2750 ast_localtime(&t,&tm,NULL);
2752 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2753 res = ast_streamfile(chan, fn, lang);
2755 res = ast_waitstream(chan, ints);
2758 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2759 res = ast_streamfile(chan, fn, lang);
2761 res = ast_waitstream(chan, ints);
2764 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2766 res = ast_waitstream(chan, ints);
2768 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2773 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2778 ast_localtime(&t,&tm,NULL);
2780 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2781 res = ast_streamfile(chan, fn, lang);
2783 res = ast_waitstream(chan, ints);
2786 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2788 res = ast_waitstream(chan, ints);
2790 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2791 res = ast_streamfile(chan, fn, lang);
2793 res = ast_waitstream(chan, ints);
2797 int year = tm.tm_year + 1900;
2798 if (year > 1999) { /* year 2000 and later */
2799 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2802 /* I'm not going to handle 1100 and prior */
2803 /* We'll just be silent on the year, instead of bombing out. */
2805 /* year 1100 to 1999. will anybody need this?!? */
2806 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2807 res = wait_file(chan, ints, fn, lang);
2809 res = wait_file(chan,ints, "digits/hundred", lang);
2810 if (!res && year % 100 != 0) {
2811 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);