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>
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34 #include <sys/types.h>
37 #include <netinet/in.h>
44 #include <iso/limits_iso.h>
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_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
153 fn = "letters/exclaimation-point";
159 fn = "letters/dollar";
168 fn = "letters/equals";
174 fn = "letters/slash";
177 fn = "letters/space";
188 strcpy(fnbuf, "digits/X");
192 default: /* '9' falls here... */
194 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
195 strcpy(fnbuf, "phonetic/X_p");
199 res = ast_streamfile(chan, fn, lang);
201 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
202 ast_stopstream(chan);
209 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
216 while (str[num] && !res) {
238 strcpy(fnbuf, "digits/X");
244 res = ast_streamfile(chan, fn, lang);
246 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
247 ast_stopstream(chan);
255 /* Forward declarations */
256 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
257 \note Not really language codes.
258 For these language codes, Asterisk will change the syntax when
259 saying numbers (and in some cases dates and voicemail messages
263 \arg \b en - English (US)
264 \arg \b en_GB - English (British)
265 \arg \b es - Spanish, Mexican
270 \arg \b no - Norwegian
272 \arg \b pt - Portuguese
273 \arg \b pt_BR - Portuguese (Brazil)
275 \arg \b tw - Taiwanese / Chinese
279 For Some languages the numbers differ for gender and plural.
280 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
281 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
282 use the option argument 'p' for plural enumerations like in German
284 Date/Time functions currently have less languages supported than saynumber().
286 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
288 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
291 Portuguese sound files needed for Time/Date functions:
302 Spanish sound files needed for Time/Date functions:
307 Italian sound files needed for Time/Date functions:
313 /* Forward declarations of language specific variants of ast_say_number_full */
314 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
315 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);
316 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);
317 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);
318 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
319 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);
320 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);
321 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);
322 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
323 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
324 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);
325 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);
326 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);
327 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);
328 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
329 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
330 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);
332 /* Forward declarations of language specific variants of ast_say_enumeration_full */
333 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
334 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);
335 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);
337 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
338 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
339 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
340 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
341 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
342 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
343 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
344 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
346 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);
347 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);
348 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);
349 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);
350 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);
351 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);
352 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);
353 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);
354 static int ast_say_date_with_format_pl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
355 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);
356 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);
357 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);
359 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
360 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
361 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
362 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
363 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
364 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
365 static int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
366 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
368 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
369 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
370 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
371 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
372 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
373 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
374 static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
375 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
377 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
378 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
379 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
381 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
384 if ((res = ast_streamfile(chan, file, lang)))
385 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
387 res = ast_waitstream(chan, ints);
391 /*! \brief ast_say_number_full: call language-specific functions */
392 /* Called from AGI */
393 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
395 if (!strcasecmp(language,"en") ) { /* English syntax */
396 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
397 } else if (!strcasecmp(language, "cz") ) { /* Czech syntax */
398 return(ast_say_number_full_cz(chan, num, ints, language, options, audiofd, ctrlfd));
399 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
400 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
401 } else if (!strcasecmp(language, "de") ) { /* German syntax */
402 return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
403 } else if (!strcasecmp(language, "en_GB") ) { /* British syntax */
404 return(ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd));
405 } else if (!strcasecmp(language, "no") ) { /* Norwegian syntax */
406 return(ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd));
407 } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
408 return(ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd));
409 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
410 return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
411 } else if (!strcasecmp(language, "he") ) { /* Hebrew syntax */
412 return(ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
413 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
414 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
415 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
416 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
417 } else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
418 return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
419 } else if (!strcasecmp(language, "pt") || !strcasecmp(language, "pt_BR")) { /* Portuguese syntax */
420 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
421 } else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
422 return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
423 } else if (!strcasecmp(language, "tw") || !strcasecmp(language, "zh") ) { /* Taiwanese / Chinese syntax */
424 return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
425 } else if (!strcasecmp(language, "gr") ) { /* Greek syntax */
426 return(ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd));
427 } else if (!strcasecmp(language, "ru") ) { /* Russian syntax */
428 return(ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd));
431 /* Default to english */
432 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
435 /*! \brief ast_say_number_full_en: English syntax */
436 /* This is the default syntax, if no other syntax defined in this file is used */
437 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
443 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
445 while (!res && (num || playh)) {
447 snprintf(fn, sizeof(fn), "digits/minus");
448 if ( num > INT_MIN ) {
454 snprintf(fn, sizeof(fn), "digits/hundred");
456 } else if (num < 20) {
457 snprintf(fn, sizeof(fn), "digits/%d", num);
459 } else if (num < 100) {
460 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
461 num -= ((num / 10) * 10);
464 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
466 num -= ((num / 100) * 100);
468 if (num < 1000000) { /* 1,000,000 */
469 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
473 snprintf(fn, sizeof(fn), "digits/thousand");
475 if (num < 1000000000) { /* 1,000,000,000 */
476 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
480 snprintf(fn, sizeof(fn), "digits/million");
483 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
490 if (!ast_streamfile(chan, fn, language)) {
491 if ((audiofd > -1) && (ctrlfd > -1))
492 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
494 res = ast_waitstream(chan, ints);
496 ast_stopstream(chan);
502 static int exp10_int(int power)
505 for (x=0;x<power;x++)
510 /*! \brief ast_say_number_full_cz: Czech syntax */
512 * 1m,2m - gender male
513 * 1w,2w - gender female
517 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
519 * for each number 10^(3n + 3) exist 3 files represented as:
520 * 1 tousand = jeden tisic = 1_E3
521 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
522 * 5,6,... tousands = pet,sest,... tisic = 5_E3
528 * tousand, milion are gender male, so 1 and 2 is 1m 2m
529 * miliard is gender female, so 1 and 2 is 1w 2w
531 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)
541 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
546 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
548 while (!res && (num || playh)) {
550 snprintf(fn, sizeof(fn), "digits/minus");
551 if ( num > INT_MIN ) {
556 } else if (num < 3 ) {
557 snprintf(fn, sizeof(fn), "digits/%d%c",num,options[0]);
560 } else if (num < 20) {
561 snprintf(fn, sizeof(fn), "digits/%d",num);
564 } else if (num < 100) {
565 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
566 num -= ((num / 10) * 10);
567 } else if (num < 1000) {
568 hundered = num / 100;
569 if ( hundered == 1 ) {
570 snprintf(fn, sizeof(fn), "digits/1sto");
571 } else if ( hundered == 2 ) {
572 snprintf(fn, sizeof(fn), "digits/2ste");
574 res = ast_say_number_full_cz(chan,hundered,ints,language,options,audiofd,ctrlfd);
577 if (hundered == 3 || hundered == 4) {
578 snprintf(fn, sizeof(fn), "digits/sta");
579 } else if ( hundered > 4 ) {
580 snprintf(fn, sizeof(fn), "digits/set");
583 num -= (hundered * 100);
584 } else { /* num > 1000 */
585 length = (int)log10(num)+1;
586 while ( (length % 3 ) != 1 ) {
589 left = num / (exp10_int(length-1));
592 case 9: options = "w"; /* 1,000,000,000 gender female */
594 default : options = "m"; /* others are male */
597 if ( left > 1 ) { /* we dont say "one thousand" but only thousand */
598 res = ast_say_number_full_cz(chan,left,ints,language,options,audiofd,ctrlfd);
602 if ( left >= 5 ) { /* >= 5 have the same declesion */
603 snprintf(fn, sizeof(fn), "digits/5_E%d",length-1);
604 } else if ( left >= 2 && left <= 4 ) {
605 snprintf(fn, sizeof(fn), "digits/2-4_E%d",length-1);
606 } else { /* left == 1 */
607 snprintf(fn, sizeof(fn), "digits/1_E%d",length-1);
609 num -= left * (exp10_int(length-1));
612 if (!ast_streamfile(chan, fn, language)) {
613 if ((audiofd > -1) && (ctrlfd > -1)) {
614 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
616 res = ast_waitstream(chan, ints);
619 ast_stopstream(chan);
625 /*! \brief ast_say_number_full_da: Danish syntax */
627 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
629 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)
634 int cn = 1; /* +1 = commune; -1 = neuter */
637 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
639 if (options && !strncasecmp(options, "n",1)) cn = -1;
641 while (!res && (num || playh || playa )) {
642 /* The grammar for Danish numbers is the same as for English except
644 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
645 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
646 * "one-and twenty" and 68 is "eight-and sixty".
647 * - "million" is different in singular and plural form
648 * - numbers > 1000 with zero as the third digit from last have an
649 * "and" before the last two digits, i.e. 2034 is "two thousand and
650 * four-and thirty" and 1000012 is "one million and twelve".
653 snprintf(fn, sizeof(fn), "digits/minus");
654 if ( num > INT_MIN ) {
660 snprintf(fn, sizeof(fn), "digits/hundred");
663 snprintf(fn, sizeof(fn), "digits/and");
665 } else if (num == 1 && cn == -1) {
666 snprintf(fn, sizeof(fn), "digits/1N");
668 } else if (num < 20) {
669 snprintf(fn, sizeof(fn), "digits/%d", num);
671 } else if (num < 100) {
674 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
677 snprintf(fn, sizeof(fn), "digits/%d", num);
682 int hundreds = num / 100;
684 snprintf(fn, sizeof(fn), "digits/1N");
686 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
689 num -= 100 * hundreds;
695 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
699 snprintf(fn, sizeof(fn), "digits/thousand");
701 if (num < 1000000000) {
702 int millions = num / 1000000;
703 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
707 snprintf(fn, sizeof(fn), "digits/million");
709 snprintf(fn, sizeof(fn), "digits/millions");
713 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
717 if (num && num < 100)
722 if (!ast_streamfile(chan, fn, language)) {
723 if ((audiofd > -1) && (ctrlfd > -1))
724 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
726 res = ast_waitstream(chan, ints);
728 ast_stopstream(chan);
734 /*! \brief ast_say_number_full_de: German syntax */
736 In addition to English, the following sounds are required:
738 "1-and" through "9-and"
741 NB "1" is recorded as 'eins'
743 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)
746 int mf = 1; /* +1 = male and neuter; -1 = female */
750 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
752 if (options && (!strncasecmp(options, "f",1)))
755 while (!res && num) {
756 /* The grammar for German numbers is the same as for English except
758 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
759 * "one-and twenty" and 68 is "eight-and sixty".
760 * - "one" varies according to gender
761 * - 100 is 'hundert', however all other instances are 'ein hundert'
762 * - 1000 is 'tausend', however all other instances are 'ein tausend'
763 * - 1000000 is always 'eine million'
764 * - "million" is different in singular and plural form
767 snprintf(fn, sizeof(fn), "digits/minus");
768 if ( num > INT_MIN ) {
773 } else if (num < 100 && t) {
774 snprintf(fn, sizeof(fn), "digits/and");
776 } else if (num == 1 && mf == -1) {
777 snprintf(fn, sizeof(fn), "digits/%dF", num);
779 } else if (num < 20) {
780 snprintf(fn, sizeof(fn), "digits/%d", num);
782 } else if (num < 100) {
785 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
788 snprintf(fn, sizeof(fn), "digits/%d", num);
791 } else if (num == 100 && t == 0) {
792 snprintf(fn, sizeof(fn), "digits/hundred");
794 } else if (num < 1000) {
795 int hundreds = num / 100;
798 snprintf(fn, sizeof(fn), "digits/1N");
800 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
802 snprintf(fna, sizeof(fna), "digits/hundred");
804 } else if (num == 1000 && t == 0) {
805 snprintf(fn, sizeof(fn), "digits/thousand");
807 } else if (num < 1000000) {
808 int thousands = num / 1000;
811 if (thousands == 1) {
812 snprintf(fn, sizeof(fn), "digits/1N");
813 snprintf(fna, sizeof(fna), "digits/thousand");
815 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
818 snprintf(fn, sizeof(fn), "digits/thousand");
820 } else if (num < 1000000000) {
821 int millions = num / 1000000;
825 snprintf(fn, sizeof(fn), "digits/1F");
826 snprintf(fna, sizeof(fna), "digits/million");
828 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
831 snprintf(fn, sizeof(fn), "digits/millions");
833 } else if (num <= INT_MAX) {
834 int billions = num / 1000000000;
835 num = num % 1000000000;
838 snprintf(fn, sizeof(fn), "digits/1F");
839 snprintf(fna, sizeof(fna), "digits/milliard");
841 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
845 snprintf(fn, sizeof(fn), "digits/milliards");
849 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
853 if (!ast_streamfile(chan, fn, language)) {
854 if ((audiofd > -1) && (ctrlfd > -1))
855 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
857 res = ast_waitstream(chan, ints);
859 ast_stopstream(chan);
861 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
862 if ((audiofd > -1) && (ctrlfd > -1))
863 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
865 res = ast_waitstream(chan, ints);
867 ast_stopstream(chan);
875 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
877 In addition to American English, the following sounds are required: "and"
879 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
886 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
888 while (!res && (num || playh || playa )) {
890 snprintf(fn, sizeof(fn), "digits/minus");
891 if ( num > INT_MIN ) {
897 snprintf(fn, sizeof(fn), "digits/hundred");
900 snprintf(fn, sizeof(fn), "digits/and");
902 } else if (num < 20) {
903 snprintf(fn, sizeof(fn), "digits/%d", num);
905 } else if (num < 100) {
906 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
907 num -= ((num / 10) * 10);
908 } else if (num < 1000) {
909 int hundreds = num / 100;
910 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
913 num -= 100 * hundreds;
916 } else if (num < 1000000) {
917 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
920 snprintf(fn, sizeof(fn), "digits/thousand");
922 if (num && num < 100)
924 } else if (num < 1000000000) {
925 int millions = num / 1000000;
926 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
929 snprintf(fn, sizeof(fn), "digits/million");
931 if (num && num < 100)
935 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
940 if (!ast_streamfile(chan, fn, language)) {
941 if ((audiofd > -1) && (ctrlfd > -1))
942 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
944 res = ast_waitstream(chan, ints);
946 ast_stopstream(chan);
952 /*! \brief ast_say_number_full_es: Spanish syntax */
954 Requires a few new audios:
955 1F.gsm: feminine 'una'
956 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
958 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)
962 int mf = 0; /* +1 = male; -1 = female */
965 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
968 if (!strncasecmp(options, "f",1))
970 else if (!strncasecmp(options, "m", 1))
974 while (!res && num) {
976 snprintf(fn, sizeof(fn), "digits/minus");
977 if ( num > INT_MIN ) {
983 snprintf(fn, sizeof(fn), "digits/and");
985 } else if (num == 1) {
987 snprintf(fn, sizeof(fn), "digits/%dF", num);
989 snprintf(fn, sizeof(fn), "digits/%dM", num);
991 snprintf(fn, sizeof(fn), "digits/%d", num);
993 } else if (num < 31) {
994 snprintf(fn, sizeof(fn), "digits/%d", num);
996 } else if (num < 100) {
997 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
998 num -= ((num/10)*10);
1001 } else if (num == 100) {
1002 snprintf(fn, sizeof(fn), "digits/100");
1004 } else if (num < 200) {
1005 snprintf(fn, sizeof(fn), "digits/100-and");
1009 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1010 num -= ((num/100)*100);
1011 } else if (num < 2000) {
1013 snprintf(fn, sizeof(fn), "digits/thousand");
1015 if (num < 1000000) {
1016 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1020 snprintf(fn, sizeof(fn), "digits/thousand");
1022 if (num < 2147483640) {
1023 if ((num/1000000) == 1) {
1024 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1027 snprintf(fn, sizeof(fn), "digits/million");
1029 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1032 snprintf(fn, sizeof(fn), "digits/millions");
1034 num = num % 1000000;
1037 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1045 if (!ast_streamfile(chan, fn, language)) {
1046 if ((audiofd > -1) && (ctrlfd > -1))
1047 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1049 res = ast_waitstream(chan, ints);
1051 ast_stopstream(chan);
1059 /*! \brief ast_say_number_full_fr: French syntax */
1060 /* Extra sounds needed:
1063 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)
1068 int mf = 1; /* +1 = male; -1 = female */
1071 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1073 if (options && !strncasecmp(options, "f",1))
1076 while (!res && (num || playh || playa)) {
1078 snprintf(fn, sizeof(fn), "digits/minus");
1079 if ( num > INT_MIN ) {
1085 snprintf(fn, sizeof(fn), "digits/hundred");
1088 snprintf(fn, sizeof(fn), "digits/et");
1090 } else if (num == 1) {
1092 snprintf(fn, sizeof(fn), "digits/%dF", num);
1094 snprintf(fn, sizeof(fn), "digits/%d", num);
1096 } else if (num < 21) {
1097 snprintf(fn, sizeof(fn), "digits/%d", num);
1099 } else if (num < 70) {
1100 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1101 if ((num % 10) == 1) playa++;
1103 } else if (num < 80) {
1104 snprintf(fn, sizeof(fn), "digits/60");
1105 if ((num % 10) == 1) playa++;
1107 } else if (num < 100) {
1108 snprintf(fn, sizeof(fn), "digits/80");
1110 } else if (num < 200) {
1111 snprintf(fn, sizeof(fn), "digits/hundred");
1113 } else if (num < 1000) {
1114 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1117 } else if (num < 2000) {
1118 snprintf(fn, sizeof(fn), "digits/thousand");
1120 } else if (num < 1000000) {
1121 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1124 snprintf(fn, sizeof(fn), "digits/thousand");
1126 } else if (num < 1000000000) {
1127 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1130 snprintf(fn, sizeof(fn), "digits/million");
1131 num = num % 1000000;
1134 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1138 if (!ast_streamfile(chan, fn, language)) {
1139 if ((audiofd > -1) && (ctrlfd > -1))
1140 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1142 res = ast_waitstream(chan, ints);
1144 ast_stopstream(chan);
1152 /*! \brief ast_say_number_full_he: Hebrew syntax */
1153 /* Extra sounds needed:
1157 2hundred: 2 hundreds
1158 2thousands: 2 thousand
1159 thousands: plural of 'thousand'
1160 3sF 'Smichut forms (female)
1167 3s 'Smichut' forms (male)
1184 TODO: 've' should sometimed be 'hu':
1185 * before 'shtaym' (2, F)
1186 * before 'shnaym' (2, M)
1187 * before 'shlosha' (3, M)
1188 * before 'shmone' (8, M)
1189 * before 'shlosim' (30)
1190 * before 'shmonim' (80)
1196 #define SAY_NUM_BUF_SIZE 256
1197 static int ast_say_number_full_he(struct ast_channel *chan, int num,
1198 const char *ints, const char *language, const char *options,
1199 int audiofd, int ctrlfd)
1202 int state = 0; /* no need to save anything */
1203 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1204 char fn[SAY_NUM_BUF_SIZE] = "";
1205 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. "
1206 "num: %d, options=\"%s\"\n",
1210 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1212 if (options && !strncasecmp(options, "f",1))
1215 /* Do we have work to do? */
1216 while (!res && (num || (state>0) )) {
1217 /* first type of work: play a second sound. In this loop
1218 * we can only play one sound file at a time. Thus playing
1219 * a second one requires repeating the loop just for the
1220 * second file. The variable 'state' remembers where we were.
1221 * state==0 is the normal mode and it means that we continue
1222 * to check if the number num has yet anything left.
1224 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, "
1225 "state=%d, options=\"%s\", mf=%d\n",
1226 num, state, options, mf
1229 snprintf(fn, sizeof(fn), "digits/hundred");
1231 } else if (state==2) {
1232 snprintf(fn, sizeof(fn), "digits/ve");
1234 } else if (state==3) {
1235 snprintf(fn, sizeof(fn), "digits/thousands");
1237 } else if (num <21) {
1239 snprintf(fn, sizeof(fn), "digits/%dF", num);
1241 snprintf(fn, sizeof(fn), "digits/%d", num);
1243 } else if (num < 100) {
1244 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1247 } else if (num < 200) {
1248 snprintf(fn, sizeof(fn), "digits/1hundred");
1251 } else if (num < 300) {
1252 snprintf(fn, sizeof(fn), "digits/2hundred");
1255 } else if (num < 1000) {
1256 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1259 } else if (num < 2000) {
1260 snprintf(fn, sizeof(fn), "digits/thousand");
1262 } else if (num < 3000) {
1263 snprintf(fn, sizeof(fn), "digits/2thousand");
1266 } else if (num < 20000) {
1267 snprintf(fn, sizeof(fn), "digits/%ds",(num/1000));
1270 } else if (num < 1000000) {
1271 res = ast_say_number_full_he(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1274 snprintf(fn, sizeof(fn), "digits/thousand");
1276 } else if (num < 1000000000) {
1277 res = ast_say_number_full_he(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1280 snprintf(fn, sizeof(fn), "digits/million");
1281 num = num % 1000000;
1284 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1288 if (!ast_streamfile(chan, fn, language)) {
1289 if ((audiofd > -1) && (ctrlfd > -1))
1290 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1292 res = ast_waitstream(chan, ints);
1294 ast_stopstream(chan);
1300 /*! \brief ast_say_number_full_it: Italian */
1301 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1309 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1314 Like english, numbers up to 20 are a single 'word', and others
1315 compound, but with exceptions.
1316 For example 21 is not twenty-one, but there is a single word in 'it'.
1317 Idem for 28 (ie when a the 2nd part of a compund number
1318 starts with a vowel)
1320 There are exceptions also for hundred, thousand and million.
1321 In english 100 = one hundred, 200 is two hundred.
1322 In italian 100 = cento , like to say hundred (without one),
1323 200 and more are like english.
1325 Same applies for thousand:
1326 1000 is one thousand in en, 2000 is two thousand.
1327 In it we have 1000 = mille , 2000 = 2 mila
1329 For million(s) we use the plural, if more than one
1330 Also, one million is abbreviated in it, like on-million,
1331 or 'un milione', not 'uno milione'.
1332 So the right file is provided.
1335 while (!res && (num || playh)) {
1337 snprintf(fn, sizeof(fn), "digits/minus");
1338 if ( num > INT_MIN ) {
1344 snprintf(fn, sizeof(fn), "digits/hundred");
1346 } else if (num < 20) {
1347 snprintf(fn, sizeof(fn), "digits/%d", num);
1349 } else if (num == 21) {
1350 snprintf(fn, sizeof(fn), "digits/%d", num);
1352 } else if (num == 28) {
1353 snprintf(fn, sizeof(fn), "digits/%d", num);
1355 } else if (num == 31) {
1356 snprintf(fn, sizeof(fn), "digits/%d", num);
1358 } else if (num == 38) {
1359 snprintf(fn, sizeof(fn), "digits/%d", num);
1361 } else if (num == 41) {
1362 snprintf(fn, sizeof(fn), "digits/%d", num);
1364 } else if (num == 48) {
1365 snprintf(fn, sizeof(fn), "digits/%d", num);
1367 } else if (num == 51) {
1368 snprintf(fn, sizeof(fn), "digits/%d", num);
1370 } else if (num == 58) {
1371 snprintf(fn, sizeof(fn), "digits/%d", num);
1373 } else if (num == 61) {
1374 snprintf(fn, sizeof(fn), "digits/%d", num);
1376 } else if (num == 68) {
1377 snprintf(fn, sizeof(fn), "digits/%d", num);
1379 } else if (num == 71) {
1380 snprintf(fn, sizeof(fn), "digits/%d", num);
1382 } else if (num == 78) {
1383 snprintf(fn, sizeof(fn), "digits/%d", num);
1385 } else if (num == 81) {
1386 snprintf(fn, sizeof(fn), "digits/%d", num);
1388 } else if (num == 88) {
1389 snprintf(fn, sizeof(fn), "digits/%d", num);
1391 } else if (num == 91) {
1392 snprintf(fn, sizeof(fn), "digits/%d", num);
1394 } else if (num == 98) {
1395 snprintf(fn, sizeof(fn), "digits/%d", num);
1397 } else if (num < 100) {
1398 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1399 num -= ((num / 10) * 10);
1402 if ((num / 100) > 1) {
1403 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1406 snprintf(fn, sizeof(fn), "digits/hundred");
1408 num -= ((num / 100) * 100);
1410 if (num < 1000000) { /* 1,000,000 */
1412 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1417 if ((tempnum / 1000) < 2)
1418 snprintf(fn, sizeof(fn), "digits/thousand");
1419 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1420 snprintf(fn, sizeof(fn), "digits/thousands");
1422 if (num < 1000000000) { /* 1,000,000,000 */
1423 if ((num / 1000000) > 1)
1424 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1428 num = num % 1000000;
1429 if ((tempnum / 1000000) < 2)
1430 snprintf(fn, sizeof(fn), "digits/million");
1432 snprintf(fn, sizeof(fn), "digits/millions");
1435 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1442 if (!ast_streamfile(chan, fn, language)) {
1443 if ((audiofd > -1) && (ctrlfd > -1))
1444 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1446 res = ast_waitstream(chan, ints);
1448 ast_stopstream(chan);
1454 /*! \brief ast_say_number_full_nl: dutch syntax */
1455 /* New files: digits/nl-en
1457 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1464 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1465 while (!res && (num || playh )) {
1467 snprintf(fn, sizeof(fn), "digits/minus");
1468 if ( num > INT_MIN ) {
1474 snprintf(fn, sizeof(fn), "digits/hundred");
1476 } else if (num < 20) {
1477 snprintf(fn, sizeof(fn), "digits/%d", num);
1479 } else if (num < 100) {
1482 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1486 snprintf(fn, sizeof(fn), "digits/nl-en");
1488 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1493 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1495 num -= ((num / 100) * 100);
1497 if (num < 1000000) { /* 1,000,000 */
1498 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1502 snprintf(fn, sizeof(fn), "digits/thousand");
1504 if (num < 1000000000) { /* 1,000,000,000 */
1505 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1508 num = num % 1000000;
1509 snprintf(fn, sizeof(fn), "digits/million");
1512 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1520 if (!ast_streamfile(chan, fn, language)) {
1521 if ((audiofd > -1) && (ctrlfd > -1))
1522 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1524 res = ast_waitstream(chan, ints);
1526 ast_stopstream(chan);
1532 /*! \brief ast_say_number_full_no: Norwegian syntax */
1534 In addition to American English, the following sounds are required: "and", "1N"
1536 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)
1541 int cn = 1; /* +1 = commune; -1 = neuter */
1545 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1547 if (options && !strncasecmp(options, "n",1)) cn = -1;
1549 while (!res && (num || playh || playa )) {
1550 /* The grammar for Norwegian numbers is the same as for English except
1551 * for the following:
1552 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1553 * "and" before the last two digits, i.e. 2034 is "two thousand and
1554 * thirty-four" and 1000012 is "one million and twelve".
1557 snprintf(fn, sizeof(fn), "digits/minus");
1558 if ( num > INT_MIN ) {
1564 snprintf(fn, sizeof(fn), "digits/hundred");
1567 snprintf(fn, sizeof(fn), "digits/and");
1569 } else if (num == 1 && cn == -1) {
1570 snprintf(fn, sizeof(fn), "digits/1N");
1572 } else if (num < 20) {
1573 snprintf(fn, sizeof(fn), "digits/%d", num);
1575 } else if (num < 100) {
1576 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1577 num -= ((num / 10) * 10);
1578 } else if (num < 1000) {
1579 int hundreds = num / 100;
1581 snprintf(fn, sizeof(fn), "digits/1N");
1583 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1586 num -= 100 * hundreds;
1589 } else if (num < 1000000) {
1590 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1593 snprintf(fn, sizeof(fn), "digits/thousand");
1595 if (num && num < 100)
1597 } else if (num < 1000000000) {
1598 int millions = num / 1000000;
1599 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1602 snprintf(fn, sizeof(fn), "digits/million");
1603 num = num % 1000000;
1604 if (num && num < 100)
1608 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1613 if (!ast_streamfile(chan, fn, language)) {
1614 if ((audiofd > -1) && (ctrlfd > -1))
1615 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1617 res = ast_waitstream(chan, ints);
1619 ast_stopstream(chan);
1626 char *separator_dziesiatek;
1630 char *dziesiatki[10];
1635 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1641 return odm->rzedy[rzad - 1][0];
1642 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1643 return odm->rzedy[rzad - 1][1];
1645 return odm->rzedy[rzad - 1][2];
1648 static char* pl_append(char* buffer, char* str)
1650 strcpy(buffer, str);
1651 buffer += strlen(str);
1655 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1657 char file_name[255] = "digits/";
1658 strcat(file_name, fn);
1660 ast_log(LOG_DEBUG, "Trying to play: %s\n", file_name);
1661 if (!ast_streamfile(chan, file_name, language)) {
1662 if ((audiofd > -1) && (ctrlfd > -1))
1663 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1665 ast_waitstream(chan, ints);
1667 ast_stopstream(chan);
1670 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1672 /* Initialise variables to allow compilation on Debian-stable, etc */
1682 if (i == 0 && rzad > 0) {
1686 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1690 m1000E6 = i % 1000000000;
1691 i1000E6 = i / 1000000000;
1693 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1695 m1000E3 = m1000E6 % 1000000;
1696 i1000E3 = m1000E6 / 1000000;
1698 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1700 m1000 = m1000E3 % 1000;
1701 i1000 = m1000E3 / 1000;
1703 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1709 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1711 if ( m100 > 0 && m100 <=9 ) {
1713 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1715 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1716 } else if (m100 % 10 == 0) {
1717 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1718 } else if (m100 <= 19 ) {
1719 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1720 } else if (m100 != 0) {
1721 if (odm->separator_dziesiatek[0]==' ') {
1722 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1723 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1727 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1728 b = pl_append(b, odm->separator_dziesiatek);
1729 b = pl_append(b, odm->cyfry2[m100 % 10]);
1730 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1735 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1739 /* ast_say_number_full_pl: Polish syntax */
1740 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)
1750 1000000000.2 miliardy
1751 1000000000.5 miliardow
1815 70m siedemdziesieciu
1827 90m dziewiedziesieciu
1829 and combinations of eg.: 20_1, 30m_3m, etc...
1833 char *zenski_cyfry[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1835 char *zenski_cyfry2[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1837 char *meski_cyfry[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1839 char *meski_cyfry2[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1841 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1843 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1845 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1847 char *nijaki_cyfry[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1849 char *nijaki_cyfry2[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1851 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1853 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1855 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1857 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1859 /* Initialise variables to allow compilation on Debian-stable, etc */
1862 static odmiana *odmiana_nieosobowa = NULL;
1863 static odmiana *odmiana_meska = NULL;
1864 static odmiana *odmiana_zenska = NULL;
1866 if (odmiana_nieosobowa == NULL) {
1867 odmiana_nieosobowa = (odmiana *) malloc(sizeof(odmiana));
1869 odmiana_nieosobowa->separator_dziesiatek = " ";
1871 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
1872 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
1873 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
1874 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
1875 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
1876 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
1879 if (odmiana_zenska == NULL) {
1880 odmiana_zenska = (odmiana *) malloc(sizeof(odmiana));
1882 odmiana_zenska->separator_dziesiatek = " ";
1884 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
1885 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
1886 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
1887 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
1888 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
1889 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
1892 if (odmiana_meska == NULL) {
1893 odmiana_meska = (odmiana *) malloc(sizeof(odmiana));
1895 odmiana_meska->separator_dziesiatek = " ";
1897 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
1898 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
1899 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
1900 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
1901 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
1902 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
1906 if (strncasecmp(options, "f", 1) == 0)
1908 else if (strncasecmp(options, "m", 1) == 0)
1911 o = odmiana_nieosobowa;
1913 o = odmiana_nieosobowa;
1915 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
1919 /* ast_say_number_full_pt: Portuguese syntax */
1920 /* Extra sounds needed: */
1921 /* For feminin all sound files end with F */
1922 /* 100E for 100+ something */
1923 /* 1000000S for plural */
1924 /* pt-e for 'and' */
1925 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)
1929 int mf = 1; /* +1 = male; -1 = female */
1933 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1935 if (options && !strncasecmp(options, "f",1))
1938 while (!res && num ) {
1940 snprintf(fn, sizeof(fn), "digits/minus");
1941 if ( num > INT_MIN ) {
1946 } else if (num < 20) {
1947 if ((num == 1 || num == 2) && (mf < 0))
1948 snprintf(fn, sizeof(fn), "digits/%dF", num);
1950 snprintf(fn, sizeof(fn), "digits/%d", num);
1952 } else if (num < 100) {
1953 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1957 } else if (num < 1000) {
1959 snprintf(fn, sizeof(fn), "digits/100");
1961 snprintf(fn, sizeof(fn), "digits/100E");
1963 if (mf < 0 && num > 199)
1964 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1966 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1971 } else if (num < 1000000) {
1973 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1977 snprintf(fn, sizeof(fn), "digits/1000");
1978 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
1981 } else if (num < 1000000000) {
1982 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
1986 snprintf(fn, sizeof(fn), "digits/1000000");
1988 snprintf(fn, sizeof(fn), "digits/1000000S");
1990 if ((num % 1000000) &&
1992 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
1993 /* no hundreds and below */
1994 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
1996 num = num % 1000000;
1998 /* number is too big */
1999 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2003 if (!ast_streamfile(chan, fn, language)) {
2004 if ((audiofd > -1) && (ctrlfd > -1))
2005 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2007 res = ast_waitstream(chan, ints);
2009 ast_stopstream(chan);
2011 if (!res && playh) {
2012 res = wait_file(chan, ints, "digits/pt-e", language);
2013 ast_stopstream(chan);
2020 /*! \brief ast_say_number_full_se: Swedish syntax */
2021 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)
2026 int cn = 1; /* +1 = commune; -1 = neuter */
2028 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2029 if (options && !strncasecmp(options, "n",1)) cn = -1;
2031 while (!res && (num || playh)) {
2033 snprintf(fn, sizeof(fn), "digits/minus");
2034 if ( num > INT_MIN ) {
2040 snprintf(fn, sizeof(fn), "digits/hundred");
2042 } else if (num < 20) {
2043 snprintf(fn, sizeof(fn), "digits/%d", num);
2045 } else if (num < 100) {
2046 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2047 num -= ((num / 10) * 10);
2048 } else if (num == 1 && cn == -1) { /* En eller ett? */
2049 snprintf(fn, sizeof(fn), "digits/1N");
2053 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2055 num -= ((num / 100) * 100);
2057 if (num < 1000000) { /* 1,000,000 */
2058 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
2063 snprintf(fn, sizeof(fn), "digits/thousand");
2065 if (num < 1000000000) { /* 1,000,000,000 */
2066 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
2070 num = num % 1000000;
2071 snprintf(fn, sizeof(fn), "digits/million");
2074 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2081 if (!ast_streamfile(chan, fn, language)) {
2082 if ((audiofd > -1) && (ctrlfd > -1))
2083 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2085 res = ast_waitstream(chan, ints);
2086 ast_stopstream(chan);
2093 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2094 static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2100 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2102 while (!res && (num || playh)) {
2104 snprintf(fn, sizeof(fn), "digits/minus");
2105 if ( num > INT_MIN ) {
2111 snprintf(fn, sizeof(fn), "digits/hundred");
2113 } else if (num < 10) {
2114 snprintf(fn, sizeof(fn), "digits/%d", num);
2116 } else if (num < 100) {
2117 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2118 num -= ((num / 10) * 10);
2121 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2123 num -= ((num / 100) * 100);
2125 if (num < 1000000) { /* 1,000,000 */
2126 res = ast_say_number_full_tw(chan, num / 1000, ints, language, audiofd, ctrlfd);
2130 snprintf(fn, sizeof(fn), "digits/thousand");
2132 if (num < 1000000000) { /* 1,000,000,000 */
2133 res = ast_say_number_full_tw(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2136 num = num % 1000000;
2137 snprintf(fn, sizeof(fn), "digits/million");
2140 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2147 if (!ast_streamfile(chan, fn, language)) {
2148 if ((audiofd > -1) && (ctrlfd > -1))
2149 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2151 res = ast_waitstream(chan, ints);
2153 ast_stopstream(chan);
2160 /*! \brief determine last digits for thousands/millions (ru) */
2161 static int get_lastdigits_ru(int num) {
2164 } else if (num < 100) {
2165 return get_lastdigits_ru(num % 10);
2166 } else if (num < 1000) {
2167 return get_lastdigits_ru(num % 100);
2169 return 0; /* number too big */
2173 /*! \brief ast_say_number_full_ru: Russian syntax */
2174 /*! \brief additional files:
2175 n00.gsm (one hundred, two hundred, ...)
2178 thousands-i.gsm (tisyachi)
2179 million-a.gsm (milliona)
2185 where 'n' from 1 to 9
2187 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)
2193 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2195 while (!res && (num)) {
2197 snprintf(fn, sizeof(fn), "digits/minus");
2198 if ( num > INT_MIN ) {
2203 } else if (num < 20) {
2204 if (options && strlen(options) == 1 && num < 3) {
2205 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2207 snprintf(fn, sizeof(fn), "digits/%d", num);
2210 } else if (num < 100) {
2211 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2213 } else if (num < 1000){
2214 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2216 } else if (num < 1000000) { /* 1,000,000 */
2217 lastdigits = get_lastdigits_ru(num / 1000);
2219 if (lastdigits < 3) {
2220 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2222 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2226 if (lastdigits == 1) {
2227 snprintf(fn, sizeof(fn), "digits/thousand");
2228 } else if (lastdigits > 1 && lastdigits < 5) {
2229 snprintf(fn, sizeof(fn), "digits/thousands-i");
2231 snprintf(fn, sizeof(fn), "digits/thousands");
2234 } else if (num < 1000000000) { /* 1,000,000,000 */
2235 lastdigits = get_lastdigits_ru(num / 1000000);
2237 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2240 if (lastdigits == 1) {
2241 snprintf(fn, sizeof(fn), "digits/million");
2242 } else if (lastdigits > 1 && lastdigits < 5) {
2243 snprintf(fn, sizeof(fn), "digits/million-a");
2245 snprintf(fn, sizeof(fn), "digits/millions");
2250 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2254 if (!ast_streamfile(chan, fn, language)) {
2255 if ((audiofd > -1) && (ctrlfd > -1))
2256 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2258 res = ast_waitstream(chan, ints);
2260 ast_stopstream(chan);
2267 /*! \brief ast_say_enumeration_full: call language-specific functions */
2268 /* Called from AGI */
2269 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2271 if (!strcasecmp(language,"en") ) { /* English syntax */
2272 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2273 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
2274 return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
2275 } else if (!strcasecmp(language, "de") ) { /* German syntax */
2276 return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
2279 /* Default to english */
2280 return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
2283 /*! \brief ast_say_enumeration_full_en: English syntax */
2284 /* This is the default syntax, if no other syntax defined in this file is used */
2285 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2290 while (!res && num) {
2292 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2293 if ( num > INT_MIN ) {
2298 } else if (num < 20) {
2299 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2301 } else if (num < 100) {
2302 int tens = num / 10;
2305 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2307 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2309 } else if (num < 1000) {
2310 int hundreds = num / 100;
2312 if (hundreds > 1 || t == 1) {
2313 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2318 snprintf(fn, sizeof(fn), "digits/hundred");
2320 snprintf(fn, sizeof(fn), "digits/h-hundred");
2322 } else if (num < 1000000) {
2323 int thousands = num / 1000;
2325 if (thousands > 1 || t == 1) {
2326 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2331 snprintf(fn, sizeof(fn), "digits/thousand");
2333 snprintf(fn, sizeof(fn), "digits/h-thousand");
2336 } else if (num < 1000000000) {
2337 int millions = num / 1000000;
2338 num = num % 1000000;
2340 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2344 snprintf(fn, sizeof(fn), "digits/million");
2346 snprintf(fn, sizeof(fn), "digits/h-million");
2348 } else if (num < INT_MAX) {
2349 int billions = num / 1000000000;
2350 num = num % 1000000000;
2352 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2356 snprintf(fn, sizeof(fn), "digits/billion");
2358 snprintf(fn, sizeof(fn), "digits/h-billion");
2360 } else if (num == INT_MAX) {
2361 snprintf(fn, sizeof(fn), "digits/h-last");
2365 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2370 if (!ast_streamfile(chan, fn, language)) {
2371 if ((audiofd > -1) && (ctrlfd > -1)) {
2372 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2374 res = ast_waitstream(chan, ints);
2377 ast_stopstream(chan);
2383 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2384 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)
2386 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2388 char fn[256] = "", fna[256] = "";
2391 if (options && !strncasecmp(options, "f",1)) {
2393 } else if (options && !strncasecmp(options, "n",1)) {
2400 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2402 while (!res && num) {
2404 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2405 if ( num > INT_MIN ) {
2410 } else if (num < 100 && t) {
2411 snprintf(fn, sizeof(fn), "digits/and");
2413 } else if (num < 20) {
2414 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2416 } else if (num < 100) {
2417 int ones = num % 10;
2419 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2422 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2425 } else if (num == 100 && t == 0) {
2426 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2428 } else if (num < 1000) {
2429 int hundreds = num / 100;
2431 if (hundreds == 1) {
2432 snprintf(fn, sizeof(fn), "digits/1N");
2434 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2437 snprintf(fna, sizeof(fna), "digits/hundred");
2439 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2442 } else if (num < 1000000) {
2443 int thousands = num / 1000;
2445 if (thousands == 1) {
2447 snprintf(fn, sizeof(fn), "digits/1N");
2448 snprintf(fna, sizeof(fna), "digits/thousand");
2451 snprintf(fn, sizeof(fn), "digits/1N");
2452 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2454 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2458 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2463 snprintf(fn, sizeof(fn), "digits/thousand");
2465 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2469 } else if (num < 1000000000) {
2470 int millions = num / 1000000;
2471 num = num % 1000000;
2472 if (millions == 1) {
2474 snprintf(fn, sizeof(fn), "digits/1F");
2475 snprintf(fna, sizeof(fna), "digits/million");
2477 snprintf(fn, sizeof(fn), "digits/1N");
2478 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2481 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2486 snprintf(fn, sizeof(fn), "digits/millions");
2488 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2492 } else if (num < INT_MAX) {
2493 int billions = num / 1000000000;
2494 num = num % 1000000000;
2495 if (billions == 1) {
2497 snprintf(fn, sizeof(fn), "digits/1F");
2498 snprintf(fna, sizeof(fna), "digits/milliard");
2500 snprintf(fn, sizeof(fn), "digits/1N");
2501 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2504 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2508 snprintf(fn, sizeof(fna), "digits/milliards");
2510 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2514 } else if (num == INT_MAX) {
2515 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2519 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2524 if (!ast_streamfile(chan, fn, language)) {
2525 if ((audiofd > -1) && (ctrlfd > -1))
2526 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2528 res = ast_waitstream(chan, ints);
2530 ast_stopstream(chan);
2532 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2533 if ((audiofd > -1) && (ctrlfd > -1)) {
2534 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2536 res = ast_waitstream(chan, ints);
2539 ast_stopstream(chan);
2547 /*! \brief ast_say_enumeration_full_de: German syntax */
2548 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)
2550 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2552 char fn[256] = "", fna[256] = "";
2555 if (options && !strncasecmp(options, "f",1)) {
2557 } else if (options && !strncasecmp(options, "n",1)) {
2564 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
2566 while (!res && num) {
2568 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2569 if ( num > INT_MIN ) {
2574 } else if (num < 100 && t) {
2575 snprintf(fn, sizeof(fn), "digits/and");
2577 } else if (num < 20) {
2578 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2580 } else if (num < 100) {
2581 int ones = num % 10;
2583 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2586 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2589 } else if (num == 100 && t == 0) {
2590 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2592 } else if (num < 1000) {
2593 int hundreds = num / 100;
2595 if (hundreds == 1) {
2596 snprintf(fn, sizeof(fn), "digits/1N");
2598 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2601 snprintf(fna, sizeof(fna), "digits/hundred");
2603 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2606 } else if (num < 1000000) {
2607 int thousands = num / 1000;
2609 if (thousands == 1) {
2611 snprintf(fn, sizeof(fn), "digits/1N");
2612 snprintf(fna, sizeof(fna), "digits/thousand");
2615 snprintf(fn, sizeof(fn), "digits/1N");
2616 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2618 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2622 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2627 snprintf(fn, sizeof(fn), "digits/thousand");
2629 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2633 } else if (num < 1000000000) {
2634 int millions = num / 1000000;
2635 num = num % 1000000;
2636 if (millions == 1) {
2638 snprintf(fn, sizeof(fn), "digits/1F");
2639 snprintf(fna, sizeof(fna), "digits/million");
2641 snprintf(fn, sizeof(fn), "digits/1N");
2642 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2645 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2650 snprintf(fn, sizeof(fn), "digits/millions");
2652 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2656 } else if (num < INT_MAX) {
2657 int billions = num / 1000000000;
2658 num = num % 1000000000;
2659 if (billions == 1) {
2661 snprintf(fn, sizeof(fn), "digits/1F");
2662 snprintf(fna, sizeof(fna), "digits/milliard");
2664 snprintf(fn, sizeof(fn), "digits/1N");
2665 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2668 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2672 snprintf(fn, sizeof(fna), "digits/milliards");
2674 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2678 } else if (num == INT_MAX) {
2679 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2683 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2688 if (!ast_streamfile(chan, fn, language)) {
2689 if ((audiofd > -1) && (ctrlfd > -1))
2690 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2692 res = ast_waitstream(chan, ints);
2694 ast_stopstream(chan);
2696 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2697 if ((audiofd > -1) && (ctrlfd > -1)) {
2698 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2700 res = ast_waitstream(chan, ints);
2703 ast_stopstream(chan);
2711 static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2713 if (!strcasecmp(lang, "en") ) { /* English syntax */
2714 return(ast_say_date_en(chan, t, ints, lang));
2715 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2716 return(ast_say_date_da(chan, t, ints, lang));
2717 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2718 return(ast_say_date_de(chan, t, ints, lang));
2719 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2720 return(ast_say_date_fr(chan, t, ints, lang));
2721 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2722 return(ast_say_date_nl(chan, t, ints, lang));
2723 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
2724 return(ast_say_date_pt(chan, t, ints, lang));
2725 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2726 return(ast_say_date_gr(chan, t, ints, lang));
2729 /* Default to English */
2730 return(ast_say_date_en(chan, t, ints, lang));
2733 /* English syntax */
2734 int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2739 ast_localtime(&t,&tm,NULL);
2741 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2742 res = ast_streamfile(chan, fn, lang);
2744 res = ast_waitstream(chan, ints);
2747 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2748 res = ast_streamfile(chan, fn, lang);
2750 res = ast_waitstream(chan, ints);
2753 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2755 res = ast_waitstream(chan, ints);
2757 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2762 int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2767 ast_localtime(&t,&tm,NULL);
2769 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2770 res = ast_streamfile(chan, fn, lang);
2772 res = ast_waitstream(chan, ints);
2775 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2777 res = ast_waitstream(chan, ints);
2779 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2780 res = ast_streamfile(chan, fn, lang);
2782 res = ast_waitstream(chan, ints);
2786 int year = tm.tm_year + 1900;
2787 if (year > 1999) { /* year 2000 and later */
2788 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2791 /* I'm not going to handle 1100 and prior */
2792 /* We'll just be silent on the year, instead of bombing out. */
2794 /* year 1100 to 1999. will anybody need this?!? */
2795 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2796 res = wait_file(chan, ints, fn, lang);
2798 res = wait_file(chan,ints, "digits/hundred", lang);
2799 if (!res && year % 100 != 0) {
2800 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2810 int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2815 ast_localtime(&t,&tm,NULL);
2817 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2818 res = ast_streamfile(chan, fn, lang);
2820 res = ast_waitstream(chan, ints);
2823 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2825 res = ast_waitstream(chan, ints);
2827 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2828 res = ast_streamfile(chan, fn, lang);
2830 res = ast_waitstream(chan, ints);
2834 int year = tm.tm_year + 1900;
2835 if (year > 1999) { /* year 2000 and later */
2836 res = ast_say_number(chan, year, ints, lang, (char *) NULL);
2839 /* I'm not going to handle 1100 and prior */
2840 /* We'll just be silent on the year, instead of bombing out. */
2842 /* year 1100 to 1999. will anybody need this?!? */
2843 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2844 snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
2845 res = wait_file(chan, ints, fn, lang);
2847 res = wait_file(chan,ints, "digits/hundred", lang);
2848 if (!res && year % 100 != 0) {
2849 res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
2859 int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2864 ast_localtime(&t,&tm,NULL);
2866 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2867 res = ast_streamfile(chan, fn, lang);
2869 res = ast_waitstream(chan, ints);
2872 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2874 res = ast_waitstream(chan, ints);
2876 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2877 res = ast_streamfile(chan, fn, lang);
2879 res = ast_waitstream(chan, ints);
2882 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2887 int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2892 ast_localtime(&t,&tm,NULL);
2894 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2895 res = ast_streamfile(chan, fn, lang);
2897 res = ast_waitstream(chan, ints);
2900 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
2902 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2903 res = ast_streamfile(chan, fn, lang);
2905 res = ast_waitstream(chan, ints);
2908 res = ast_waitstream(chan, ints);
2910 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2914 /* Portuguese syntax */
2915 int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
2920 ast_localtime(&t,&tm,NULL);
2921 localtime_r(&t,&tm);
2922 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2924 res = wait_file(chan, ints, fn, lang);
2926 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2928 res = wait_file(chan, ints, "digits/pt-de", lang);
2929 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2931 res = wait_file(chan, ints, fn, lang);
2933 res = wait_file(chan, ints, "digits/pt-de", lang);
2935 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2940 static int say_date_with_format(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
2942 if (!strcasecmp(lang, "en") ) { /* English syntax */
2943 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2944 } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
2945 return(ast_say_date_with_format_da(chan, time, ints, lang, format, timezone));
2946 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
2947 return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
2948 } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) { /* Spanish syntax */
2949 return(ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
2950 } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
2951 return(ast_say_date_with_format_he(chan, time, ints, lang, format, timezone));
2952 } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
2953 return(ast_say_date_with_format_fr(chan, time, ints, lang, format, timezone));
2954 } else if (!strcasecmp(lang, "it") ) { /* Italian syntax */
2955 return(ast_say_date_with_format_it(chan, time, ints, lang, format, timezone));
2956 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2957 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
2958 } else if (!strcasecmp(lang, "pl") ) { /* Polish syntax */
2959 return(ast_say_date_with_format_pl(chan, time, ints, lang, format, timezone));
2960 } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
2961 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
2962 } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
2963 return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
2964 } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
2965 return(ast_say_date_with_format_gr(chan, time, ints, lang, format, timezone));
2968 /* Default to English */
2969 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
2972 /* English syntax */
2973 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)
2976 int res=0, offset, sndoffset;
2977 char sndfile[256], nextmsg[256];
2980 format = "ABdY 'digits/at' IMp";
2982 ast_localtime(&time,&tm,timezone);
2984 for (offset=0 ; format[offset] != '\0' ; offset++) {
2986 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2987 switch (format[offset]) {
2988 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2990 /* Literal name of a sound file */
2992 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2993 sndfile[sndoffset] = format[offset];
2994 sndfile[sndoffset] = '\0';
2995 res = wait_file(chan,ints,sndfile,lang);
2999 /* Sunday - Saturday */
3000 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
3001 res = wait_file(chan,ints,nextmsg,lang);
3006 /* January - December */
3007 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
3008 res = wait_file(chan,ints,nextmsg,lang);
3011 /* Month enumerated */
3012 res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
3016 /* First - Thirtyfirst */
3017 res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
3021 if (tm.tm_year > 99) {
3022 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
3023 } else if (tm.tm_year < 1) {
3024 /* I'm not going to handle 1900 and prior */
3025 /* We'll just be silent on the year, instead of bombing out. */
3027 res = wait_file(chan, ints, "digits/19", lang);
3029 if (tm.tm_year <= 9) {
3031 res = wait_file(chan,ints, "digits/oh", lang);
3034 res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
3041 if (tm.tm_hour == 0)
3042 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
3043 else if (tm.tm_hour > 12)
3044 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
3046 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
3047 res = wait_file(chan,ints,nextmsg,lang);
3052 if (format[offset] == 'H') {
3054 if (tm.tm_hour < 10) {
3055 res = wait_file(chan,ints, "digits/oh",lang);
3059 if (tm.tm_hour == 0) {
3060 res = wait_file(chan,ints, "digits/oh",lang);
3064 if (tm.tm_hour != 0) {
3065 int remainder = tm.tm_hour;
3066 if (tm.tm_hour > 20) {
3067 res = wait_file(chan,ints, "digits/20",lang);
3071 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
3072 res = wait_file(chan,ints,nextmsg,lang);
3080 if (tm.tm_min == 0) {
3081 if (format[offset] == 'M') {
3082 res = wait_file(chan, ints, "digits/oclock", lang);
3084 res = wait_file(chan, ints, "digits/hundred", lang);
3086 } else if (tm.tm_min < 10) {
3087 res = wait_file(chan,ints, "digits/oh",lang);
3089 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
3090 res = wait_file(chan,ints,nextmsg,lang);
3093 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
3099 if (tm.tm_hour > 11)
3100 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
3102 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
3103 res = wait_file(chan,ints,nextmsg,lang);
3106 /* Shorthand for "Today", "Yesterday", or ABdY */
3107 /* XXX As emphasized elsewhere, this should the native way in your
3108 * language to say the date, with changes in what you say, depending
3109 * upon how recent the date is. XXX */