2 * Asterisk -- A telephony toolkit for Linux.
4 * Say numbers and dates (maybe words one day too)
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <sys/types.h>
17 #include <netinet/in.h>
20 #include <asterisk/file.h>
21 #include <asterisk/channel.h>
22 #include <asterisk/logger.h>
23 #include <asterisk/say.h>
24 #include <asterisk/lock.h>
25 #include <asterisk/localtime.h>
30 /* Forward declaration */
31 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang);
33 int ast_say_digit_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
35 /* XXX Merge with full version? XXX */
39 while(fn2[num] && !res) {
43 snprintf(fn, sizeof(fn), "digits/star");
46 snprintf(fn, sizeof(fn), "digits/pound");
49 if((fn2[num] >= '0') && (fn2[num] <= '9')){ /* Must be in {0-9} */
50 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
53 if(strlen(fn)){ /* if length == 0, then skip this digit as it is invalid */
54 res = ast_streamfile(chan, fn, lang);
56 res = ast_waitstream(chan, ints);
64 int ast_say_character_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
66 /* XXX Merge with full version? XXX */
71 while(fn2[num] && !res) {
75 snprintf(fn, sizeof(fn), "digits/star");
78 snprintf(fn, sizeof(fn), "digits/pound");
90 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
93 strncpy(fn, "letters/exclaimation-point", sizeof(fn));
96 strncpy(fn, "letters/at", sizeof(fn));
99 strncpy(fn, "letters/dollar", sizeof(fn));
102 strncpy(fn, "letters/dash", sizeof(fn));
105 strncpy(fn, "letters/dot", sizeof(fn));
108 strncpy(fn, "letters/equals", sizeof(fn));
111 strncpy(fn, "letters/plus", sizeof(fn));
114 strncpy(fn, "letters/slash", sizeof(fn));
117 strncpy(fn, "letters/space", sizeof(fn));
121 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
122 snprintf(fn, sizeof(fn), "letters/%c", ltr);
124 if(strlen(fn)){ /* if length == 0, then skip this digit as it is invalid */
125 res = ast_streamfile(chan, fn, lang);
127 res = ast_waitstream(chan, ints);
128 } ast_stopstream(chan);
134 int ast_say_phonetic_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
136 /* XXX Merge with full version? XXX */
144 /* while(fn2[num] && !res) { */
149 snprintf(fn, sizeof(fn), "digits/star");
152 snprintf(fn, sizeof(fn), "digits/pound");
163 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
166 strncpy(fn, "exclaimation-point", sizeof(fn));
169 strncpy(fn, "at", sizeof(fn));
172 strncpy(fn, "dollar", sizeof(fn));
175 strncpy(fn, "dash", sizeof(fn));
178 strncpy(fn, "dot", sizeof(fn));
181 strncpy(fn, "equals", sizeof(fn));
184 strncpy(fn, "plus", sizeof(fn));
187 strncpy(fn, "slash", sizeof(fn));
190 strncpy(fn, "space", sizeof(fn));
194 /* check if we have 2 chars after the % */
195 if (strlen(fn2)>num+2)
200 if (sscanf(hex,"%x", &temp))
201 { /* Hex to char convertion successfull */
205 { /* If it is a percent, play it now */
206 strncpy(fn, "percent", sizeof(fn));
210 /* check for invalid characters */
211 if ((temp<32) || (temp>126))
220 default: /* '9' falls through to here, too */
221 ltr = tolower(fn2[num]);
222 snprintf(fn, sizeof(fn), "phonetic/%c_p", ltr);
226 res = ast_streamfile(chan, fn, lang);
228 res = ast_waitstream(chan, ints);
229 ast_stopstream(chan);
236 int ast_say_digit_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
241 while(fn2[num] && !res) {
242 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
243 res = ast_streamfile(chan, fn, lang);
245 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
246 ast_stopstream(chan);
252 int ast_say_character_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
258 while(fn2[num] && !res) {
261 snprintf(fn, sizeof(fn), "digits/star");
264 snprintf(fn, sizeof(fn), "digits/pound");
276 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
279 strncpy(fn, "exclaimation-point", sizeof(fn));
282 strncpy(fn, "at", sizeof(fn));
285 strncpy(fn, "dollar", sizeof(fn));
288 strncpy(fn, "dash", sizeof(fn));
291 strncpy(fn, "dot", sizeof(fn));
294 strncpy(fn, "equals", sizeof(fn));
297 strncpy(fn, "plus", sizeof(fn));
300 strncpy(fn, "slash", sizeof(fn));
303 strncpy(fn, "space", sizeof(fn));
307 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
308 snprintf(fn, sizeof(fn), "letters/%c", ltr);
310 /* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
311 res = ast_streamfile(chan, fn, lang);
313 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
314 ast_stopstream(chan);
320 int ast_say_phonetic_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
326 while(fn2[num] && !res) {
329 snprintf(fn, sizeof(fn), "digits/star");
332 snprintf(fn, sizeof(fn), "digits/pound");
343 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
346 strncpy(fn, "exclaimation-point", sizeof(fn));
349 strncpy(fn, "at", sizeof(fn));
352 strncpy(fn, "dollar", sizeof(fn));
355 strncpy(fn, "dash", sizeof(fn));
358 strncpy(fn, "dot", sizeof(fn));
361 strncpy(fn, "equals", sizeof(fn));
364 strncpy(fn, "plus", sizeof(fn));
367 strncpy(fn, "slash", sizeof(fn));
370 strncpy(fn, "space", sizeof(fn));
372 default: /* '9' falls here... */
374 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
375 snprintf(fn, sizeof(fn), "phonetic/%c", ltr);
377 /* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
378 res = ast_streamfile(chan, fn, lang);
380 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
381 ast_stopstream(chan);
387 int ast_say_digits(struct ast_channel *chan, int num, char *ints, char *lang)
389 /* XXX Should I be merged with say_digits_full XXX */
391 snprintf(fn2, sizeof(fn2), "%d", num);
392 return ast_say_digit_str(chan, fn2, ints, lang);
395 int ast_say_digits_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd)
398 snprintf(fn2, sizeof(fn2), "%d", num);
399 return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd);
402 /* Forward declarations */
403 /* Syntaxes supported, not really language codes.
406 en - English, Swedish, Norwegian
414 For Portuguese, we're using m & f options to saynumber() to indicate if the gender is masculine or feminine.
415 For Danish, we're using c & n options to saynumber() to indicate if the gender is commune or neutrum.
416 This still needs to be implemented for French, Spanish & German.
418 Date/Time functions currently have less languages supported than saynumber().
420 Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
422 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
424 Portuguese sound files needed for Time/Date functions:
436 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
437 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
438 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
439 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
440 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
441 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
442 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
443 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
444 static int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
445 static int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
446 static int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
447 static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
448 static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
449 static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
450 static int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
451 static int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
452 static int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
453 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
454 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
455 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
456 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
457 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
459 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang)
462 if ((res = ast_streamfile(chan, file, lang)))
463 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
465 res = ast_waitstream(chan, ints);
469 /*--- ast_say_number_full: call language-specific functions */
470 /* Called from AGI */
471 int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
473 char *options=(char *) NULL; /* While waiting for a general hack for agi */
475 if (!strcasecmp(language, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) {
476 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
477 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
478 return(ast_say_number_full_fr(chan, num, ints, language, audiofd, ctrlfd));
479 } else if (!strcasecmp(language, "de") ) { /* German syntax */
480 return(ast_say_number_full_de(chan, num, ints, language, audiofd, ctrlfd));
481 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
482 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
483 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
484 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
485 } else if (!strcasecmp(language, "pt") ) { /* Portuguese syntax */
486 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
487 } else if (!strcasecmp(language, "es") ) { /* Spanish syntax */
488 return(ast_say_number_full_es(chan, num, ints, language, audiofd, ctrlfd));
489 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
490 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
493 /* Default to english */
494 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
497 /*--- ast_say_number: call language-specific functions without file descriptors */
498 int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language, char *options)
500 if (!strcasecmp(language, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) {
501 return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
504 if (!strcasecmp(language, "fr")) { /* French syntax */
505 return(ast_say_number_full_fr(chan, num, ints, language, -1, -1));
506 } else if (!strcasecmp(language, "da")) { /* Danish syntax */
507 return(ast_say_number_full_da(chan, num, ints, language, options, -1, -1));
508 } else if (!strcasecmp(language, "de")) { /* German syntax */
509 return(ast_say_number_full_de(chan, num, ints, language, -1, -1));
510 } else if (!strcasecmp(language, "it")) { /* Italian syntax */
511 return(ast_say_number_full_it(chan, num, ints, language, -1, -1));
512 } else if (!strcasecmp(language, "pt")) { /* Portuguese syntax */
513 return(ast_say_number_full_pt(chan, num, ints, language, options, -1, -1));
514 } else if (!strcasecmp(language, "nl")) { /* Dutch syntax */
515 return(ast_say_number_full_nl(chan, num, ints, language, -1, -1));
516 } else if (!strcasecmp(language, "es")) { /* Spanish syntax */
517 return(ast_say_number_full_es(chan, num, ints, language, -1, -1));
520 /* Default to english */
521 return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
524 /*--- ast_say_number_full_en: English syntax */
525 /* This is the default syntax, if no other syntax defined in this file is used */
526 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
532 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
534 while(!res && (num || playh)) {
536 snprintf(fn, sizeof(fn), "digits/hundred");
538 } else if (num < 20) {
539 snprintf(fn, sizeof(fn), "digits/%d", num);
541 } else if (num < 100) {
542 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
543 num -= ((num / 10) * 10);
546 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
548 num -= ((num / 100) * 100);
550 if (num < 1000000) { /* 1,000,000 */
551 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
555 snprintf(fn, sizeof(fn), "digits/thousand");
557 if (num < 1000000000) { /* 1,000,000,000 */
558 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
562 snprintf(fn, sizeof(fn), "digits/million");
564 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
571 if(!ast_streamfile(chan, fn, language)) {
572 if (audiofd && ctrlfd)
573 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
575 res = ast_waitstream(chan, ints);
577 ast_stopstream(chan);
585 /*--- ast_say_number_full_da: Danish syntax */
587 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
589 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
594 int cn = 1; /* +1 = Commune; -1 = Neutrum */
597 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
599 if (options && !strncasecmp(options, "n",1)) cn = -1;
601 while(!res && (num || playh || playa )) {
602 /* The grammar for Danish numbers is the same as for English except
604 * - 1 exists in both commune ("en", file "1N") and neutrum ("et", file "1")
605 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
606 * "one-and twenty" and 68 is "eight-and sixty".
607 * - "million" is different in singular and plural form
608 * - numbers > 1000 with zero as the third digit from last have an
609 * "and" before the last two digits, i.e. 2034 is "two thousand and
610 * four-and thirty" and 1000012 is "one million and twelve".
613 snprintf(fn, sizeof(fn), "digits/hundred");
616 snprintf(fn, sizeof(fn), "digits/and");
618 } else if (num == 1 && cn == -1) {
619 snprintf(fn, sizeof(fn), "digits/1N");
621 } else if (num < 20) {
622 snprintf(fn, sizeof(fn), "digits/%d", num);
624 } else if (num < 100) {
627 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
630 snprintf(fn, sizeof(fn), "digits/%d", num);
635 int hundreds = num / 100;
637 snprintf(fn, sizeof(fn), "digits/1N");
639 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
642 num -= 100 * hundreds;
648 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
652 snprintf(fn, sizeof(fn), "digits/thousand");
654 if (num < 1000000000) {
655 int millions = num / 1000000;
656 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
660 snprintf(fn, sizeof(fn), "digits/million");
662 snprintf(fn, sizeof(fn), "digits/millions");
665 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
669 if (num && num < 100)
674 if(!ast_streamfile(chan, fn, language)) {
675 if (audiofd && ctrlfd)
676 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
678 res = ast_waitstream(chan, ints);
680 ast_stopstream(chan);
686 /*--- ast_say_number_full_de: German syntax */
688 In addition to English, the following sounds are required: "millions", "and" and "1-and" through "9-and"
690 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
697 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
699 while(!res && (num || playh || playa )) {
700 /* The grammar for German numbers is the same as for English except
702 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
703 * "one-and twenty" and 68 is "eight-and sixty".
704 * - "million" is different in singular and plural form
705 * - numbers > 1000 with zero as the third digit from last have an
706 * "and" before the last two digits, i.e. 2034 is "two thousand and
707 * four-and thirty" and 1000012 is "one million and twelve".
710 snprintf(fn, sizeof(fn), "digits/hundred");
713 snprintf(fn, sizeof(fn), "digits/and");
715 } else if (num < 20) {
716 snprintf(fn, sizeof(fn), "digits/%d", num);
718 } else if (num < 100) {
721 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
724 snprintf(fn, sizeof(fn), "digits/%d", num);
729 int hundreds = num / 100;
731 snprintf(fn, sizeof(fn), "digits/1N");
733 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
735 num -= 100 * hundreds;
740 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
744 snprintf(fn, sizeof(fn), "digits/thousand");
746 if (num < 1000000000) {
747 int millions = num / 1000000;
748 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
752 snprintf(fn, sizeof(fn), "digits/million");
754 snprintf(fn, sizeof(fn), "digits/millions");
757 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
761 if (num && num < 100)
766 if(!ast_streamfile(chan, fn, language)) {
767 if (audiofd && ctrlfd)
768 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
770 res = ast_waitstream(chan, ints);
772 ast_stopstream(chan);
778 /*--- ast_say_number_full_es: spanish syntax */
780 Requires a few new audios:
781 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
783 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
789 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
790 while (!res && num) {
792 snprintf(fn, sizeof(fn), "digits/y");
794 } else if (num < 31) {
795 snprintf(fn, sizeof(fn), "digits/%d", num);
797 } else if (num < 100) {
798 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
799 num -= ((num/10)*10);
802 } else if (num == 100) {
803 snprintf(fn, sizeof(fn), "digits/cien");
807 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
808 num -= ((num/100)*100);
811 res = ast_say_number_full_es(chan, num / 1000, ints, language, audiofd, ctrlfd);
815 snprintf(fn, sizeof(fn), "digits/mil");
817 if (num < 2147483640) {
818 res = ast_say_number_full_es(chan, num / 1000000, ints, language, audiofd, ctrlfd);
821 if ((num/1000000) == 1) {
822 snprintf(fn, sizeof(fn), "digits/millon");
824 snprintf(fn, sizeof(fn), "digits/millones");
828 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
836 if(!ast_streamfile(chan, fn, language)) {
837 if (audiofd && ctrlfd)
838 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
840 res = ast_waitstream(chan, ints);
842 ast_stopstream(chan);
851 /*--- ast_say_number_full_fr: French syntax */
852 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
859 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
860 while(!res && (num || playh || playa)) {
862 snprintf(fn, sizeof(fn), "digits/hundred");
865 snprintf(fn, sizeof(fn), "digits/et");
867 } else if (num < 21) {
868 snprintf(fn, sizeof(fn), "digits/%d", num);
870 } else if (num < 70) {
871 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
872 if ((num % 10) == 1) playa++;
874 } else if (num < 80) {
875 snprintf(fn, sizeof(fn), "digits/60");
876 if ((num % 10) == 1) playa++;
878 } else if (num < 100) {
879 snprintf(fn, sizeof(fn), "digits/80");
881 } else if (num < 200) {
882 snprintf(fn, sizeof(fn), "digits/hundred");
884 } else if (num < 1000) {
885 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
888 } else if (num < 2000) {
889 snprintf(fn, sizeof(fn), "digits/thousand");
891 } else if (num < 1000000) {
892 res = ast_say_number_full_fr(chan, num / 1000, ints, language, audiofd, ctrlfd);
895 snprintf(fn, sizeof(fn), "digits/thousand");
897 } else if (num < 1000000000) {
898 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, audiofd, ctrlfd);
901 snprintf(fn, sizeof(fn), "digits/million");
904 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
908 if(!ast_streamfile(chan, fn, language)) {
909 if (audiofd && ctrlfd)
910 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
912 res = ast_waitstream(chan, ints);
914 ast_stopstream(chan);
920 /*--- ast_say_number_full_it: Italian */
921 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
929 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
934 Like english, numbers up to 20 are a single 'word', and others
935 compound, but with exceptions.
936 For example 21 is not twenty-one, but there is a single word in 'it'.
937 Idem for 28 (ie when a the 2nd part of a compund number
940 There are exceptions also for hundred, thousand and million.
941 In english 100 = one hundred, 200 is two hundred.
942 In italian 100 = cento , like to say hundred (without one),
943 200 and more are like english.
945 Same applies for thousand:
946 1000 is one thousand in en, 2000 is two thousand.
947 In it we have 1000 = mille , 2000 = 2 mila
949 For million(s) we use the plural, if more than one
950 Also, one million is abbreviated in it, like on-million,
951 or 'un milione', not 'uno milione'.
952 So the right file is provided.
955 while(!res && (num || playh)) {
957 snprintf(fn, sizeof(fn), "digits/hundred");
959 } else if (num < 20) {
960 snprintf(fn, sizeof(fn), "digits/%d", num);
962 } else if (num == 21) {
963 snprintf(fn, sizeof(fn), "digits/%d", num);
965 } else if (num == 28) {
966 snprintf(fn, sizeof(fn), "digits/%d", num);
968 } else if (num == 31) {
969 snprintf(fn, sizeof(fn), "digits/%d", num);
971 } else if (num == 38) {
972 snprintf(fn, sizeof(fn), "digits/%d", num);
974 } else if (num == 41) {
975 snprintf(fn, sizeof(fn), "digits/%d", num);
977 } else if (num == 48) {
978 snprintf(fn, sizeof(fn), "digits/%d", num);
980 } else if (num == 51) {
981 snprintf(fn, sizeof(fn), "digits/%d", num);
983 } else if (num == 58) {
984 snprintf(fn, sizeof(fn), "digits/%d", num);
986 } else if (num == 61) {
987 snprintf(fn, sizeof(fn), "digits/%d", num);
989 } else if (num == 68) {
990 snprintf(fn, sizeof(fn), "digits/%d", num);
992 } else if (num == 71) {
993 snprintf(fn, sizeof(fn), "digits/%d", num);
995 } else if (num == 78) {
996 snprintf(fn, sizeof(fn), "digits/%d", num);
998 } else if (num == 81) {
999 snprintf(fn, sizeof(fn), "digits/%d", num);
1001 } else if (num == 88) {
1002 snprintf(fn, sizeof(fn), "digits/%d", num);
1004 } else if (num == 91) {
1005 snprintf(fn, sizeof(fn), "digits/%d", num);
1007 } else if (num == 98) {
1008 snprintf(fn, sizeof(fn), "digits/%d", num);
1010 } else if (num < 100) {
1011 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1012 num -= ((num / 10) * 10);
1015 if ((num / 100) > 1) {
1016 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1019 snprintf(fn, sizeof(fn), "digits/hundred");
1021 num -= ((num / 100) * 100);
1023 if (num < 1000000) { /* 1,000,000 */
1025 res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd);
1030 if ((tempnum / 1000) < 2)
1031 snprintf(fn, sizeof(fn), "digits/thousand");
1032 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1033 snprintf(fn, sizeof(fn), "digits/thousands");
1035 if (num < 1000000000) { /* 1,000,000,000 */
1036 if ((num / 1000000) > 1)
1037 res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1041 num = num % 1000000;
1042 if ((tempnum / 1000000) < 2)
1043 snprintf(fn, sizeof(fn), "digits/million");
1045 snprintf(fn, sizeof(fn), "digits/millions");
1047 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1054 if(!ast_streamfile(chan, fn, language)) {
1055 if (audiofd && ctrlfd)
1056 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1058 res = ast_waitstream(chan, ints);
1060 ast_stopstream(chan);
1066 /*--- ast_say_number_full_nl: dutch syntax */
1067 /* New files: digits/nl-en
1069 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
1076 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1077 while (!res && (num || playh )) {
1079 snprintf(fn, sizeof(fn), "digits/hundred");
1081 } else if (num < 20) {
1082 snprintf(fn, sizeof(fn), "digits/%d", num);
1084 } else if (num < 100) {
1087 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1091 snprintf(fn, sizeof(fn), "digits/nl-en");
1093 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1098 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1100 num -= ((num / 100) * 100);
1102 if (num < 1000000) { /* 1,000,000 */
1103 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1107 snprintf(fn, sizeof(fn), "digits/thousand");
1109 if (num < 1000000000) { /* 1,000,000,000 */
1110 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1113 num = num % 1000000;
1114 snprintf(fn, sizeof(fn), "digits/million");
1116 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1124 if(!ast_streamfile(chan, fn, language)) {
1125 if (audiofd && ctrlfd)
1126 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1128 res = ast_waitstream(chan, ints);
1130 ast_stopstream(chan);
1136 /* ast_say_number_full_pt: Portuguese syntax */
1137 /* Extra sounds needed: */
1138 /* For feminin all sound files end with F */
1139 /* 100E for 100+ something */
1140 /* 1000000S for plural */
1141 /* pt-e for 'and' */
1142 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1146 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1150 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1152 if (options && !strncasecmp(options, "f",1))
1155 while(!res && num ) {
1157 if ((num == 1 || num == 2) && (mf < 0))
1158 snprintf(fn, sizeof(fn), "digits/%dF", num);
1160 snprintf(fn, sizeof(fn), "digits/%d", num);
1162 } else if (num < 100) {
1163 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1167 } else if (num < 1000) {
1169 snprintf(fn, sizeof(fn), "digits/100");
1171 snprintf(fn, sizeof(fn), "digits/100E");
1173 if (mf < 0 && num > 199)
1174 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1176 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1181 } else if (num < 1000000) {
1183 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1187 snprintf(fn, sizeof(fn), "digits/1000");
1188 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
1191 } else if (num < 1000000000) {
1192 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
1196 snprintf(fn, sizeof(fn), "digits/1000000");
1198 snprintf(fn, sizeof(fn), "digits/1000000S");
1200 if ((num % 1000000) &&
1202 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
1203 // no hundreds and below
1204 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
1206 num = num % 1000000;
1208 if (!res && playh) {
1209 res = wait_file(chan, ints, "digits/pt-e", language);
1210 ast_stopstream(chan);
1214 if(!ast_streamfile(chan, fn, language)) {
1215 if (audiofd && ctrlfd)
1216 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else
1217 res = ast_waitstream(chan, ints);
1219 ast_stopstream(chan);
1225 int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang)
1227 if (!strcasecmp(lang,"en") ) { /* English syntax */
1228 return(ast_say_date_en(chan, t, ints, lang));
1229 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
1230 return(ast_say_date_nl(chan, t, ints, lang));
1231 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
1232 return(ast_say_date_pt(chan, t, ints, lang));
1235 /* Default to English */
1236 return(ast_say_date_en(chan, t, ints, lang));
1240 /* English syntax */
1241 int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
1246 ast_localtime(&t,&tm,NULL);
1248 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1249 res = ast_streamfile(chan, fn, lang);
1251 res = ast_waitstream(chan, ints);
1254 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1255 res = ast_streamfile(chan, fn, lang);
1257 res = ast_waitstream(chan, ints);
1260 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1262 res = ast_waitstream(chan, ints);
1264 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1269 int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
1274 ast_localtime(&t,&tm,NULL);
1276 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1277 res = ast_streamfile(chan, fn, lang);
1279 res = ast_waitstream(chan, ints);
1282 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1284 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1285 res = ast_streamfile(chan, fn, lang);
1287 res = ast_waitstream(chan, ints);
1290 res = ast_waitstream(chan, ints);
1292 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1296 /* Portuguese syntax */
1297 int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
1302 ast_localtime(&t,&tm,NULL);
1303 localtime_r(&t,&tm);
1304 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1306 res = wait_file(chan, ints, fn, lang);
1308 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1310 res = wait_file(chan, ints, "digits/pt-de", lang);
1311 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1313 res = wait_file(chan, ints, fn, lang);
1315 res = wait_file(chan, ints, "digits/pt-de", lang);
1317 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1322 int ast_say_date_with_format(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1324 if (!strcasecmp(lang, "en") ) { /* English syntax */
1325 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1326 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
1327 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
1328 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
1329 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
1332 /* Default to English */
1333 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1336 /* English syntax */
1337 int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1340 int res=0, offset, sndoffset;
1341 char sndfile[256], nextmsg[256];
1343 ast_localtime(&time,&tm,timezone);
1345 for (offset=0 ; format[offset] != '\0' ; offset++) {
1346 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1347 switch (format[offset]) {
1348 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
1350 /* Literal name of a sound file */
1352 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1353 sndfile[sndoffset] = format[offset];
1354 sndfile[sndoffset] = '\0';
1355 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1356 res = wait_file(chan,ints,nextmsg,lang);
1360 /* Sunday - Saturday */
1361 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1362 res = wait_file(chan,ints,nextmsg,lang);
1367 /* January - December */
1368 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1369 res = wait_file(chan,ints,nextmsg,lang);
1373 /* First - Thirtyfirst */
1374 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1375 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
1376 res = wait_file(chan,ints,nextmsg,lang);
1377 } else if (tm.tm_mday == 31) {
1378 /* "Thirty" and "first" */
1379 res = wait_file(chan,ints, "digits/30",lang);
1381 res = wait_file(chan,ints, "digits/h-1",lang);
1384 /* Between 21 and 29 - two sounds */
1385 res = wait_file(chan,ints, "digits/20",lang);
1387 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
1388 res = wait_file(chan,ints,nextmsg,lang);
1394 if (tm.tm_year > 99) {
1395 res = wait_file(chan,ints, "digits/2",lang);
1397 res = wait_file(chan,ints, "digits/thousand",lang);
1399 if (tm.tm_year > 100) {
1401 /* This works until the end of 2020 */
1402 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1403 res = wait_file(chan,ints,nextmsg,lang);
1407 if (tm.tm_year < 1) {
1408 /* I'm not going to handle 1900 and prior */
1409 /* We'll just be silent on the year, instead of bombing out. */
1411 res = wait_file(chan,ints, "digits/19",lang);
1413 if (tm.tm_year <= 9) {
1415 res = wait_file(chan,ints, "digits/oh",lang);
1417 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1418 res = wait_file(chan,ints,nextmsg,lang);
1420 } else if (tm.tm_year <= 20) {
1422 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1423 res = wait_file(chan,ints,nextmsg,lang);
1427 ten = tm.tm_year / 10;
1428 one = tm.tm_year % 10;
1429 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1430 res = wait_file(chan,ints,nextmsg,lang);
1433 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1434 res = wait_file(chan,ints,nextmsg,lang);
1445 if (tm.tm_hour == 0)
1446 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1447 else if (tm.tm_hour > 12)
1448 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1450 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1451 res = wait_file(chan,ints,nextmsg,lang);
1456 if (format[offset] == 'H') {
1458 if (tm.tm_hour < 10) {
1459 res = wait_file(chan,ints, "digits/oh",lang);
1463 if (tm.tm_hour == 0) {
1464 res = wait_file(chan,ints, "digits/oh",lang);
1468 if (tm.tm_hour != 0) {
1469 int remainder = tm.tm_hour;
1470 if (tm.tm_hour > 20) {
1471 res = wait_file(chan,ints, "digits/20",lang);
1475 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1476 res = wait_file(chan,ints,nextmsg,lang);
1483 if (tm.tm_min == 0) {
1484 res = wait_file(chan,ints, "digits/oclock",lang);
1485 } else if (tm.tm_min < 10) {
1486 res = wait_file(chan,ints, "digits/oh",lang);
1488 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1489 res = wait_file(chan,ints,nextmsg,lang);
1491 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
1492 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1493 res = wait_file(chan,ints,nextmsg,lang);
1496 ten = (tm.tm_min / 10) * 10;
1497 one = (tm.tm_min % 10);
1498 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1499 res = wait_file(chan,ints,nextmsg,lang);
1501 /* Fifty, not fifty-zero */
1503 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1504 res = wait_file(chan,ints,nextmsg,lang);
1512 if (tm.tm_hour > 11)
1513 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1515 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1516 res = wait_file(chan,ints,nextmsg,lang);
1519 /* Shorthand for "Today", "Yesterday", or ABdY */
1525 gettimeofday(&now,NULL);
1526 ast_localtime(&now.tv_sec,&tmnow,timezone);
1527 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1528 /* In any case, it saves not having to do ast_mktime() */
1529 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1530 if (beg_today < time) {
1532 res = wait_file(chan,ints, "digits/today",lang);
1533 } else if (beg_today - 86400 < time) {
1535 res = wait_file(chan,ints, "digits/yesterday",lang);
1537 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1542 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1548 gettimeofday(&now,NULL);
1549 ast_localtime(&now.tv_sec,&tmnow,timezone);
1550 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1551 /* In any case, it saves not having to do ast_mktime() */
1552 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1553 if (beg_today < time) {
1555 } else if ((beg_today - 86400) < time) {
1557 res = wait_file(chan,ints, "digits/yesterday",lang);
1558 } else if (beg_today - 86400 * 6 < time) {
1559 /* Within the last week */
1560 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1562 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1567 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1571 if (tm.tm_sec == 0) {
1572 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1573 res = wait_file(chan,ints,nextmsg,lang);
1574 } else if (tm.tm_sec < 10) {
1575 res = wait_file(chan,ints, "digits/oh",lang);
1577 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1578 res = wait_file(chan,ints,nextmsg,lang);
1580 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1581 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1582 res = wait_file(chan,ints,nextmsg,lang);
1585 ten = (tm.tm_sec / 10) * 10;
1586 one = (tm.tm_sec % 10);
1587 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1588 res = wait_file(chan,ints,nextmsg,lang);
1590 /* Fifty, not fifty-zero */
1592 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1593 res = wait_file(chan,ints,nextmsg,lang);
1599 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1603 /* Just ignore spaces and tabs */
1606 /* Unknown character */
1607 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1609 /* Jump out on DTMF */
1618 int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1621 int res=0, offset, sndoffset;
1622 char sndfile[256], nextmsg[256];
1624 ast_localtime(&time,&tm,timezone);
1626 for (offset=0 ; format[offset] != '\0' ; offset++) {
1627 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1628 switch (format[offset]) {
1629 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
1631 /* Literal name of a sound file */
1633 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1634 sndfile[sndoffset] = format[offset];
1635 sndfile[sndoffset] = '\0';
1636 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1637 res = wait_file(chan,ints,nextmsg,lang);
1641 /* Sunday - Saturday */
1642 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1643 res = wait_file(chan,ints,nextmsg,lang);
1648 /* January - December */
1649 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1650 res = wait_file(chan,ints,nextmsg,lang);
1654 /* First - Thirtyfirst */
1655 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
1659 if (tm.tm_year > 99) {
1660 res = wait_file(chan,ints, "digits/2",lang);
1662 res = wait_file(chan,ints, "digits/thousand",lang);
1664 if (tm.tm_year > 100) {
1666 /* This works until the end of 2020 */
1667 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1668 res = wait_file(chan,ints,nextmsg,lang);
1672 if (tm.tm_year < 1) {
1673 /* I'm not going to handle 1900 and prior */
1674 /* We'll just be silent on the year, instead of bombing out. */
1676 res = wait_file(chan,ints, "digits/19",lang);
1678 if (tm.tm_year <= 9) {
1680 res = wait_file(chan,ints, "digits/oh",lang);
1682 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1683 res = wait_file(chan,ints,nextmsg,lang);
1685 } else if (tm.tm_year <= 20) {
1687 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1688 res = wait_file(chan,ints,nextmsg,lang);
1692 ten = tm.tm_year / 10;
1693 one = tm.tm_year % 10;
1694 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1695 res = wait_file(chan,ints,nextmsg,lang);
1698 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1699 res = wait_file(chan,ints,nextmsg,lang);
1710 if (tm.tm_hour == 0)
1711 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1712 else if (tm.tm_hour > 12)
1713 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1715 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1716 res = wait_file(chan,ints,nextmsg,lang);
1721 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
1723 res = wait_file(chan,ints, "digits/nl-uur",lang);
1728 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
1733 if (tm.tm_hour > 11)
1734 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1736 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1737 res = wait_file(chan,ints,nextmsg,lang);
1740 /* Shorthand for "Today", "Yesterday", or ABdY */
1746 gettimeofday(&now,NULL);
1747 ast_localtime(&now.tv_sec,&tmnow,timezone);
1748 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1749 /* In any case, it saves not having to do ast_mktime() */
1750 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1751 if (beg_today < time) {
1753 res = wait_file(chan,ints, "digits/today",lang);
1754 } else if (beg_today - 86400 < time) {
1756 res = wait_file(chan,ints, "digits/yesterday",lang);
1758 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1763 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1769 gettimeofday(&now,NULL);
1770 ast_localtime(&now.tv_sec,&tmnow,timezone);
1771 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1772 /* In any case, it saves not having to do ast_mktime() */
1773 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1774 if (beg_today < time) {
1776 } else if ((beg_today - 86400) < time) {
1778 res = wait_file(chan,ints, "digits/yesterday",lang);
1779 } else if (beg_today - 86400 * 6 < time) {
1780 /* Within the last week */
1781 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1783 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1788 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1792 if (tm.tm_sec == 0) {
1793 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1794 res = wait_file(chan,ints,nextmsg,lang);
1795 } else if (tm.tm_sec < 10) {
1796 res = wait_file(chan,ints, "digits/oh",lang);
1798 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1799 res = wait_file(chan,ints,nextmsg,lang);
1801 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1802 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1803 res = wait_file(chan,ints,nextmsg,lang);
1806 ten = (tm.tm_sec / 10) * 10;
1807 one = (tm.tm_sec % 10);
1808 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1809 res = wait_file(chan,ints,nextmsg,lang);
1811 /* Fifty, not fifty-zero */
1813 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1814 res = wait_file(chan,ints,nextmsg,lang);
1820 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1824 /* Just ignore spaces and tabs */
1827 /* Unknown character */
1828 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1830 /* Jump out on DTMF */
1838 /* Portuguese syntax */
1839 int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1842 int res=0, offset, sndoffset;
1843 char sndfile[256], nextmsg[256];
1845 ast_localtime(&time,&tm,timezone);
1847 for (offset=0 ; format[offset] != '\0' ; offset++) {
1848 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1849 switch (format[offset]) {
1850 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
1852 /* Literal name of a sound file */
1854 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1855 sndfile[sndoffset] = format[offset];
1856 sndfile[sndoffset] = '\0';
1857 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
1858 res = wait_file(chan,ints,nextmsg,lang);
1862 /* Sunday - Saturday */
1863 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1864 res = wait_file(chan,ints,nextmsg,lang);
1869 /* January - December */
1870 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1871 res = wait_file(chan,ints,nextmsg,lang);
1875 /* First - Thirtyfirst */
1876 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1880 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1885 if (tm.tm_hour == 0) {
1886 if (format[offset] == 'I')
1887 res = wait_file(chan, ints, "digits/pt-ah", lang);
1889 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
1891 else if (tm.tm_hour == 12) {
1892 if (format[offset] == 'I')
1893 res = wait_file(chan, ints, "digits/pt-ao", lang);
1895 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
1898 if (format[offset] == 'I') {
1899 res = wait_file(chan, ints, "digits/pt-ah", lang);
1900 if ((tm.tm_hour % 12) != 1)
1902 res = wait_file(chan, ints, "digits/pt-sss", lang);
1905 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
1911 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
1913 if (tm.tm_hour != 0) {
1914 int remainder = tm.tm_hour;
1915 if (tm.tm_hour > 20) {
1916 res = wait_file(chan,ints, "digits/20",lang);
1920 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1921 res = wait_file(chan,ints,nextmsg,lang);
1928 if (tm.tm_min == 0) {
1929 res = wait_file(chan, ints, "digits/pt-hora", lang);
1930 if (tm.tm_hour != 1)
1932 res = wait_file(chan, ints, "digits/pt-sss", lang); } else {
1933 res = wait_file(chan,ints,"digits/pt-e",lang);
1935 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
1941 if (tm.tm_hour > 12)
1942 res = wait_file(chan, ints, "digits/p-m", lang);
1943 else if (tm.tm_hour && tm.tm_hour < 12)
1944 res = wait_file(chan, ints, "digits/a-m", lang);
1947 /* Shorthand for "Today", "Yesterday", or ABdY */
1953 gettimeofday(&now,NULL);
1954 ast_localtime(&now.tv_sec,&tmnow,timezone);
1955 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1956 /* In any case, it saves not having to do ast_mktime() */
1957 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1958 if (beg_today < time) {
1960 res = wait_file(chan,ints, "digits/today",lang);
1961 } else if (beg_today - 86400 < time) {
1963 res = wait_file(chan,ints, "digits/yesterday",lang);
1965 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
1970 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1976 gettimeofday(&now,NULL);
1977 ast_localtime(&now.tv_sec,&tmnow,timezone);
1978 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1979 /* In any case, it saves not having to do ast_mktime() */
1980 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1981 if (beg_today < time) {
1983 } else if ((beg_today - 86400) < time) {
1985 res = wait_file(chan,ints, "digits/yesterday",lang);
1986 } else if (beg_today - 86400 * 6 < time) {
1987 /* Within the last week */
1988 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1990 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
1995 res = ast_say_date_with_format(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
1999 if (tm.tm_sec == 0) {
2000 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2001 res = wait_file(chan,ints,nextmsg,lang);
2002 } else if (tm.tm_sec < 10) {
2003 res = wait_file(chan,ints, "digits/oh",lang);
2005 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2006 res = wait_file(chan,ints,nextmsg,lang);
2008 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2009 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2010 res = wait_file(chan,ints,nextmsg,lang);
2013 ten = (tm.tm_sec / 10) * 10;
2014 one = (tm.tm_sec % 10);
2015 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2016 res = wait_file(chan,ints,nextmsg,lang);
2018 /* Fifty, not fifty-zero */
2020 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2021 res = wait_file(chan,ints,nextmsg,lang);
2027 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2031 /* Just ignore spaces and tabs */
2034 /* Unknown character */
2035 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2037 /* Jump out on DTMF */
2045 int ast_say_time(struct ast_channel *chan, time_t t, char *ints, char *lang)
2047 if (!strcasecmp(lang, "en") ) { /* English syntax */
2048 return(ast_say_time_en(chan, t, ints, lang));
2049 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2050 return(ast_say_time_nl(chan, t, ints, lang));
2051 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2052 return(ast_say_time_pt(chan, t, ints, lang));
2055 /* Default to English */
2056 return(ast_say_time_en(chan, t, ints, lang));
2059 /* English syntax */
2060 int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2065 localtime_r(&t,&tm);
2069 else if (hour == 12)
2071 else if (hour > 12) {
2076 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2078 if (tm.tm_min > 9) {
2080 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2081 } else if (tm.tm_min) {
2083 res = ast_streamfile(chan, "digits/oh", lang);
2085 res = ast_waitstream(chan, ints);
2087 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2090 res = ast_streamfile(chan, "digits/oclock", lang);
2092 res = ast_waitstream(chan, ints);
2096 res = ast_streamfile(chan, "digits/p-m", lang);
2099 res = ast_streamfile(chan, "digits/a-m", lang);
2102 res = ast_waitstream(chan, ints);
2107 int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
2112 localtime_r(&t,&tm);
2115 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2118 res = ast_streamfile(chan, "digits/nl-uur", lang);
2120 res = ast_waitstream(chan, ints);
2123 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
2127 /* Portuguese syntax */
2128 int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2133 localtime_r(&t,&tm);
2136 res = ast_say_number(chan, hour, ints, lang, "f");
2139 res = wait_file(chan, ints, "digits/pt-e", lang);
2141 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2144 res = wait_file(chan, ints, "digits/pt-hora", lang);
2145 if (tm.tm_hour != 1)
2147 res = wait_file(chan, ints, "digits/pt-sss", lang);
2150 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2154 int ast_say_datetime(struct ast_channel *chan, time_t t, char *ints, char *lang)
2156 if (!strcasecmp(lang, "en") ) { /* English syntax */
2157 return(ast_say_datetime_en(chan, t, ints, lang));
2158 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2159 return(ast_say_datetime_nl(chan, t, ints, lang));
2160 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2161 return(ast_say_datetime_pt(chan, t, ints, lang));
2164 /* Default to English */
2165 return(ast_say_datetime_en(chan, t, ints, lang));
2168 /* English syntax */
2169 int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2175 localtime_r(&t,&tm);
2177 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2178 res = ast_streamfile(chan, fn, lang);
2180 res = ast_waitstream(chan, ints);
2183 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2184 res = ast_streamfile(chan, fn, lang);
2186 res = ast_waitstream(chan, ints);
2189 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2194 else if (hour == 12)
2196 else if (hour > 12) {
2201 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2203 if (tm.tm_min > 9) {
2205 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2206 } else if (tm.tm_min) {
2208 res = ast_streamfile(chan, "digits/oh", lang);
2210 res = ast_waitstream(chan, ints);
2212 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2215 res = ast_streamfile(chan, "digits/oclock", lang);
2217 res = ast_waitstream(chan, ints);
2221 res = ast_streamfile(chan, "digits/p-m", lang);
2224 res = ast_streamfile(chan, "digits/a-m", lang);
2227 res = ast_waitstream(chan, ints);
2229 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2234 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
2238 localtime_r(&t,&tm);
2239 res = ast_say_date(chan, t, ints, lang);
2241 res = ast_streamfile(chan, "digits/nl-om", lang);
2243 res = ast_waitstream(chan, ints);
2246 ast_say_time(chan, t, ints, lang);
2250 /* Portuguese syntax */
2251 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2257 localtime_r(&t,&tm);
2259 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2260 res = ast_streamfile(chan, fn, lang);
2262 res = ast_waitstream(chan, ints);
2265 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2266 res = ast_streamfile(chan, fn, lang);
2268 res = ast_waitstream(chan, ints);
2271 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2276 else if (hour == 12)
2278 else if (hour > 12) {
2283 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2285 if (tm.tm_min > 9) {
2287 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2288 } else if (tm.tm_min) {
2290 res = ast_streamfile(chan, "digits/oh", lang);
2292 res = ast_waitstream(chan, ints);
2294 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2297 res = ast_streamfile(chan, "digits/oclock", lang);
2299 res = ast_waitstream(chan, ints);
2303 res = ast_streamfile(chan, "digits/p-m", lang);
2306 res = ast_streamfile(chan, "digits/a-m", lang);
2309 res = ast_waitstream(chan, ints);
2311 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2315 int ast_say_datetime_from_now(struct ast_channel *chan, time_t t, char *ints, char *lang)
2317 if (!strcasecmp(lang, "en") ) { /* English syntax */
2318 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
2319 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2320 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
2323 /* Default to English */
2324 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
2327 /* English syntax */
2328 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2339 localtime_r(&t,&tm);
2340 localtime_r(&nowt,&now);
2341 daydiff = now.tm_yday - tm.tm_yday;
2342 if ((daydiff < 0) || (daydiff > 6)) {
2343 /* Day of month and month */
2345 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2346 res = ast_streamfile(chan, fn, lang);
2348 res = ast_waitstream(chan, ints);
2351 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2353 } else if (daydiff) {
2354 /* Just what day of the week */
2356 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2357 res = ast_streamfile(chan, fn, lang);
2359 res = ast_waitstream(chan, ints);
2361 } /* Otherwise, it was today */
2363 res = ast_say_time(chan, t, ints, lang);
2367 /* Portuguese syntax */
2368 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2379 localtime_r(&t,&tm);
2380 localtime_r(&nowt,&now);
2381 daydiff = now.tm_yday - tm.tm_yday;
2382 if ((daydiff < 0) || (daydiff > 6)) {
2383 /* Day of month and month */
2385 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2387 res = wait_file(chan, ints, "digits/pt-de", lang);
2388 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2390 res = wait_file(chan, ints, fn, lang);
2392 } else if (daydiff) {
2393 /* Just what day of the week */
2394 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2396 res = wait_file(chan, ints, fn, lang);
2397 } /* Otherwise, it was today */
2398 snprintf(fn, sizeof(fn), "digits/pt-ah");
2400 res = wait_file(chan, ints, fn, lang);
2401 if (tm.tm_hour != 1)
2403 res = wait_file(chan, ints, "digits/pt-sss", lang);
2405 res = ast_say_time(chan, t, ints, lang);