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);
59 int ast_say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
77 fn = "letters/exclaimation-point";
83 fn = "letters/dollar";
92 fn = "letters/equals";
101 fn = "letters/space";
113 strcpy(fnbuf, "digits/X");
119 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
120 strcpy(fnbuf, "letters/X");
124 res = ast_streamfile(chan, fn, lang);
126 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
127 ast_stopstream(chan);
134 int ast_say_character_str(struct ast_channel *chan, const char *str, const char *ints, const char *lang)
136 return ast_say_character_str_full(chan, str, ints, lang, -1, -1);
139 int ast_say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
157 fn = "letters/exclaimation-point";
163 fn = "letters/dollar";
172 fn = "letters/equals";
178 fn = "letters/slash";
181 fn = "letters/space";
192 strcpy(fnbuf, "digits/X");
196 default: /* '9' falls here... */
198 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
199 strcpy(fnbuf, "phonetic/X_p");
203 res = ast_streamfile(chan, fn, lang);
205 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
206 ast_stopstream(chan);
213 int ast_say_phonetic_str(struct ast_channel *chan, const char *str, const char *ints, const char *lang)
215 return ast_say_phonetic_str_full(chan, str, ints, lang, -1, -1);
218 int ast_say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
225 while (str[num] && !res) {
247 strcpy(fnbuf, "digits/X");
253 res = ast_streamfile(chan, fn, lang);
255 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
256 ast_stopstream(chan);
264 int ast_say_digit_str(struct ast_channel *chan, const char *str, const char *ints, const char *lang)
266 return ast_say_digit_str_full(chan, str, ints, lang, -1, -1);
269 int ast_say_digits_full(struct ast_channel *chan, int num, const char *ints, const char *lang, int audiofd, int ctrlfd)
273 snprintf(fn2, sizeof(fn2), "%d", num);
274 return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd);
277 int ast_say_digits(struct ast_channel *chan, int num, const char *ints, const char *lang)
279 return ast_say_digits_full(chan, num, ints, lang, -1, -1);
282 /* Forward declarations */
283 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
284 \note Not really language codes.
285 For these language codes, Asterisk will change the syntax when
286 saying numbers (and in some cases dates and voicemail messages
290 \arg \b en - English (US)
291 \arg \b en_GB - English (British)
292 \arg \b es - Spanish, Mexican
297 \arg \b no - Norwegian
299 \arg \b pt - Portuguese
301 \arg \b tw - Taiwanese / Chinese
305 For Some languages the numbers differ for gender and plural.
306 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
307 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
308 use the option argument 'p' for plural enumerations like in German
310 Date/Time functions currently have less languages supported than saynumber().
312 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
314 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
317 Portuguese sound files needed for Time/Date functions:
328 Spanish sound files needed for Time/Date functions:
333 Italian sound files needed for Time/Date functions:
339 /* Forward declarations of language specific variants of ast_say_number_full */
340 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
341 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);
342 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);
343 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);
344 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
345 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);
346 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);
347 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);
348 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
349 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
350 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);
351 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);
352 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);
353 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);
354 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
355 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
356 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);
358 /* Forward declarations of language specific variants of ast_say_enumeration_full */
359 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
360 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);
361 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);
363 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
364 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
365 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
366 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
367 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
368 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
369 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
370 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
372 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);
373 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);
374 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);
375 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);
376 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);
377 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);
378 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);
379 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);
380 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);
381 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);
382 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);
384 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
385 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
386 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
387 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
388 static int ast_say_time_pt(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);
392 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
393 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
394 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
395 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
396 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
397 static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
398 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
400 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
401 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
402 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
404 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
407 if ((res = ast_streamfile(chan, file, lang)))
408 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
410 res = ast_waitstream(chan, ints);
414 /*! \brief ast_say_number_full: call language-specific functions */
415 /* Called from AGI */
416 int ast_say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
418 if (!strcasecmp(language,"en") ) { /* English syntax */
419 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
420 } else if (!strcasecmp(language, "cz") ) { /* Czech syntax */
421 return(ast_say_number_full_cz(chan, num, ints, language, options, audiofd, ctrlfd));
422 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
423 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
424 } else if (!strcasecmp(language, "de") ) { /* German syntax */
425 return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
426 } else if (!strcasecmp(language, "en_GB") ) { /* British syntax */
427 return(ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd));
428 } else if (!strcasecmp(language, "no") ) { /* Norwegian syntax */
429 return(ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd));
430 } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
431 return(ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd));
432 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
433 return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
434 } else if (!strcasecmp(language, "he") ) { /* Hebrew syntax */
435 return(ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
436 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
437 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
438 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
439 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
440 } else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
441 return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
442 } else if (!strcasecmp(language, "pt") ) { /* Portuguese syntax */
443 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
444 } else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
445 return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
446 } else if (!strcasecmp(language, "tw") || !strcasecmp(language, "zh") ) { /* Taiwanese / Chinese syntax */
447 return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
448 } else if (!strcasecmp(language, "gr") ) { /* Greek syntax */
449 return(ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd));
450 } else if (!strcasecmp(language, "ru") ) { /* Russian syntax */
451 return(ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd));
454 /* Default to english */
455 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
458 /*! \brief ast_say_number: call language-specific functions without file descriptors */
459 int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options)
461 return(ast_say_number_full(chan, num, ints, language, options, -1, -1));
464 /*! \brief ast_say_number_full_en: English syntax */
465 /* This is the default syntax, if no other syntax defined in this file is used */
466 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
472 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
474 while(!res && (num || playh)) {
476 snprintf(fn, sizeof(fn), "digits/minus");
477 if ( num > INT_MIN ) {
483 snprintf(fn, sizeof(fn), "digits/hundred");
485 } else if (num < 20) {
486 snprintf(fn, sizeof(fn), "digits/%d", num);
488 } else if (num < 100) {
489 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
490 num -= ((num / 10) * 10);
493 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
495 num -= ((num / 100) * 100);
497 if (num < 1000000) { /* 1,000,000 */
498 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
502 snprintf(fn, sizeof(fn), "digits/thousand");
504 if (num < 1000000000) { /* 1,000,000,000 */
505 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
509 snprintf(fn, sizeof(fn), "digits/million");
511 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
518 if(!ast_streamfile(chan, fn, language)) {
519 if ((audiofd > -1) && (ctrlfd > -1))
520 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
522 res = ast_waitstream(chan, ints);
524 ast_stopstream(chan);
530 static int exp10_int(int power)
533 for (x=0;x<power;x++)
538 /*! \brief ast_say_number_full_cz: Czech syntax */
540 * 1m,2m - gender male
541 * 1w,2w - gender female
545 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
547 * for each number 10^(3n + 3) exist 3 files represented as:
548 * 1 tousand = jeden tisic = 1_E3
549 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
550 * 5,6,... tousands = pet,sest,... tisic = 5_E3
556 * tousand, milion are gender male, so 1 and 2 is 1m 2m
557 * miliard is gender female, so 1 and 2 is 1w 2w
559 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)
569 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
574 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
576 while(!res && (num || playh)) {
578 snprintf(fn, sizeof(fn), "digits/minus");
579 if ( num > INT_MIN ) {
584 } else if (num < 3 ) {
585 snprintf(fn, sizeof(fn), "digits/%d%c",num,options[0]);
588 } else if (num < 20) {
589 snprintf(fn, sizeof(fn), "digits/%d",num);
592 } else if (num < 100) {
593 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
594 num -= ((num / 10) * 10);
595 } else if (num < 1000) {
596 hundered = num / 100;
597 if ( hundered == 1 ) {
598 snprintf(fn, sizeof(fn), "digits/1sto");
599 } else if ( hundered == 2 ) {
600 snprintf(fn, sizeof(fn), "digits/2ste");
602 res = ast_say_number_full_cz(chan,hundered,ints,language,options,audiofd,ctrlfd);
605 if (hundered == 3 || hundered == 4) {
606 snprintf(fn, sizeof(fn), "digits/sta");
607 } else if ( hundered > 4 ) {
608 snprintf(fn, sizeof(fn), "digits/set");
611 num -= (hundered * 100);
612 } else { /* num > 1000 */
613 length = (int)log10(num)+1;
614 while ( (length % 3 ) != 1 ) {
617 left = num / (exp10_int(length-1));
620 case 9: options = "w"; /* 1,000,000,000 gender female */
622 default : options = "m"; /* others are male */
625 if ( left > 1 ) { /* we dont say "one thousand" but only thousand */
626 res = ast_say_number_full_cz(chan,left,ints,language,options,audiofd,ctrlfd);
630 if ( left >= 5 ) { /* >= 5 have the same declesion */
631 snprintf(fn, sizeof(fn), "digits/5_E%d",length-1);
632 } else if ( left >= 2 && left <= 4 ) {
633 snprintf(fn, sizeof(fn), "digits/2-4_E%d",length-1);
634 } else { /* left == 1 */
635 snprintf(fn, sizeof(fn), "digits/1_E%d",length-1);
637 num -= left * (exp10_int(length-1));
640 if(!ast_streamfile(chan, fn, language)) {
641 if ((audiofd > -1) && (ctrlfd > -1)) {
642 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
644 res = ast_waitstream(chan, ints);
647 ast_stopstream(chan);
653 /*! \brief ast_say_number_full_da: Danish syntax */
655 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
657 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)
662 int cn = 1; /* +1 = commune; -1 = neuter */
665 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
667 if (options && !strncasecmp(options, "n",1)) cn = -1;
669 while(!res && (num || playh || playa )) {
670 /* The grammar for Danish numbers is the same as for English except
672 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
673 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
674 * "one-and twenty" and 68 is "eight-and sixty".
675 * - "million" is different in singular and plural form
676 * - numbers > 1000 with zero as the third digit from last have an
677 * "and" before the last two digits, i.e. 2034 is "two thousand and
678 * four-and thirty" and 1000012 is "one million and twelve".
681 snprintf(fn, sizeof(fn), "digits/minus");
682 if ( num > INT_MIN ) {
688 snprintf(fn, sizeof(fn), "digits/hundred");
691 snprintf(fn, sizeof(fn), "digits/and");
693 } else if (num == 1 && cn == -1) {
694 snprintf(fn, sizeof(fn), "digits/1N");
696 } else if (num < 20) {
697 snprintf(fn, sizeof(fn), "digits/%d", num);
699 } else if (num < 100) {
702 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
705 snprintf(fn, sizeof(fn), "digits/%d", num);
710 int hundreds = num / 100;
712 snprintf(fn, sizeof(fn), "digits/1N");
714 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
717 num -= 100 * hundreds;
723 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
727 snprintf(fn, sizeof(fn), "digits/thousand");
729 if (num < 1000000000) {
730 int millions = num / 1000000;
731 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
735 snprintf(fn, sizeof(fn), "digits/million");
737 snprintf(fn, sizeof(fn), "digits/millions");
740 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
744 if (num && num < 100)
749 if(!ast_streamfile(chan, fn, language)) {
750 if ((audiofd > -1) && (ctrlfd > -1))
751 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
753 res = ast_waitstream(chan, ints);
755 ast_stopstream(chan);
761 /*! \brief ast_say_number_full_de: German syntax */
763 In addition to English, the following sounds are required:
765 "1-and" through "9-and"
768 NB "1" is recorded as 'eins'
770 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)
773 int mf = 1; /* +1 = male and neuter; -1 = female */
777 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
779 if (options && (!strncasecmp(options, "f",1)))
783 /* The grammar for German numbers is the same as for English except
785 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
786 * "one-and twenty" and 68 is "eight-and sixty".
787 * - "one" varies according to gender
788 * - 100 is 'hundert', however all other instances are 'ein hundert'
789 * - 1000 is 'tausend', however all other instances are 'ein tausend'
790 * - 1000000 is always 'eine million'
791 * - "million" is different in singular and plural form
794 snprintf(fn, sizeof(fn), "digits/minus");
795 if ( num > INT_MIN ) {
800 } else if (num < 100 && t) {
801 snprintf(fn, sizeof(fn), "digits/and");
803 } else if (num == 1 && mf == -1) {
804 snprintf(fn, sizeof(fn), "digits/%dF", num);
806 } else if (num < 20) {
807 snprintf(fn, sizeof(fn), "digits/%d", num);
809 } else if (num < 100) {
812 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
815 snprintf(fn, sizeof(fn), "digits/%d", num);
818 } else if (num == 100 && t == 0) {
819 snprintf(fn, sizeof(fn), "digits/hundred");
821 } else if (num < 1000) {
822 int hundreds = num / 100;
825 snprintf(fn, sizeof(fn), "digits/1N");
827 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
829 snprintf(fna, sizeof(fna), "digits/hundred");
831 } else if (num == 1000 && t == 0) {
832 snprintf(fn, sizeof(fn), "digits/thousand");
834 } else if (num < 1000000) {
835 int thousands = num / 1000;
838 if (thousands == 1) {
839 snprintf(fn, sizeof(fn), "digits/1N");
840 snprintf(fna, sizeof(fna), "digits/thousand");
842 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
845 snprintf(fn, sizeof(fn), "digits/thousand");
847 } else if (num < 1000000000) {
848 int millions = num / 1000000;
852 snprintf(fn, sizeof(fn), "digits/1F");
853 snprintf(fna, sizeof(fna), "digits/million");
855 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
858 snprintf(fn, sizeof(fn), "digits/millions");
860 } else if (num <= INT_MAX) {
861 int billions = num / 1000000000;
862 num = num % 1000000000;
865 snprintf(fn, sizeof(fn), "digits/1F");
866 snprintf(fna, sizeof(fna), "digits/milliard");
868 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
872 snprintf(fn, sizeof(fn), "digits/milliards");
875 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
879 if(!ast_streamfile(chan, fn, language)) {
880 if ((audiofd > -1) && (ctrlfd > -1))
881 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
883 res = ast_waitstream(chan, ints);
885 ast_stopstream(chan);
887 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, 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);
901 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
903 In addition to American English, the following sounds are required: "and"
905 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
912 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
914 while(!res && (num || playh || playa )) {
916 snprintf(fn, sizeof(fn), "digits/minus");
917 if ( num > INT_MIN ) {
923 snprintf(fn, sizeof(fn), "digits/hundred");
926 snprintf(fn, sizeof(fn), "digits/and");
928 } else if (num < 20) {
929 snprintf(fn, sizeof(fn), "digits/%d", num);
931 } else if (num < 100) {
932 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
933 num -= ((num / 10) * 10);
934 } else if (num < 1000) {
935 int hundreds = num / 100;
936 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
939 num -= 100 * hundreds;
942 } else if (num < 1000000) {
943 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
946 snprintf(fn, sizeof(fn), "digits/thousand");
948 if (num && num < 100)
950 } else if (num < 1000000000) {
951 int millions = num / 1000000;
952 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
955 snprintf(fn, sizeof(fn), "digits/million");
957 if (num && num < 100)
960 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
965 if(!ast_streamfile(chan, fn, language)) {
966 if ((audiofd > -1) && (ctrlfd > -1))
967 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
969 res = ast_waitstream(chan, ints);
971 ast_stopstream(chan);
977 /*! \brief ast_say_number_full_es: Spanish syntax */
979 Requires a few new audios:
980 1F.gsm: feminine 'una'
981 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
983 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)
987 int mf = 0; /* +1 = male; -1 = female */
990 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
993 if (!strncasecmp(options, "f",1))
995 else if (!strncasecmp(options, "m", 1))
999 while (!res && num) {
1001 snprintf(fn, sizeof(fn), "digits/minus");
1002 if ( num > INT_MIN ) {
1008 snprintf(fn, sizeof(fn), "digits/and");
1010 } else if (num == 1) {
1012 snprintf(fn, sizeof(fn), "digits/%dF", num);
1014 snprintf(fn, sizeof(fn), "digits/%dM", num);
1016 snprintf(fn, sizeof(fn), "digits/%d", num);
1018 } else if (num < 31) {
1019 snprintf(fn, sizeof(fn), "digits/%d", num);
1021 } else if (num < 100) {
1022 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1023 num -= ((num/10)*10);
1026 } else if (num == 100) {
1027 snprintf(fn, sizeof(fn), "digits/100");
1029 } else if (num < 200) {
1030 snprintf(fn, sizeof(fn), "digits/100-and");
1034 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1035 num -= ((num/100)*100);
1036 } else if (num < 2000) {
1038 snprintf(fn, sizeof(fn), "digits/thousand");
1040 if (num < 1000000) {
1041 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1045 snprintf(fn, sizeof(fn), "digits/thousand");
1047 if (num < 2147483640) {
1048 if ((num/1000000) == 1) {
1049 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1052 snprintf(fn, sizeof(fn), "digits/million");
1054 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1057 snprintf(fn, sizeof(fn), "digits/millions");
1059 num = num % 1000000;
1061 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1069 if(!ast_streamfile(chan, fn, language)) {
1070 if ((audiofd > -1) && (ctrlfd > -1))
1071 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1073 res = ast_waitstream(chan, ints);
1075 ast_stopstream(chan);
1083 /*! \brief ast_say_number_full_fr: French syntax */
1084 /* Extra sounds needed:
1087 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)
1092 int mf = 1; /* +1 = male; -1 = female */
1095 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1097 if (options && !strncasecmp(options, "f",1))
1100 while(!res && (num || playh || playa)) {
1102 snprintf(fn, sizeof(fn), "digits/minus");
1103 if ( num > INT_MIN ) {
1109 snprintf(fn, sizeof(fn), "digits/hundred");
1112 snprintf(fn, sizeof(fn), "digits/et");
1114 } else if (num == 1) {
1116 snprintf(fn, sizeof(fn), "digits/%dF", num);
1118 snprintf(fn, sizeof(fn), "digits/%d", num);
1120 } else if (num < 21) {
1121 snprintf(fn, sizeof(fn), "digits/%d", num);
1123 } else if (num < 70) {
1124 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1125 if ((num % 10) == 1) playa++;
1127 } else if (num < 80) {
1128 snprintf(fn, sizeof(fn), "digits/60");
1129 if ((num % 10) == 1) playa++;
1131 } else if (num < 100) {
1132 snprintf(fn, sizeof(fn), "digits/80");
1134 } else if (num < 200) {
1135 snprintf(fn, sizeof(fn), "digits/hundred");
1137 } else if (num < 1000) {
1138 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1141 } else if (num < 2000) {
1142 snprintf(fn, sizeof(fn), "digits/thousand");
1144 } else if (num < 1000000) {
1145 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1148 snprintf(fn, sizeof(fn), "digits/thousand");
1150 } else if (num < 1000000000) {
1151 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1154 snprintf(fn, sizeof(fn), "digits/million");
1155 num = num % 1000000;
1157 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1161 if(!ast_streamfile(chan, fn, language)) {
1162 if ((audiofd > -1) && (ctrlfd > -1))
1163 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1165 res = ast_waitstream(chan, ints);
1167 ast_stopstream(chan);
1175 /*! \brief ast_say_number_full_he: Hebrew syntax */
1176 /* Extra sounds needed:
1180 2thousands: 2 thousand
1181 thousands: plural of 'thousand'
1182 3sF 'Smichut forms (female)
1189 3s 'Smichut' forms (male)
1206 TODO: 've' should sometimed be 'hu':
1207 * before 'shtaym' (2, F)
1208 * before 'shnaym' (2, M)
1209 * before 'shlosha' (3, M)
1210 * before 'shmone' (8, M)
1211 * before 'shlosim' (30)
1212 * before 'shmonim' (80)
1218 #define SAY_NUM_BUF_SIZE 256
1219 static int ast_say_number_full_he(struct ast_channel *chan, int num,
1220 const char *ints, const char *language, const char *options,
1221 int audiofd, int ctrlfd)
1224 int state = 0; /* no need to save anything */
1225 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1226 char fn[SAY_NUM_BUF_SIZE] = "";
1227 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. "
1228 "num: %d, options=\"%s\"\n",
1232 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1234 if (options && !strncasecmp(options, "f",1))
1237 /* Do we have work to do? */
1238 while(!res && (num || (state>0) )) {
1239 /* first type of work: play a second sound. In this loop
1240 * we can only play one sound file at a time. Thus playing
1241 * a second one requires repeating the loop just for the
1242 * second file. The variable 'state' remembers where we were.
1243 * state==0 is the normal mode and it means that we continue
1244 * to check if the number num has yet anything left.
1246 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, "
1247 "state=%d, options=\"%s\", mf=%d\n",
1248 num, state, options, mf
1251 snprintf(fn, sizeof(fn), "digits/hundred");
1253 } else if (state==2) {
1254 snprintf(fn, sizeof(fn), "digits/ve");
1256 } else if (state==3) {
1257 snprintf(fn, sizeof(fn), "digits/thousands");
1259 } else if (num <21) {
1261 snprintf(fn, sizeof(fn), "digits/%dF", num);
1263 snprintf(fn, sizeof(fn), "digits/%d", num);
1265 } else if (num < 100) {
1266 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1269 } else if (num < 200) {
1270 snprintf(fn, sizeof(fn), "digits/hundred");
1272 } else if (num < 300) {
1273 snprintf(fn, sizeof(fn), "digits/hundred");
1275 } else if (num < 1000) {
1276 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1279 } else if (num < 2000) {
1280 snprintf(fn, sizeof(fn), "digits/thousand");
1282 } else if (num < 3000) {
1283 snprintf(fn, sizeof(fn), "digits/2thousand");
1286 } else if (num < 20000) {
1287 snprintf(fn, sizeof(fn), "digits/%ds",(num/1000));
1290 } else if (num < 1000000) {
1291 res = ast_say_number_full_he(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1294 snprintf(fn, sizeof(fn), "digits/thousand");
1296 } else if (num < 1000000000) {
1297 res = ast_say_number_full_he(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1300 snprintf(fn, sizeof(fn), "digits/million");
1301 num = num % 1000000;
1303 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1307 if(!ast_streamfile(chan, fn, language)) {
1308 if ((audiofd > -1) && (ctrlfd > -1))
1309 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1311 res = ast_waitstream(chan, ints);
1313 ast_stopstream(chan);
1319 /*! \brief ast_say_number_full_it: Italian */
1320 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1328 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1333 Like english, numbers up to 20 are a single 'word', and others
1334 compound, but with exceptions.
1335 For example 21 is not twenty-one, but there is a single word in 'it'.
1336 Idem for 28 (ie when a the 2nd part of a compund number
1337 starts with a vowel)
1339 There are exceptions also for hundred, thousand and million.
1340 In english 100 = one hundred, 200 is two hundred.
1341 In italian 100 = cento , like to say hundred (without one),
1342 200 and more are like english.
1344 Same applies for thousand:
1345 1000 is one thousand in en, 2000 is two thousand.
1346 In it we have 1000 = mille , 2000 = 2 mila
1348 For million(s) we use the plural, if more than one
1349 Also, one million is abbreviated in it, like on-million,
1350 or 'un milione', not 'uno milione'.
1351 So the right file is provided.
1354 while(!res && (num || playh)) {
1356 snprintf(fn, sizeof(fn), "digits/minus");
1357 if ( num > INT_MIN ) {
1363 snprintf(fn, sizeof(fn), "digits/hundred");
1365 } else if (num < 20) {
1366 snprintf(fn, sizeof(fn), "digits/%d", num);
1368 } else if (num == 21) {
1369 snprintf(fn, sizeof(fn), "digits/%d", num);
1371 } else if (num == 28) {
1372 snprintf(fn, sizeof(fn), "digits/%d", num);
1374 } else if (num == 31) {
1375 snprintf(fn, sizeof(fn), "digits/%d", num);
1377 } else if (num == 38) {
1378 snprintf(fn, sizeof(fn), "digits/%d", num);
1380 } else if (num == 41) {
1381 snprintf(fn, sizeof(fn), "digits/%d", num);
1383 } else if (num == 48) {
1384 snprintf(fn, sizeof(fn), "digits/%d", num);
1386 } else if (num == 51) {
1387 snprintf(fn, sizeof(fn), "digits/%d", num);
1389 } else if (num == 58) {
1390 snprintf(fn, sizeof(fn), "digits/%d", num);
1392 } else if (num == 61) {
1393 snprintf(fn, sizeof(fn), "digits/%d", num);
1395 } else if (num == 68) {
1396 snprintf(fn, sizeof(fn), "digits/%d", num);
1398 } else if (num == 71) {
1399 snprintf(fn, sizeof(fn), "digits/%d", num);
1401 } else if (num == 78) {
1402 snprintf(fn, sizeof(fn), "digits/%d", num);
1404 } else if (num == 81) {
1405 snprintf(fn, sizeof(fn), "digits/%d", num);
1407 } else if (num == 88) {
1408 snprintf(fn, sizeof(fn), "digits/%d", num);
1410 } else if (num == 91) {
1411 snprintf(fn, sizeof(fn), "digits/%d", num);
1413 } else if (num == 98) {
1414 snprintf(fn, sizeof(fn), "digits/%d", num);
1416 } else if (num < 100) {
1417 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1418 num -= ((num / 10) * 10);
1421 if ((num / 100) > 1) {
1422 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1425 snprintf(fn, sizeof(fn), "digits/hundred");
1427 num -= ((num / 100) * 100);
1429 if (num < 1000000) { /* 1,000,000 */
1431 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1436 if ((tempnum / 1000) < 2)
1437 snprintf(fn, sizeof(fn), "digits/thousand");
1438 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1439 snprintf(fn, sizeof(fn), "digits/thousands");
1441 if (num < 1000000000) { /* 1,000,000,000 */
1442 if ((num / 1000000) > 1)
1443 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1447 num = num % 1000000;
1448 if ((tempnum / 1000000) < 2)
1449 snprintf(fn, sizeof(fn), "digits/million");
1451 snprintf(fn, sizeof(fn), "digits/millions");
1453 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1460 if(!ast_streamfile(chan, fn, language)) {
1461 if ((audiofd > -1) && (ctrlfd > -1))
1462 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1464 res = ast_waitstream(chan, ints);
1466 ast_stopstream(chan);
1472 /*! \brief ast_say_number_full_nl: dutch syntax */
1473 /* New files: digits/nl-en
1475 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1482 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1483 while (!res && (num || playh )) {
1485 snprintf(fn, sizeof(fn), "digits/minus");
1486 if ( num > INT_MIN ) {
1492 snprintf(fn, sizeof(fn), "digits/hundred");
1494 } else if (num < 20) {
1495 snprintf(fn, sizeof(fn), "digits/%d", num);
1497 } else if (num < 100) {
1500 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1504 snprintf(fn, sizeof(fn), "digits/nl-en");
1506 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1511 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1513 num -= ((num / 100) * 100);
1515 if (num < 1000000) { /* 1,000,000 */
1516 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1520 snprintf(fn, sizeof(fn), "digits/thousand");
1522 if (num < 1000000000) { /* 1,000,000,000 */
1523 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1526 num = num % 1000000;
1527 snprintf(fn, sizeof(fn), "digits/million");
1529 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1537 if(!ast_streamfile(chan, fn, language)) {
1538 if ((audiofd > -1) && (ctrlfd > -1))
1539 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1541 res = ast_waitstream(chan, ints);
1543 ast_stopstream(chan);
1549 /*! \brief ast_say_number_full_no: Norwegian syntax */
1551 In addition to American English, the following sounds are required: "and", "1N"
1553 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)
1558 int cn = 1; /* +1 = commune; -1 = neuter */
1562 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1564 if (options && !strncasecmp(options, "n",1)) cn = -1;
1566 while(!res && (num || playh || playa )) {
1567 /* The grammar for Norwegian numbers is the same as for English except
1568 * for the following:
1569 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1570 * "and" before the last two digits, i.e. 2034 is "two thousand and
1571 * thirty-four" and 1000012 is "one million and twelve".
1574 snprintf(fn, sizeof(fn), "digits/minus");
1575 if ( num > INT_MIN ) {
1581 snprintf(fn, sizeof(fn), "digits/hundred");
1584 snprintf(fn, sizeof(fn), "digits/and");
1586 } else if (num == 1 && cn == -1) {
1587 snprintf(fn, sizeof(fn), "digits/1N");
1589 } else if (num < 20) {
1590 snprintf(fn, sizeof(fn), "digits/%d", num);
1592 } else if (num < 100) {
1593 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1594 num -= ((num / 10) * 10);
1595 } else if (num < 1000) {
1596 int hundreds = num / 100;
1598 snprintf(fn, sizeof(fn), "digits/1N");
1600 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1603 num -= 100 * hundreds;
1606 } else if (num < 1000000) {
1607 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1610 snprintf(fn, sizeof(fn), "digits/thousand");
1612 if (num && num < 100)
1614 } else if (num < 1000000000) {
1615 int millions = num / 1000000;
1616 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1619 snprintf(fn, sizeof(fn), "digits/million");
1620 num = num % 1000000;
1621 if (num && num < 100)
1624 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1629 if(!ast_streamfile(chan, fn, language)) {
1630 if ((audiofd > -1) && (ctrlfd > -1))
1631 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1633 res = ast_waitstream(chan, ints);
1635 ast_stopstream(chan);
1642 char *separator_dziesiatek;
1646 char *dziesiatki[10];
1651 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1657 return odm->rzedy[rzad - 1][0];
1658 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1659 return odm->rzedy[rzad - 1][1];
1661 return odm->rzedy[rzad - 1][2];
1664 static char* pl_append(char* buffer, char* str)
1666 strcpy(buffer, str);
1667 buffer += strlen(str);
1671 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1673 char file_name[255] = "digits/";
1674 strcat(file_name, fn);
1675 ast_log(LOG_DEBUG, "Trying to play: %s\n", file_name);
1676 if (!ast_streamfile(chan, file_name, language)) {
1677 if ((audiofd > -1) && (ctrlfd > -1))
1678 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1680 ast_waitstream(chan, ints);
1682 ast_stopstream(chan);
1685 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1687 /* Initialise variables to allow compilation on Debian-stable, etc */
1697 if (i == 0 && rzad > 0) {
1701 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1704 m1000E6 = i % 1000000000;
1705 i1000E6 = i / 1000000000;
1707 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1709 m1000E3 = m1000E6 % 1000000;
1710 i1000E3 = m1000E6 / 1000000;
1712 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1714 m1000 = m1000E3 % 1000;
1715 i1000 = m1000E3 / 1000;
1717 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1723 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1725 if ( m100 > 0 && m100 <=9 ) {
1727 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1729 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1730 } else if (m100 % 10 == 0) {
1731 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1732 } else if (m100 <= 19 ) {
1733 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1734 } else if (m100 != 0) {
1735 if (odm->separator_dziesiatek[0]==' ') {
1736 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1737 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1741 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1742 b = pl_append(b, odm->separator_dziesiatek);
1743 b = pl_append(b, odm->cyfry2[m100 % 10]);
1744 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1749 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1753 /* ast_say_number_full_pl: Polish syntax */
1754 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)
1764 1000000000.2 miliardy
1765 1000000000.5 miliardow
1829 70m siedemdziesieciu
1841 90m dziewiedziesieciu
1843 and combinations of eg.: 20_1, 30m_3m, etc...
1847 char *zenski_cyfry[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1849 char *zenski_cyfry2[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1851 char *meski_cyfry[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1853 char *meski_cyfry2[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1855 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1857 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1859 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1861 char *nijaki_cyfry[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1863 char *nijaki_cyfry2[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1865 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1867 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1869 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1871 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1873 /* Initialise variables to allow compilation on Debian-stable, etc */
1876 static odmiana *odmiana_nieosobowa = NULL;
1877 static odmiana *odmiana_meska = NULL;
1878 static odmiana *odmiana_zenska = NULL;
1880 if (odmiana_nieosobowa == NULL) {
1881 odmiana_nieosobowa = (odmiana *) malloc(sizeof(odmiana));
1883 odmiana_nieosobowa->separator_dziesiatek = "_";
1885 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
1886 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
1887 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
1888 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
1889 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
1890 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
1893 if (odmiana_zenska == NULL) {
1894 odmiana_zenska = (odmiana *) malloc(sizeof(odmiana));
1896 odmiana_zenska->separator_dziesiatek = " ";
1898 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
1899 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
1900 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
1901 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
1902 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
1903 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
1906 if (odmiana_meska == NULL) {
1907 odmiana_meska = (odmiana *) malloc(sizeof(odmiana));
1909 odmiana_meska->separator_dziesiatek = " ";
1911 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
1912 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
1913 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
1914 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
1915 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
1916 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
1920 if (strncasecmp(options, "f", 1) == 0)
1922 else if (strncasecmp(options, "m", 1) == 0)
1925 o = odmiana_nieosobowa;
1927 o = odmiana_nieosobowa;
1929 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
1933 /* ast_say_number_full_pt: Portuguese syntax */
1934 /* Extra sounds needed: */
1935 /* For feminin all sound files end with F */
1936 /* 100E for 100+ something */
1937 /* 1000000S for plural */
1938 /* pt-e for 'and' */
1939 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)
1943 int mf = 1; /* +1 = male; -1 = female */
1947 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1949 if (options && !strncasecmp(options, "f",1))
1952 while(!res && num ) {
1954 snprintf(fn, sizeof(fn), "digits/minus");
1955 if ( num > INT_MIN ) {
1960 } else if (num < 20) {
1961 if ((num == 1 || num == 2) && (mf < 0))
1962 snprintf(fn, sizeof(fn), "digits/%dF", num);
1964 snprintf(fn, sizeof(fn), "digits/%d", num);
1966 } else if (num < 100) {
1967 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1971 } else if (num < 1000) {
1973 snprintf(fn, sizeof(fn), "digits/100");
1975 snprintf(fn, sizeof(fn), "digits/100E");
1977 if (mf < 0 && num > 199)
1978 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1980 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1985 } else if (num < 1000000) {
1987 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1991 snprintf(fn, sizeof(fn), "digits/1000");
1992 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
1995 } else if (num < 1000000000) {
1996 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2000 snprintf(fn, sizeof(fn), "digits/1000000");
2002 snprintf(fn, sizeof(fn), "digits/1000000S");
2004 if ((num % 1000000) &&
2006 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2007 /* no hundreds and below */
2008 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2010 num = num % 1000000;
2013 if (!ast_streamfile(chan, fn, language)) {
2014 if ((audiofd > -1) && (ctrlfd > -1))
2015 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2017 res = ast_waitstream(chan, ints);
2019 ast_stopstream(chan);
2021 if (!res && playh) {
2022 res = wait_file(chan, ints, "digits/pt-e", language);
2023 ast_stopstream(chan);
2030 /*! \brief ast_say_number_full_se: Swedish syntax */
2031 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)
2036 int cn = 1; /* +1 = commune; -1 = neuter */
2038 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2039 if (options && !strncasecmp(options, "n",1)) cn = -1;
2041 while(!res && (num || playh)) {
2043 snprintf(fn, sizeof(fn), "digits/minus");
2044 if ( num > INT_MIN ) {
2050 snprintf(fn, sizeof(fn), "digits/hundred");
2052 } else if (num < 20) {
2053 snprintf(fn, sizeof(fn), "digits/%d", num);
2055 } else if (num < 100) {
2056 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2057 num -= ((num / 10) * 10);
2058 } else if (num == 1 && cn == -1) { /* En eller ett? */
2059 snprintf(fn, sizeof(fn), "digits/1N");
2063 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2065 num -= ((num / 100) * 100);
2067 if (num < 1000000) { /* 1,000,000 */
2068 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
2073 snprintf(fn, sizeof(fn), "digits/thousand");
2075 if (num < 1000000000) { /* 1,000,000,000 */
2076 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
2080 num = num % 1000000;
2081 snprintf(fn, sizeof(fn), "digits/million");
2083 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2090 if(!ast_streamfile(chan, fn, language)) {
2091 if ((audiofd > -1) && (ctrlfd > -1))
2092 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2094 res = ast_waitstream(chan, ints);
2095 ast_stopstream(chan);
2102 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2103 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2109 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2111 while(!res && (num || playh)) {
2113 snprintf(fn, sizeof(fn), "digits/minus");
2114 if ( num > INT_MIN ) {
2120 snprintf(fn, sizeof(fn), "digits/hundred");
2122 } else if (num < 10) {
2123 snprintf(fn, sizeof(fn), "digits/%d", num);
2125 } else if (num < 100) {
2126 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2127 num -= ((num / 10) * 10);
2130 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2132 num -= ((num / 100) * 100);
2134 if (num < 1000000) { /* 1,000,000 */
2135 res = ast_say_number_full_tw(chan, num / 1000, ints, language, audiofd, ctrlfd);
2139 snprintf(fn, sizeof(fn), "digits/thousand");
2141 if (num < 1000000000) { /* 1,000,000,000 */
2142 res = ast_say_number_full_tw(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2145 num = num % 1000000;
2146 snprintf(fn, sizeof(fn), "digits/million");
2148 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2155 if(!ast_streamfile(chan, fn, language)) {
2156 if ((audiofd > -1) && (ctrlfd > -1))
2157 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2159 res = ast_waitstream(chan, ints);
2161 ast_stopstream(chan);
2168 /*! \brief determine last digits for thousands/millions (ru) */
2169 static int get_lastdigits_ru(int num) {
2172 } else if (num < 100) {
2173 return get_lastdigits_ru(num % 10);
2174 } else if (num < 1000) {
2175 return get_lastdigits_ru(num % 100);
2177 return 0; /* number too big */
2181 /*! \brief ast_say_number_full_ru: Russian syntax */
2182 /*! \brief additional files:
2183 n00.gsm (one hundred, two hundred, ...)
2186 thousands-i.gsm (tisyachi)
2187 million-a.gsm (milliona)
2193 where 'n' from 1 to 9
2195 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)
2201 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2203 while(!res && (num)) {
2205 snprintf(fn, sizeof(fn), "digits/minus");
2206 if ( num > INT_MIN ) {
2211 } else if (num < 20) {
2212 if(options && strlen(options) == 1 && num < 3) {
2213 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2215 snprintf(fn, sizeof(fn), "digits/%d", num);
2218 } else if (num < 100) {
2219 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2221 } else if (num < 1000){
2222 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2224 } else if (num < 1000000) { /* 1,000,000 */
2225 lastdigits = get_lastdigits_ru(num / 1000);
2227 if (lastdigits < 3) {
2228 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2230 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2234 if (lastdigits == 1) {
2235 snprintf(fn, sizeof(fn), "digits/thousand");
2236 } else if (lastdigits > 1 && lastdigits < 5) {
2237 snprintf(fn, sizeof(fn), "digits/thousands-i");
2239 snprintf(fn, sizeof(fn), "digits/thousands");
2242 } else if (num < 1000000000) { /* 1,000,000,000 */
2243 lastdigits = get_lastdigits_ru(num / 1000000);
2245 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2248 if (lastdigits == 1) {
2249 snprintf(fn, sizeof(fn), "digits/million");
2250 } else if (lastdigits > 1 && lastdigits < 5) {
2251 snprintf(fn, sizeof(fn), "digits/million-a");
2253 snprintf(fn, sizeof(fn), "digits/millions");
2257 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2261 if (!ast_streamfile(chan, fn, language)) {
2262 if ((audiofd > -1) && (ctrlfd > -1))
2263 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2265 res = ast_waitstream(chan, ints);
2267 ast_stopstream(chan);
2274 /*! \brief ast_say_enumeration_full: call language-specific functions */
2275 /* Called from AGI */
2276 int ast_say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2278 if (!strcasecmp(language,"en") ) { /* English syntax */
2279 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2280 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
2281 return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
2282 } else if (!strcasecmp(language, "de") ) { /* German syntax */
2283 return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
2286 /* Default to english */
2287 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2290 /*! \brief ast_say_enumeration: call language-specific functions without file descriptors */
2291 int ast_say_enumeration(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options)
2293 return(ast_say_enumeration_full(chan, num, ints, language, options, -1, -1));
2296 /*! \brief ast_say_enumeration_full_en: English syntax */
2297 /* This is the default syntax, if no other syntax defined in this file is used */
2298 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2303 while(!res && num) {
2305 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2306 if ( num > INT_MIN ) {
2311 } else if (num < 20) {
2312 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2314 } else if (num < 100) {
2315 int tens = num / 10;
2318 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2320 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2322 } else if (num < 1000) {
2323 int hundreds = num / 100;
2325 if (hundreds > 1 || t == 1) {
2326 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2331 snprintf(fn, sizeof(fn), "digits/hundred");
2333 snprintf(fn, sizeof(fn), "digits/h-hundred");
2335 } else if (num < 1000000) {
2336 int thousands = num / 1000;
2338 if (thousands > 1 || t == 1) {
2339 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2344 snprintf(fn, sizeof(fn), "digits/thousand");
2346 snprintf(fn, sizeof(fn), "digits/h-thousand");
2349 } else if (num < 1000000000) {
2350 int millions = num / 1000000;
2351 num = num % 1000000;
2353 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2357 snprintf(fn, sizeof(fn), "digits/million");
2359 snprintf(fn, sizeof(fn), "digits/h-million");
2361 } else if (num < INT_MAX) {
2362 int billions = num / 1000000000;
2363 num = num % 1000000000;
2365 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2369 snprintf(fn, sizeof(fn), "digits/billion");
2371 snprintf(fn, sizeof(fn), "digits/h-billion");
2373 } else if (num == INT_MAX) {
2374 snprintf(fn, sizeof(fn), "digits/h-last");
2377 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2382 if (!ast_streamfile(chan, fn, language)) {
2383 if ((audiofd > -1) && (ctrlfd > -1)) {
2384 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2386 res = ast_waitstream(chan, ints);
2389 ast_stopstream(chan);
2395 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2396 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)
2398 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2400 char fn[256] = "", fna[256] = "";
2403 if (options && !strncasecmp(options, "f",1)) {
2405 } else if (options && !strncasecmp(options, "n",1)) {
2412 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2414 while(!res && num) {
2416 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2417 if ( num > INT_MIN ) {
2422 } else if (num < 100 && t) {
2423 snprintf(fn, sizeof(fn), "digits/and");
2425 } else if (num < 20) {
2426 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2428 } else if (num < 100) {
2429 int ones = num % 10;
2431 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2434 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2437 } else if (num == 100 && t == 0) {
2438 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2440 } else if (num < 1000) {
2441 int hundreds = num / 100;
2443 if (hundreds == 1) {
2444 snprintf(fn, sizeof(fn), "digits/1N");
2446 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2449 snprintf(fna, sizeof(fna), "digits/hundred");
2451 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2454 } else if (num < 1000000) {
2455 int thousands = num / 1000;
2457 if (thousands == 1) {
2459 snprintf(fn, sizeof(fn), "digits/1N");
2460 snprintf(fna, sizeof(fna), "digits/thousand");
2463 snprintf(fn, sizeof(fn), "digits/1N");
2464 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2466 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2470 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2475 snprintf(fn, sizeof(fn), "digits/thousand");
2477 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2481 } else if (num < 1000000000) {
2482 int millions = num / 1000000;
2483 num = num % 1000000;
2484 if (millions == 1) {
2486 snprintf(fn, sizeof(fn), "digits/1F");
2487 snprintf(fna, sizeof(fna), "digits/million");
2489 snprintf(fn, sizeof(fn), "digits/1N");
2490 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2493 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2498 snprintf(fn, sizeof(fn), "digits/millions");
2500 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2504 } else if (num < INT_MAX) {
2505 int billions = num / 1000000000;
2506 num = num % 1000000000;
2507 if (billions == 1) {
2509 snprintf(fn, sizeof(fn), "digits/1F");
2510 snprintf(fna, sizeof(fna), "digits/milliard");
2512 snprintf(fn, sizeof(fn), "digits/1N");
2513 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2516 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2520 snprintf(fn, sizeof(fna), "digits/milliards");
2522 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2526 } else if (num == INT_MAX) {
2527 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2530 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2535 if (!ast_streamfile(chan, fn, language)) {
2536 if ((audiofd > -1) && (ctrlfd > -1))
2537 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2539 res = ast_waitstream(chan, ints);
2541 ast_stopstream(chan);
2543 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2544 if ((audiofd > -1) && (ctrlfd > -1)) {
2545 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2547 res = ast_waitstream(chan, ints);
2550 ast_stopstream(chan);
2558 /*! \brief ast_say_enumeration_full_de: German syntax */
2559 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)
2561 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2563 char fn[256] = "", fna[256] = "";
2566 if (options && !strncasecmp(options, "f",1)) {
2568 } else if (options && !strncasecmp(options, "n",1)) {
2575 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2577 while(!res && num) {
2579 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2580 if ( num > INT_MIN ) {
2585 } else if (num < 100 && t) {
2586 snprintf(fn, sizeof(fn), "digits/and");
2588 } else if (num < 20) {
2589 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2591 } else if (num < 100) {
2592 int ones = num % 10;
2594 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2597 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2600 } else if (num == 100 && t == 0) {
2601 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2603 } else if (num < 1000) {
2604 int hundreds = num / 100;
2606 if (hundreds == 1) {
2607 snprintf(fn, sizeof(fn), "digits/1N");
2609 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2612 snprintf(fna, sizeof(fna), "digits/hundred");
2614 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2617 } else if (num < 1000000) {
2618 int thousands = num / 1000;
2620 if (thousands == 1) {
2622 snprintf(fn, sizeof(fn), "digits/1N");
2623 snprintf(fna, sizeof(fna), "digits/thousand");
2626 snprintf(fn, sizeof(fn), "digits/1N");
2627 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2629 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2633 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2638 snprintf(fn, sizeof(fn), "digits/thousand");
2640 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2644 } else if (num < 1000000000) {
2645 int millions = num / 1000000;
2646 num = num % 1000000;
2647 if (millions == 1) {
2649 snprintf(fn, sizeof(fn), "digits/1F");
2650 snprintf(fna, sizeof(fna), "digits/million");
2652 snprintf(fn, sizeof(fn), "digits/1N");
2653 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2656 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2661 snprintf(fn, sizeof(fn), "digits/millions");
2663 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2667 } else if (num < INT_MAX) {
2668 int billions = num / 1000000000;
2669 num = num % 1000000000;
2670 if (billions == 1) {
2672 snprintf(fn, sizeof(fn), "digits/1F");
2673 snprintf(fna, sizeof(fna), "digits/milliard");
2675 snprintf(fn, sizeof(fn), "digits/1N");
2676 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2679 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2683 snprintf(fn, sizeof(fna), "digits/milliards");
2685 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2689 } else if (num == INT_MAX) {
2690 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2693 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2698 if (!ast_streamfile(chan, fn, language)) {
2699 if ((audiofd > -1) && (ctrlfd > -1))
2700 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2702 res = ast_waitstream(chan, ints);
2704 ast_stopstream(chan);
2706 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2707 if ((audiofd > -1) && (ctrlfd > -1)) {
2708 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2710 res = ast_waitstream(chan, ints);
2713 ast_stopstream(chan);
2721 int ast_say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2723 if (!strcasecmp(lang, "en") ) { /* English syntax */
2724 return(ast_say_date_en(chan, t, ints, lang));
2725 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2726 return(ast_say_date_da(chan, t, ints, lang));
2727 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2728 return(ast_say_date_de(chan, t, ints, lang));
2729 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2730 return(ast_say_date_fr(chan, t, ints, lang));
2731 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2732 return(ast_say_date_nl(chan, t, ints, lang));
2733 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2734 return(ast_say_date_pt(chan, t, ints, lang));
2735 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2736 return(ast_say_date_gr(chan, t, ints, lang));
2739 /* Default to English */
2740 return(ast_say_date_en(chan, t, ints, lang));
2743 /* English syntax */
2744 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2749 ast_localtime(&t,&tm,NULL);
2751 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2752 res = ast_streamfile(chan, fn, lang);
2754 res = ast_waitstream(chan, ints);
2757 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2758 res = ast_streamfile(chan, fn, lang);
2760 res = ast_waitstream(chan, ints);
2763 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2765 res = ast_waitstream(chan, ints);
2767 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2772 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2777 ast_localtime(&t,&tm,NULL);
2779 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2780 res = ast_streamfile(chan, fn, lang);
2782 res = ast_waitstream(chan, ints);
2785 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2787 res = ast_waitstream(chan, ints);
2789 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2790 res = ast_streamfile(chan, fn, lang);
2792 res = ast_waitstream(chan, ints);
2796 int year = tm.tm_year + 1900;
2797 if (year > 1999) { /* year 2000 and later */
2798 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2801 /* I'm not going to handle 1100 and prior */
2802 /* We'll just be silent on the year, instead of bombing out. */
2804 /* year 1100 to 1999. will anybody need this?!? */
2805 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2806 res = wait_file(chan, ints, fn, lang);
2808 res = wait_file(chan,ints, "digits/hundred", lang);
2809 if (!res && year % 100 != 0) {
2810 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2820 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2825 ast_localtime(&t,&tm,NULL);
2827 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2828 res = ast_streamfile(chan, fn, lang);
2830 res = ast_waitstream(chan, ints);
2833 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2835 res = ast_waitstream(chan, ints);
2837 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2838 res = ast_streamfile(chan, fn, lang);
2840 res = ast_waitstream(chan, ints);
2844 int year = tm.tm_year + 1900;
2845 if (year > 1999) { /* year 2000 and later */
2846 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2849 /* I'm not going to handle 1100 and prior */
2850 /* We'll just be silent on the year, instead of bombing out. */
2852 /* year 1100 to 1999. will anybody need this?!? */
2853 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2854 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2855 res = wait_file(chan, ints, fn, lang);
2857 res = wait_file(chan,ints, "digits/hundred", lang);
2858 if (!res && year % 100 != 0) {
2859 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2869 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2874 ast_localtime(&t,&tm,NULL);
2876 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2877 res = ast_streamfile(chan, fn, lang);
2879 res = ast_waitstream(chan, ints);
2882 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2884 res = ast_waitstream(chan, ints);
2886 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2887 res = ast_streamfile(chan, fn, lang);
2889 res = ast_waitstream(chan, ints);
2892 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2897 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2902 ast_localtime(&t,&tm,NULL);
2904 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2905 res = ast_streamfile(chan, fn, lang);
2907 res = ast_waitstream(chan, ints);
2910 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2912 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2913 res = ast_streamfile(chan, fn, lang);
2915 res = ast_waitstream(chan, ints);
2918 res = ast_waitstream(chan, ints);
2920 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2924 /* Portuguese syntax */
2925 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2930 ast_localtime(&t,&tm,NULL);
2931 localtime_r(&t,&tm);
2932 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2934 res = wait_file(chan, ints, fn, lang);
2936 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2938 res = wait_file(chan, ints, "digits/pt-de", lang);
2939 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2941 res = wait_file(chan, ints, fn, lang);
2943 res = wait_file(chan, ints, "digits/pt-de", lang);
2945 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2950 int ast_say_date_with_format(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
2952 /* If no format is given, use default english format */
2954 format = "ABdY 'digits/at' IMp";
2956 if (!strcasecmp(lang, "en") ) { /* English syntax */
2957 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2958 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2959 return(ast_say_date_with_format_da(chan, time, ints, lang, format, timezone));
2960 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2961 return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
2962 } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) { /* Spanish syntax */
2963 return(ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
2964 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
2965 return(ast_say_date_with_format_he(chan, time, ints, lang, format, timezone));
2966 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2967 return(ast_say_date_with_format_fr(chan, time, ints, lang, format, timezone));
2968 } else if (!strcasecmp(lang, "it") ) { /* Italian syntax */
2969 return(ast_say_date_with_format_it(chan, time, ints, lang, format, timezone));
2970 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2971 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
2972 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2973 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
2974 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
2975 return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
2976 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2977 return(ast_say_date_with_format_gr(chan, time, ints, lang, format, timezone));
2980 /* Default to English */
2981 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2984 /* English syntax */
2985 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)
2988 int res=0, offset, sndoffset;
2989 char sndfile[256], nextmsg[256];
2991 ast_localtime(&time,&tm,timezone);
2993 for (offset=0 ; format[offset] != '\0' ; offset++) {
2994 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2995 switch (format[offset]) {
2996 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2998 /* Literal name of a sound file */
3000 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
3001 sndfile[sndoffset] = format[offset];
3002 sndfile[sndoffset] = '\0';
3003 res = wait_file(chan,ints,sndfile,lang);
3007 /* Sunday - Saturday */
3008 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3009 res = wait_file(chan,ints,nextmsg,lang);
3014 /* January - December */
3015 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3016 res = wait_file(chan,ints,nextmsg,lang);
3019 /* Month enumerated */
3020 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
3024 /* First - Thirtyfirst */
3025 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
3029 if (tm.tm_year > 99) {
3030 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3031 } else if (tm.tm_year < 1) {
3032 /* I'm not going to handle 1900 and prior */
3033 /* We'll just be silent on the year, instead of bombing out. */
3035 res = wait_file(chan, ints, "digits/19", lang);
3037 if (tm.tm_year <= 9) {
3039 res = wait_file(chan,ints, "digits/oh", lang);
3042 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
3049 if (tm.tm_hour == 0)
3050 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3051 else if (tm.tm_hour > 12)
3052 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3054 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3055 res = wait_file(chan,ints,nextmsg,lang);
3060 if (format[offset] == 'H') {
3062 if (tm.tm_hour < 10) {
3063 res = wait_file(chan,ints, "digits/oh",lang);
3067 if (tm.tm_hour == 0) {
3068 res = wait_file(chan,ints, "digits/oh",lang);
3072 if (tm.tm_hour != 0) {
3073 int remainder = tm.tm_hour;
3074 if (tm.tm_hour > 20) {
3075 res = wait_file(chan,ints, "digits/20",lang);
3079 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
3080 res = wait_file(chan,ints,nextmsg,lang);
3088 if (tm.tm_min == 0) {
3089 if (format[offset] == 'M') {
3090 res = wait_file(chan, ints, "digits/oclock", lang);
3092 res = wait_file(chan, ints, "digits/hundred", lang);
3094 } else if (tm.tm_min < 10) {
3095 res = wait_file(chan,ints, "digits/oh",lang);
3097 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
3098 res = wait_file(chan,ints,nextmsg,lang);
3101 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3107 if (tm.tm_hour > 11)
3108 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3110 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3111 res = wait_file(chan,ints,nextmsg,lang);
3114 /* Shorthand for "Today", "Yesterday", or ABdY */
3115 /* XXX As emphasized elsewhere, this should the native way in your
3116 * language to say the date, with changes in what you say, depending
3117 * upon how recent the date is. XXX */
3121 time_t beg_today, tt;
3123 gettimeofday(&now,NULL);
3125 ast_localtime(&tt,&tmnow,timezone);
3126 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3127 /* In any case, it saves not having to do ast_mktime() */
3128 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3129 if (beg_today < time) {
3131 res = wait_file(chan,ints, "digits/today",lang);
3132 } else if (beg_today - 86400 < time) {
3134 res = wait_file(chan,ints, "digits/yesterday",lang);
3135 } else if (beg_today - 86400 * 6 < time) {
3136 /* Within the last week */
3137 res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
3138 } else if (beg_today - 2628000 < time) {
3139 /* Less than a month ago - "Sunday, October third" */
3140 res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
3141 } else if (beg_today - 15768000 < time) {
3142 /* Less than 6 months ago - "August seventh" */
3143 res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
3145 /* More than 6 months ago - "April nineteenth two thousand three" */
3146 res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
3151 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3152 /* XXX As emphasized elsewhere, this should the native way in your
3153 * language to say the date, with changes in what you say, depending
3154 * upon how recent the date is. XXX */
3158 time_t beg_today, tt;
3160 gettimeofday(&now,NULL);
3162 ast_localtime(&tt,&tmnow,timezone);
3163 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3164 /* In any case, it saves not having to do ast_mktime() */
3165 beg_today = tt - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
3166 if (beg_today < time) {