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.
415 For Portuguese, we're using m & f options to saynumber() to indicate if the gender is masculine or feminine.
416 For Danish, we're using c & n options to saynumber() to indicate if the gender is commune or neutrum.
417 This still needs to be implemented for French, Spanish & German.
419 Date/Time functions currently have less languages supported than saynumber().
421 Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
423 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
425 Portuguese sound files needed for Time/Date functions:
437 /* Forward declarations of language specific variants of ast_say_number_full */
438 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
439 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
440 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
441 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
442 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
443 static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
444 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
445 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
446 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
448 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
449 static int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
450 static int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
451 static int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
453 static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
454 static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
455 static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
456 static int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
457 static int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
458 static int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
459 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
460 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
461 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
462 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
463 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
465 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang)
468 if ((res = ast_streamfile(chan, file, lang)))
469 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
471 res = ast_waitstream(chan, ints);
475 /*--- ast_say_number_full: call language-specific functions */
476 /* Called from AGI */
477 int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
479 char *options=(char *) NULL; /* While waiting for a general hack for agi */
481 if (!strcasecmp(language, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) {
482 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
483 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
484 return(ast_say_number_full_fr(chan, num, ints, language, audiofd, ctrlfd));
485 } else if (!strcasecmp(language, "de") ) { /* German syntax */
486 return(ast_say_number_full_de(chan, num, ints, language, audiofd, ctrlfd));
487 } else if (!strcasecmp(language, "se") ) { /* German syntax */
488 return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
489 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
490 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
491 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
492 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
493 } else if (!strcasecmp(language, "pt") ) { /* Portuguese syntax */
494 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
495 } else if (!strcasecmp(language, "es") ) { /* Spanish syntax */
496 return(ast_say_number_full_es(chan, num, ints, language, audiofd, ctrlfd));
497 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
498 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
501 /* Default to english */
502 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
505 /*--- ast_say_number: call language-specific functions without file descriptors */
506 int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language, char *options)
508 if (!strcasecmp(language, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) {
509 return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
512 if (!strcasecmp(language, "fr")) { /* French syntax */
513 return(ast_say_number_full_fr(chan, num, ints, language, -1, -1));
514 } else if (!strcasecmp(language, "da")) { /* Danish syntax */
515 return(ast_say_number_full_da(chan, num, ints, language, options, -1, -1));
516 } else if (!strcasecmp(language, "de")) { /* German syntax */
517 return(ast_say_number_full_de(chan, num, ints, language, -1, -1));
518 } else if (!strcasecmp(language, "se")) { /* Swedish syntax */
519 return(ast_say_number_full_se(chan, num, ints, language, options, -1, -1));
520 } else if (!strcasecmp(language, "it")) { /* Italian syntax */
521 return(ast_say_number_full_it(chan, num, ints, language, -1, -1));
522 } else if (!strcasecmp(language, "pt")) { /* Portuguese syntax */
523 return(ast_say_number_full_pt(chan, num, ints, language, options, -1, -1));
524 } else if (!strcasecmp(language, "nl")) { /* Dutch syntax */
525 return(ast_say_number_full_nl(chan, num, ints, language, -1, -1));
526 } else if (!strcasecmp(language, "es")) { /* Spanish syntax */
527 return(ast_say_number_full_es(chan, num, ints, language, -1, -1));
530 /* Default to english */
531 return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
534 /*--- ast_say_number_full_en: English syntax */
535 /* This is the default syntax, if no other syntax defined in this file is used */
536 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
542 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
544 while(!res && (num || playh)) {
546 snprintf(fn, sizeof(fn), "digits/hundred");
548 } else if (num < 20) {
549 snprintf(fn, sizeof(fn), "digits/%d", num);
551 } else if (num < 100) {
552 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
553 num -= ((num / 10) * 10);
556 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
558 num -= ((num / 100) * 100);
560 if (num < 1000000) { /* 1,000,000 */
561 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
565 snprintf(fn, sizeof(fn), "digits/thousand");
567 if (num < 1000000000) { /* 1,000,000,000 */
568 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
572 snprintf(fn, sizeof(fn), "digits/million");
574 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
581 if(!ast_streamfile(chan, fn, language)) {
582 if (audiofd && ctrlfd)
583 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
585 res = ast_waitstream(chan, ints);
587 ast_stopstream(chan);
595 /*--- ast_say_number_full_se: Swedish/norwegian syntax */
596 /* This is the default syntax, if no other syntax defined in this file is used */
597 static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
602 int cn = 1; /* +1 = Commune; -1 = Neutrum */
604 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
605 if (options && !strncasecmp(options, "n",1)) cn = -1;
607 while(!res && (num || playh)) {
609 snprintf(fn, sizeof(fn), "digits/hundred");
613 snprintf(fn, sizeof(fn), "digits/%d", num);
617 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
618 num -= ((num / 10) * 10);
620 if (num == 1 && cn == -1) { /* En eller ett? */
621 snprintf(fn, sizeof(fn), "digits/1N");
625 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
627 num -= ((num / 100) * 100);
629 if (num < 1000000) { /* 1,000,000 */
630 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
634 snprintf(fn, sizeof(fn), "digits/thousand");
636 if (num < 1000000000) { /* 1,000,000,000 */
637 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
641 snprintf(fn, sizeof(fn), "digits/million");
643 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
650 if(!ast_streamfile(chan, fn, language)) {
651 if (audiofd && ctrlfd)
652 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
654 res = ast_waitstream(chan, ints);
656 ast_stopstream(chan);
666 /*--- ast_say_number_full_da: Danish syntax */
668 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
670 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
675 int cn = 1; /* +1 = Commune; -1 = Neutrum */
678 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
680 if (options && !strncasecmp(options, "n",1)) cn = -1;
682 while(!res && (num || playh || playa )) {
683 /* The grammar for Danish numbers is the same as for English except
685 * - 1 exists in both commune ("en", file "1N") and neutrum ("et", file "1")
686 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
687 * "one-and twenty" and 68 is "eight-and sixty".
688 * - "million" is different in singular and plural form
689 * - numbers > 1000 with zero as the third digit from last have an
690 * "and" before the last two digits, i.e. 2034 is "two thousand and
691 * four-and thirty" and 1000012 is "one million and twelve".
694 snprintf(fn, sizeof(fn), "digits/hundred");
697 snprintf(fn, sizeof(fn), "digits/and");
699 } else if (num == 1 && cn == -1) {
700 snprintf(fn, sizeof(fn), "digits/1N");
702 } else if (num < 20) {
703 snprintf(fn, sizeof(fn), "digits/%d", num);
705 } else if (num < 100) {
708 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
711 snprintf(fn, sizeof(fn), "digits/%d", num);
716 int hundreds = num / 100;
718 snprintf(fn, sizeof(fn), "digits/1N");
720 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
723 num -= 100 * hundreds;
729 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
733 snprintf(fn, sizeof(fn), "digits/thousand");
735 if (num < 1000000000) {
736 int millions = num / 1000000;
737 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
741 snprintf(fn, sizeof(fn), "digits/million");
743 snprintf(fn, sizeof(fn), "digits/millions");
746 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
750 if (num && num < 100)
755 if(!ast_streamfile(chan, fn, language)) {
756 if (audiofd && ctrlfd)
757 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
759 res = ast_waitstream(chan, ints);
761 ast_stopstream(chan);
767 /*--- ast_say_number_full_de: German syntax */
769 In addition to English, the following sounds are required: "millions", "and" and "1-and" through "9-and"
771 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
778 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
780 while(!res && (num || playh || playa )) {
781 /* The grammar for German numbers is the same as for English except
783 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
784 * "one-and twenty" and 68 is "eight-and sixty".
785 * - "million" is different in singular and plural form
786 * - numbers > 1000 with zero as the third digit from last have an
787 * "and" before the last two digits, i.e. 2034 is "two thousand and
788 * four-and thirty" and 1000012 is "one million and twelve".
791 snprintf(fn, sizeof(fn), "digits/hundred");
794 snprintf(fn, sizeof(fn), "digits/and");
796 } else if (num < 20) {
797 snprintf(fn, sizeof(fn), "digits/%d", num);
799 } else if (num < 100) {
802 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
805 snprintf(fn, sizeof(fn), "digits/%d", num);
810 int hundreds = num / 100;
812 snprintf(fn, sizeof(fn), "digits/1N");
814 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
816 num -= 100 * hundreds;
821 res = ast_say_number_full_de(chan, num / 1000, ints, language, audiofd, ctrlfd);
825 snprintf(fn, sizeof(fn), "digits/thousand");
827 if (num < 1000000000) {
828 int millions = num / 1000000;
829 res = ast_say_number_full_de(chan, millions, ints, language, audiofd, ctrlfd);
833 snprintf(fn, sizeof(fn), "digits/million");
835 snprintf(fn, sizeof(fn), "digits/millions");
838 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
842 if (num && num < 100)
847 if(!ast_streamfile(chan, fn, language)) {
848 if (audiofd && ctrlfd)
849 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
851 res = ast_waitstream(chan, ints);
853 ast_stopstream(chan);
859 /*--- ast_say_number_full_es: spanish syntax */
861 Requires a few new audios:
862 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
864 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
870 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
871 while (!res && num) {
873 snprintf(fn, sizeof(fn), "digits/y");
875 } else if (num < 31) {
876 snprintf(fn, sizeof(fn), "digits/%d", num);
878 } else if (num < 100) {
879 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
880 num -= ((num/10)*10);
883 } else if (num == 100) {
884 snprintf(fn, sizeof(fn), "digits/cien");
888 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
889 num -= ((num/100)*100);
892 res = ast_say_number_full_es(chan, num / 1000, ints, language, audiofd, ctrlfd);
896 snprintf(fn, sizeof(fn), "digits/mil");
898 if (num < 2147483640) {
899 res = ast_say_number_full_es(chan, num / 1000000, ints, language, audiofd, ctrlfd);
902 if ((num/1000000) == 1) {
903 snprintf(fn, sizeof(fn), "digits/millon");
905 snprintf(fn, sizeof(fn), "digits/millones");
909 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
917 if(!ast_streamfile(chan, fn, language)) {
918 if (audiofd && ctrlfd)
919 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
921 res = ast_waitstream(chan, ints);
923 ast_stopstream(chan);
932 /*--- ast_say_number_full_fr: French syntax */
933 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
940 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
941 while(!res && (num || playh || playa)) {
943 snprintf(fn, sizeof(fn), "digits/hundred");
946 snprintf(fn, sizeof(fn), "digits/et");
948 } else if (num < 21) {
949 snprintf(fn, sizeof(fn), "digits/%d", num);
951 } else if (num < 70) {
952 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
953 if ((num % 10) == 1) playa++;
955 } else if (num < 80) {
956 snprintf(fn, sizeof(fn), "digits/60");
957 if ((num % 10) == 1) playa++;
959 } else if (num < 100) {
960 snprintf(fn, sizeof(fn), "digits/80");
962 } else if (num < 200) {
963 snprintf(fn, sizeof(fn), "digits/hundred");
965 } else if (num < 1000) {
966 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
969 } else if (num < 2000) {
970 snprintf(fn, sizeof(fn), "digits/thousand");
972 } else if (num < 1000000) {
973 res = ast_say_number_full_fr(chan, num / 1000, ints, language, audiofd, ctrlfd);
976 snprintf(fn, sizeof(fn), "digits/thousand");
978 } else if (num < 1000000000) {
979 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, audiofd, ctrlfd);
982 snprintf(fn, sizeof(fn), "digits/million");
985 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
989 if(!ast_streamfile(chan, fn, language)) {
990 if (audiofd && ctrlfd)
991 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
993 res = ast_waitstream(chan, ints);
995 ast_stopstream(chan);
1001 /*--- ast_say_number_full_it: Italian */
1002 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
1010 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1015 Like english, numbers up to 20 are a single 'word', and others
1016 compound, but with exceptions.
1017 For example 21 is not twenty-one, but there is a single word in 'it'.
1018 Idem for 28 (ie when a the 2nd part of a compund number
1019 starts with a vowel)
1021 There are exceptions also for hundred, thousand and million.
1022 In english 100 = one hundred, 200 is two hundred.
1023 In italian 100 = cento , like to say hundred (without one),
1024 200 and more are like english.
1026 Same applies for thousand:
1027 1000 is one thousand in en, 2000 is two thousand.
1028 In it we have 1000 = mille , 2000 = 2 mila
1030 For million(s) we use the plural, if more than one
1031 Also, one million is abbreviated in it, like on-million,
1032 or 'un milione', not 'uno milione'.
1033 So the right file is provided.
1036 while(!res && (num || playh)) {
1038 snprintf(fn, sizeof(fn), "digits/hundred");
1040 } else if (num < 20) {
1041 snprintf(fn, sizeof(fn), "digits/%d", num);
1043 } else if (num == 21) {
1044 snprintf(fn, sizeof(fn), "digits/%d", num);
1046 } else if (num == 28) {
1047 snprintf(fn, sizeof(fn), "digits/%d", num);
1049 } else if (num == 31) {
1050 snprintf(fn, sizeof(fn), "digits/%d", num);
1052 } else if (num == 38) {
1053 snprintf(fn, sizeof(fn), "digits/%d", num);
1055 } else if (num == 41) {
1056 snprintf(fn, sizeof(fn), "digits/%d", num);
1058 } else if (num == 48) {
1059 snprintf(fn, sizeof(fn), "digits/%d", num);
1061 } else if (num == 51) {
1062 snprintf(fn, sizeof(fn), "digits/%d", num);
1064 } else if (num == 58) {
1065 snprintf(fn, sizeof(fn), "digits/%d", num);
1067 } else if (num == 61) {
1068 snprintf(fn, sizeof(fn), "digits/%d", num);
1070 } else if (num == 68) {
1071 snprintf(fn, sizeof(fn), "digits/%d", num);
1073 } else if (num == 71) {
1074 snprintf(fn, sizeof(fn), "digits/%d", num);
1076 } else if (num == 78) {
1077 snprintf(fn, sizeof(fn), "digits/%d", num);
1079 } else if (num == 81) {
1080 snprintf(fn, sizeof(fn), "digits/%d", num);
1082 } else if (num == 88) {
1083 snprintf(fn, sizeof(fn), "digits/%d", num);
1085 } else if (num == 91) {
1086 snprintf(fn, sizeof(fn), "digits/%d", num);
1088 } else if (num == 98) {
1089 snprintf(fn, sizeof(fn), "digits/%d", num);
1091 } else if (num < 100) {
1092 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1093 num -= ((num / 10) * 10);
1096 if ((num / 100) > 1) {
1097 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1100 snprintf(fn, sizeof(fn), "digits/hundred");
1102 num -= ((num / 100) * 100);
1104 if (num < 1000000) { /* 1,000,000 */
1106 res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd);
1111 if ((tempnum / 1000) < 2)
1112 snprintf(fn, sizeof(fn), "digits/thousand");
1113 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1114 snprintf(fn, sizeof(fn), "digits/thousands");
1116 if (num < 1000000000) { /* 1,000,000,000 */
1117 if ((num / 1000000) > 1)
1118 res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1122 num = num % 1000000;
1123 if ((tempnum / 1000000) < 2)
1124 snprintf(fn, sizeof(fn), "digits/million");
1126 snprintf(fn, sizeof(fn), "digits/millions");
1128 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1135 if(!ast_streamfile(chan, fn, language)) {
1136 if (audiofd && ctrlfd)
1137 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1139 res = ast_waitstream(chan, ints);
1141 ast_stopstream(chan);
1147 /*--- ast_say_number_full_nl: dutch syntax */
1148 /* New files: digits/nl-en
1150 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
1157 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1158 while (!res && (num || playh )) {
1160 snprintf(fn, sizeof(fn), "digits/hundred");
1162 } else if (num < 20) {
1163 snprintf(fn, sizeof(fn), "digits/%d", num);
1165 } else if (num < 100) {
1168 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1172 snprintf(fn, sizeof(fn), "digits/nl-en");
1174 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1179 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1181 num -= ((num / 100) * 100);
1183 if (num < 1000000) { /* 1,000,000 */
1184 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1188 snprintf(fn, sizeof(fn), "digits/thousand");
1190 if (num < 1000000000) { /* 1,000,000,000 */
1191 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1194 num = num % 1000000;
1195 snprintf(fn, sizeof(fn), "digits/million");
1197 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1205 if(!ast_streamfile(chan, fn, language)) {
1206 if (audiofd && ctrlfd)
1207 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1209 res = ast_waitstream(chan, ints);
1211 ast_stopstream(chan);
1217 /* ast_say_number_full_pt: Portuguese syntax */
1218 /* Extra sounds needed: */
1219 /* For feminin all sound files end with F */
1220 /* 100E for 100+ something */
1221 /* 1000000S for plural */
1222 /* pt-e for 'and' */
1223 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1227 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1231 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1233 if (options && !strncasecmp(options, "f",1))
1236 while(!res && num ) {
1238 if ((num == 1 || num == 2) && (mf < 0))
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);
1248 } else if (num < 1000) {
1250 snprintf(fn, sizeof(fn), "digits/100");
1252 snprintf(fn, sizeof(fn), "digits/100E");
1254 if (mf < 0 && num > 199)
1255 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1257 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1262 } else if (num < 1000000) {
1264 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1268 snprintf(fn, sizeof(fn), "digits/1000");
1269 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
1272 } else if (num < 1000000000) {
1273 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
1277 snprintf(fn, sizeof(fn), "digits/1000000");
1279 snprintf(fn, sizeof(fn), "digits/1000000S");
1281 if ((num % 1000000) &&
1283 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
1284 // no hundreds and below
1285 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
1287 num = num % 1000000;
1289 if (!res && playh) {
1290 res = wait_file(chan, ints, "digits/pt-e", language);
1291 ast_stopstream(chan);
1295 if(!ast_streamfile(chan, fn, language)) {
1296 if (audiofd && ctrlfd)
1297 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else
1298 res = ast_waitstream(chan, ints);
1300 ast_stopstream(chan);
1306 int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang)
1308 if (!strcasecmp(lang,"en") ) { /* English syntax */
1309 return(ast_say_date_en(chan, t, ints, lang));
1310 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
1311 return(ast_say_date_nl(chan, t, ints, lang));
1312 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
1313 return(ast_say_date_pt(chan, t, ints, lang));
1316 /* Default to English */
1317 return(ast_say_date_en(chan, t, ints, lang));
1321 /* English syntax */
1322 int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
1327 ast_localtime(&t,&tm,NULL);
1329 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1330 res = ast_streamfile(chan, fn, lang);
1332 res = ast_waitstream(chan, ints);
1335 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1336 res = ast_streamfile(chan, fn, lang);
1338 res = ast_waitstream(chan, ints);
1341 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1343 res = ast_waitstream(chan, ints);
1345 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1350 int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
1355 ast_localtime(&t,&tm,NULL);
1357 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1358 res = ast_streamfile(chan, fn, lang);
1360 res = ast_waitstream(chan, ints);
1363 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1365 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1366 res = ast_streamfile(chan, fn, lang);
1368 res = ast_waitstream(chan, ints);
1371 res = ast_waitstream(chan, ints);
1373 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1377 /* Portuguese syntax */
1378 int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
1383 ast_localtime(&t,&tm,NULL);
1384 localtime_r(&t,&tm);
1385 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1387 res = wait_file(chan, ints, fn, lang);
1389 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1391 res = wait_file(chan, ints, "digits/pt-de", lang);
1392 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1394 res = wait_file(chan, ints, fn, lang);
1396 res = wait_file(chan, ints, "digits/pt-de", lang);
1398 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1403 int ast_say_date_with_format(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1405 if (!strcasecmp(lang, "en") ) { /* English syntax */
1406 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1407 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
1408 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
1409 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
1410 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
1413 /* Default to English */
1414 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1417 /* English syntax */
1418 int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1421 int res=0, offset, sndoffset;
1422 char sndfile[256], nextmsg[256];
1424 ast_localtime(&time,&tm,timezone);
1426 for (offset=0 ; format[offset] != '\0' ; offset++) {
1427 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1428 switch (format[offset]) {
1429 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
1431 /* Literal name of a sound file */
1433 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1434 sndfile[sndoffset] = format[offset];
1435 sndfile[sndoffset] = '\0';
1436 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1437 res = wait_file(chan,ints,nextmsg,lang);
1441 /* Sunday - Saturday */
1442 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1443 res = wait_file(chan,ints,nextmsg,lang);
1448 /* January - December */
1449 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1450 res = wait_file(chan,ints,nextmsg,lang);
1454 /* First - Thirtyfirst */
1455 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1456 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
1457 res = wait_file(chan,ints,nextmsg,lang);
1458 } else if (tm.tm_mday == 31) {
1459 /* "Thirty" and "first" */
1460 res = wait_file(chan,ints, "digits/30",lang);
1462 res = wait_file(chan,ints, "digits/h-1",lang);
1465 /* Between 21 and 29 - two sounds */
1466 res = wait_file(chan,ints, "digits/20",lang);
1468 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
1469 res = wait_file(chan,ints,nextmsg,lang);
1475 if (tm.tm_year > 99) {
1476 res = wait_file(chan,ints, "digits/2",lang);
1478 res = wait_file(chan,ints, "digits/thousand",lang);
1480 if (tm.tm_year > 100) {
1482 /* This works until the end of 2020 */
1483 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1484 res = wait_file(chan,ints,nextmsg,lang);
1488 if (tm.tm_year < 1) {
1489 /* I'm not going to handle 1900 and prior */
1490 /* We'll just be silent on the year, instead of bombing out. */
1492 res = wait_file(chan,ints, "digits/19",lang);
1494 if (tm.tm_year <= 9) {
1496 res = wait_file(chan,ints, "digits/oh",lang);
1498 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1499 res = wait_file(chan,ints,nextmsg,lang);
1501 } else if (tm.tm_year <= 20) {
1503 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1504 res = wait_file(chan,ints,nextmsg,lang);
1508 ten = tm.tm_year / 10;
1509 one = tm.tm_year % 10;
1510 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1511 res = wait_file(chan,ints,nextmsg,lang);
1514 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1515 res = wait_file(chan,ints,nextmsg,lang);
1526 if (tm.tm_hour == 0)
1527 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1528 else if (tm.tm_hour > 12)
1529 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1531 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1532 res = wait_file(chan,ints,nextmsg,lang);
1537 if (format[offset] == 'H') {
1539 if (tm.tm_hour < 10) {
1540 res = wait_file(chan,ints, "digits/oh",lang);
1544 if (tm.tm_hour == 0) {
1545 res = wait_file(chan,ints, "digits/oh",lang);
1549 if (tm.tm_hour != 0) {
1550 int remainder = tm.tm_hour;
1551 if (tm.tm_hour > 20) {
1552 res = wait_file(chan,ints, "digits/20",lang);
1556 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1557 res = wait_file(chan,ints,nextmsg,lang);
1564 if (tm.tm_min == 0) {
1565 res = wait_file(chan,ints, "digits/oclock",lang);
1566 } else if (tm.tm_min < 10) {
1567 res = wait_file(chan,ints, "digits/oh",lang);
1569 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1570 res = wait_file(chan,ints,nextmsg,lang);
1572 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
1573 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1574 res = wait_file(chan,ints,nextmsg,lang);
1577 ten = (tm.tm_min / 10) * 10;
1578 one = (tm.tm_min % 10);
1579 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1580 res = wait_file(chan,ints,nextmsg,lang);
1582 /* Fifty, not fifty-zero */
1584 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1585 res = wait_file(chan,ints,nextmsg,lang);
1593 if (tm.tm_hour > 11)
1594 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1596 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1597 res = wait_file(chan,ints,nextmsg,lang);
1600 /* Shorthand for "Today", "Yesterday", or ABdY */
1606 gettimeofday(&now,NULL);
1607 ast_localtime(&now.tv_sec,&tmnow,timezone);
1608 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1609 /* In any case, it saves not having to do ast_mktime() */
1610 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1611 if (beg_today < time) {
1613 res = wait_file(chan,ints, "digits/today",lang);
1614 } else if (beg_today - 86400 < time) {
1616 res = wait_file(chan,ints, "digits/yesterday",lang);
1618 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1623 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1629 gettimeofday(&now,NULL);
1630 ast_localtime(&now.tv_sec,&tmnow,timezone);
1631 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1632 /* In any case, it saves not having to do ast_mktime() */
1633 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1634 if (beg_today < time) {
1636 } else if ((beg_today - 86400) < time) {
1638 res = wait_file(chan,ints, "digits/yesterday",lang);
1639 } else if (beg_today - 86400 * 6 < time) {
1640 /* Within the last week */
1641 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1643 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1648 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1652 if (tm.tm_sec == 0) {
1653 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1654 res = wait_file(chan,ints,nextmsg,lang);
1655 } else if (tm.tm_sec < 10) {
1656 res = wait_file(chan,ints, "digits/oh",lang);
1658 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1659 res = wait_file(chan,ints,nextmsg,lang);
1661 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1662 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1663 res = wait_file(chan,ints,nextmsg,lang);
1666 ten = (tm.tm_sec / 10) * 10;
1667 one = (tm.tm_sec % 10);
1668 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1669 res = wait_file(chan,ints,nextmsg,lang);
1671 /* Fifty, not fifty-zero */
1673 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1674 res = wait_file(chan,ints,nextmsg,lang);
1680 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1684 /* Just ignore spaces and tabs */
1687 /* Unknown character */
1688 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1690 /* Jump out on DTMF */
1699 int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1702 int res=0, offset, sndoffset;
1703 char sndfile[256], nextmsg[256];
1705 ast_localtime(&time,&tm,timezone);
1707 for (offset=0 ; format[offset] != '\0' ; offset++) {
1708 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1709 switch (format[offset]) {
1710 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
1712 /* Literal name of a sound file */
1714 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1715 sndfile[sndoffset] = format[offset];
1716 sndfile[sndoffset] = '\0';
1717 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1718 res = wait_file(chan,ints,nextmsg,lang);
1722 /* Sunday - Saturday */
1723 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1724 res = wait_file(chan,ints,nextmsg,lang);
1729 /* January - December */
1730 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1731 res = wait_file(chan,ints,nextmsg,lang);
1735 /* First - Thirtyfirst */
1736 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
1740 if (tm.tm_year > 99) {
1741 res = wait_file(chan,ints, "digits/2",lang);
1743 res = wait_file(chan,ints, "digits/thousand",lang);
1745 if (tm.tm_year > 100) {
1747 /* This works until the end of 2020 */
1748 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1749 res = wait_file(chan,ints,nextmsg,lang);
1753 if (tm.tm_year < 1) {
1754 /* I'm not going to handle 1900 and prior */
1755 /* We'll just be silent on the year, instead of bombing out. */
1757 res = wait_file(chan,ints, "digits/19",lang);
1759 if (tm.tm_year <= 9) {
1761 res = wait_file(chan,ints, "digits/oh",lang);
1763 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1764 res = wait_file(chan,ints,nextmsg,lang);
1766 } else if (tm.tm_year <= 20) {
1768 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1769 res = wait_file(chan,ints,nextmsg,lang);
1773 ten = tm.tm_year / 10;
1774 one = tm.tm_year % 10;
1775 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1776 res = wait_file(chan,ints,nextmsg,lang);
1779 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1780 res = wait_file(chan,ints,nextmsg,lang);
1791 if (tm.tm_hour == 0)
1792 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1793 else if (tm.tm_hour > 12)
1794 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1796 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1797 res = wait_file(chan,ints,nextmsg,lang);
1802 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
1804 res = wait_file(chan,ints, "digits/nl-uur",lang);
1809 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
1814 if (tm.tm_hour > 11)
1815 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1817 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1818 res = wait_file(chan,ints,nextmsg,lang);
1821 /* Shorthand for "Today", "Yesterday", or ABdY */
1827 gettimeofday(&now,NULL);
1828 ast_localtime(&now.tv_sec,&tmnow,timezone);
1829 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1830 /* In any case, it saves not having to do ast_mktime() */
1831 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1832 if (beg_today < time) {
1834 res = wait_file(chan,ints, "digits/today",lang);
1835 } else if (beg_today - 86400 < time) {
1837 res = wait_file(chan,ints, "digits/yesterday",lang);
1839 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1844 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1850 gettimeofday(&now,NULL);
1851 ast_localtime(&now.tv_sec,&tmnow,timezone);
1852 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1853 /* In any case, it saves not having to do ast_mktime() */
1854 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1855 if (beg_today < time) {
1857 } else if ((beg_today - 86400) < time) {
1859 res = wait_file(chan,ints, "digits/yesterday",lang);
1860 } else if (beg_today - 86400 * 6 < time) {
1861 /* Within the last week */
1862 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1864 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1869 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1873 if (tm.tm_sec == 0) {
1874 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1875 res = wait_file(chan,ints,nextmsg,lang);
1876 } else if (tm.tm_sec < 10) {
1877 res = wait_file(chan,ints, "digits/oh",lang);
1879 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1880 res = wait_file(chan,ints,nextmsg,lang);
1882 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1883 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1884 res = wait_file(chan,ints,nextmsg,lang);
1887 ten = (tm.tm_sec / 10) * 10;
1888 one = (tm.tm_sec % 10);
1889 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1890 res = wait_file(chan,ints,nextmsg,lang);
1892 /* Fifty, not fifty-zero */
1894 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1895 res = wait_file(chan,ints,nextmsg,lang);
1901 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1905 /* Just ignore spaces and tabs */
1908 /* Unknown character */
1909 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1911 /* Jump out on DTMF */
1919 /* Portuguese syntax */
1920 int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1923 int res=0, offset, sndoffset;
1924 char sndfile[256], nextmsg[256];
1926 ast_localtime(&time,&tm,timezone);
1928 for (offset=0 ; format[offset] != '\0' ; offset++) {
1929 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1930 switch (format[offset]) {
1931 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
1933 /* Literal name of a sound file */
1935 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1936 sndfile[sndoffset] = format[offset];
1937 sndfile[sndoffset] = '\0';
1938 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
1939 res = wait_file(chan,ints,nextmsg,lang);
1943 /* Sunday - Saturday */
1944 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1945 res = wait_file(chan,ints,nextmsg,lang);
1950 /* January - December */
1951 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1952 res = wait_file(chan,ints,nextmsg,lang);
1956 /* First - Thirtyfirst */
1957 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1961 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1966 if (tm.tm_hour == 0) {
1967 if (format[offset] == 'I')
1968 res = wait_file(chan, ints, "digits/pt-ah", lang);
1970 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
1972 else if (tm.tm_hour == 12) {
1973 if (format[offset] == 'I')
1974 res = wait_file(chan, ints, "digits/pt-ao", lang);
1976 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
1979 if (format[offset] == 'I') {
1980 res = wait_file(chan, ints, "digits/pt-ah", lang);
1981 if ((tm.tm_hour % 12) != 1)
1983 res = wait_file(chan, ints, "digits/pt-sss", lang);
1986 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
1992 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
1994 if (tm.tm_hour != 0) {
1995 int remainder = tm.tm_hour;
1996 if (tm.tm_hour > 20) {
1997 res = wait_file(chan,ints, "digits/20",lang);
2001 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
2002 res = wait_file(chan,ints,nextmsg,lang);
2009 if (tm.tm_min == 0) {
2010 res = wait_file(chan, ints, "digits/pt-hora", lang);
2011 if (tm.tm_hour != 1)
2013 res = wait_file(chan, ints, "digits/pt-sss", lang); } else {
2014 res = wait_file(chan,ints,"digits/pt-e",lang);
2016 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2022 if (tm.tm_hour > 12)
2023 res = wait_file(chan, ints, "digits/p-m", lang);
2024 else if (tm.tm_hour && tm.tm_hour < 12)
2025 res = wait_file(chan, ints, "digits/a-m", lang);
2028 /* Shorthand for "Today", "Yesterday", or ABdY */
2034 gettimeofday(&now,NULL);
2035 ast_localtime(&now.tv_sec,&tmnow,timezone);
2036 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2037 /* In any case, it saves not having to do ast_mktime() */
2038 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2039 if (beg_today < time) {
2041 res = wait_file(chan,ints, "digits/today",lang);
2042 } else if (beg_today - 86400 < time) {
2044 res = wait_file(chan,ints, "digits/yesterday",lang);
2046 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
2051 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2057 gettimeofday(&now,NULL);
2058 ast_localtime(&now.tv_sec,&tmnow,timezone);
2059 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2060 /* In any case, it saves not having to do ast_mktime() */
2061 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2062 if (beg_today < time) {
2064 } else if ((beg_today - 86400) < time) {
2066 res = wait_file(chan,ints, "digits/yesterday",lang);
2067 } else if (beg_today - 86400 * 6 < time) {
2068 /* Within the last week */
2069 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2071 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
2076 res = ast_say_date_with_format(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
2080 if (tm.tm_sec == 0) {
2081 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2082 res = wait_file(chan,ints,nextmsg,lang);
2083 } else if (tm.tm_sec < 10) {
2084 res = wait_file(chan,ints, "digits/oh",lang);
2086 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2087 res = wait_file(chan,ints,nextmsg,lang);
2089 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2090 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2091 res = wait_file(chan,ints,nextmsg,lang);
2094 ten = (tm.tm_sec / 10) * 10;
2095 one = (tm.tm_sec % 10);
2096 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2097 res = wait_file(chan,ints,nextmsg,lang);
2099 /* Fifty, not fifty-zero */
2101 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2102 res = wait_file(chan,ints,nextmsg,lang);
2108 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2112 /* Just ignore spaces and tabs */
2115 /* Unknown character */
2116 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2118 /* Jump out on DTMF */
2126 int ast_say_time(struct ast_channel *chan, time_t t, char *ints, char *lang)
2128 if (!strcasecmp(lang, "en") ) { /* English syntax */
2129 return(ast_say_time_en(chan, t, ints, lang));
2130 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2131 return(ast_say_time_nl(chan, t, ints, lang));
2132 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2133 return(ast_say_time_pt(chan, t, ints, lang));
2136 /* Default to English */
2137 return(ast_say_time_en(chan, t, ints, lang));
2140 /* English syntax */
2141 int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2146 localtime_r(&t,&tm);
2150 else if (hour == 12)
2152 else if (hour > 12) {
2157 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2159 if (tm.tm_min > 9) {
2161 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2162 } else if (tm.tm_min) {
2164 res = ast_streamfile(chan, "digits/oh", lang);
2166 res = ast_waitstream(chan, ints);
2168 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2171 res = ast_streamfile(chan, "digits/oclock", lang);
2173 res = ast_waitstream(chan, ints);
2177 res = ast_streamfile(chan, "digits/p-m", lang);
2180 res = ast_streamfile(chan, "digits/a-m", lang);
2183 res = ast_waitstream(chan, ints);
2188 int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
2193 localtime_r(&t,&tm);
2196 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2199 res = ast_streamfile(chan, "digits/nl-uur", lang);
2201 res = ast_waitstream(chan, ints);
2204 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
2208 /* Portuguese syntax */
2209 int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2214 localtime_r(&t,&tm);
2217 res = ast_say_number(chan, hour, ints, lang, "f");
2220 res = wait_file(chan, ints, "digits/pt-e", lang);
2222 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2225 res = wait_file(chan, ints, "digits/pt-hora", lang);
2226 if (tm.tm_hour != 1)
2228 res = wait_file(chan, ints, "digits/pt-sss", lang);
2231 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2235 int ast_say_datetime(struct ast_channel *chan, time_t t, char *ints, char *lang)
2237 if (!strcasecmp(lang, "en") ) { /* English syntax */
2238 return(ast_say_datetime_en(chan, t, ints, lang));
2239 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2240 return(ast_say_datetime_nl(chan, t, ints, lang));
2241 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2242 return(ast_say_datetime_pt(chan, t, ints, lang));
2245 /* Default to English */
2246 return(ast_say_datetime_en(chan, t, ints, lang));
2249 /* English syntax */
2250 int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2256 localtime_r(&t,&tm);
2258 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2259 res = ast_streamfile(chan, fn, lang);
2261 res = ast_waitstream(chan, ints);
2264 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2265 res = ast_streamfile(chan, fn, lang);
2267 res = ast_waitstream(chan, ints);
2270 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2275 else if (hour == 12)
2277 else if (hour > 12) {
2282 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2284 if (tm.tm_min > 9) {
2286 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2287 } else if (tm.tm_min) {
2289 res = ast_streamfile(chan, "digits/oh", lang);
2291 res = ast_waitstream(chan, ints);
2293 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2296 res = ast_streamfile(chan, "digits/oclock", lang);
2298 res = ast_waitstream(chan, ints);
2302 res = ast_streamfile(chan, "digits/p-m", lang);
2305 res = ast_streamfile(chan, "digits/a-m", lang);
2308 res = ast_waitstream(chan, ints);
2310 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2315 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
2319 localtime_r(&t,&tm);
2320 res = ast_say_date(chan, t, ints, lang);
2322 res = ast_streamfile(chan, "digits/nl-om", lang);
2324 res = ast_waitstream(chan, ints);
2327 ast_say_time(chan, t, ints, lang);
2331 /* Portuguese syntax */
2332 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2338 localtime_r(&t,&tm);
2340 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2341 res = ast_streamfile(chan, fn, lang);
2343 res = ast_waitstream(chan, ints);
2346 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2347 res = ast_streamfile(chan, fn, lang);
2349 res = ast_waitstream(chan, ints);
2352 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2357 else if (hour == 12)
2359 else if (hour > 12) {
2364 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2366 if (tm.tm_min > 9) {
2368 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2369 } else if (tm.tm_min) {
2371 res = ast_streamfile(chan, "digits/oh", lang);
2373 res = ast_waitstream(chan, ints);
2375 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2378 res = ast_streamfile(chan, "digits/oclock", lang);
2380 res = ast_waitstream(chan, ints);
2384 res = ast_streamfile(chan, "digits/p-m", lang);
2387 res = ast_streamfile(chan, "digits/a-m", lang);
2390 res = ast_waitstream(chan, ints);
2392 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2396 int ast_say_datetime_from_now(struct ast_channel *chan, time_t t, char *ints, char *lang)
2398 if (!strcasecmp(lang, "en") ) { /* English syntax */
2399 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
2400 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2401 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
2404 /* Default to English */
2405 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
2408 /* English syntax */
2409 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2420 localtime_r(&t,&tm);
2421 localtime_r(&nowt,&now);
2422 daydiff = now.tm_yday - tm.tm_yday;
2423 if ((daydiff < 0) || (daydiff > 6)) {
2424 /* Day of month and month */
2426 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2427 res = ast_streamfile(chan, fn, lang);
2429 res = ast_waitstream(chan, ints);
2432 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2434 } else if (daydiff) {
2435 /* Just what day of the week */
2437 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2438 res = ast_streamfile(chan, fn, lang);
2440 res = ast_waitstream(chan, ints);
2442 } /* Otherwise, it was today */
2444 res = ast_say_time(chan, t, ints, lang);
2448 /* Portuguese syntax */
2449 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2460 localtime_r(&t,&tm);
2461 localtime_r(&nowt,&now);
2462 daydiff = now.tm_yday - tm.tm_yday;
2463 if ((daydiff < 0) || (daydiff > 6)) {
2464 /* Day of month and month */
2466 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2468 res = wait_file(chan, ints, "digits/pt-de", lang);
2469 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2471 res = wait_file(chan, ints, fn, lang);
2473 } else if (daydiff) {
2474 /* Just what day of the week */
2475 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2477 res = wait_file(chan, ints, fn, lang);
2478 } /* Otherwise, it was today */
2479 snprintf(fn, sizeof(fn), "digits/pt-ah");
2481 res = wait_file(chan, ints, fn, lang);
2482 if (tm.tm_hour != 1)
2484 res = wait_file(chan, ints, "digits/pt-sss", lang);
2486 res = ast_say_time(chan, t, ints, lang);