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>
26 #include <asterisk/utils.h>
31 /* Forward declaration */
32 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang);
34 int ast_say_digit_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
36 /* XXX Merge with full version? XXX */
40 while(fn2[num] && !res) {
44 snprintf(fn, sizeof(fn), "digits/star");
47 snprintf(fn, sizeof(fn), "digits/pound");
50 if((fn2[num] >= '0') && (fn2[num] <= '9')){ /* Must be in {0-9} */
51 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
54 if(!ast_strlen_zero(fn)){ /* if length == 0, then skip this digit as it is invalid */
55 res = ast_streamfile(chan, fn, lang);
57 res = ast_waitstream(chan, ints);
65 int ast_say_character_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
67 /* XXX Merge with full version? XXX */
72 while(fn2[num] && !res) {
76 snprintf(fn, sizeof(fn), "digits/star");
79 snprintf(fn, sizeof(fn), "digits/pound");
91 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
94 strncpy(fn, "letters/exclaimation-point", sizeof(fn));
97 strncpy(fn, "letters/at", sizeof(fn));
100 strncpy(fn, "letters/dollar", sizeof(fn));
103 strncpy(fn, "letters/dash", sizeof(fn));
106 strncpy(fn, "letters/dot", sizeof(fn));
109 strncpy(fn, "letters/equals", sizeof(fn));
112 strncpy(fn, "letters/plus", sizeof(fn));
115 strncpy(fn, "letters/slash", sizeof(fn));
118 strncpy(fn, "letters/space", sizeof(fn));
122 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
123 snprintf(fn, sizeof(fn), "letters/%c", ltr);
125 if(!ast_strlen_zero(fn)) { /* if length == 0, then skip this digit as it is invalid */
126 res = ast_streamfile(chan, fn, lang);
128 res = ast_waitstream(chan, ints);
129 } ast_stopstream(chan);
135 int ast_say_phonetic_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
137 /* XXX Merge with full version? XXX */
145 /* while(fn2[num] && !res) { */
150 snprintf(fn, sizeof(fn), "digits/star");
153 snprintf(fn, sizeof(fn), "digits/pound");
164 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
167 strncpy(fn, "exclaimation-point", sizeof(fn));
170 strncpy(fn, "at", sizeof(fn));
173 strncpy(fn, "dollar", sizeof(fn));
176 strncpy(fn, "dash", sizeof(fn));
179 strncpy(fn, "dot", sizeof(fn));
182 strncpy(fn, "equals", sizeof(fn));
185 strncpy(fn, "plus", sizeof(fn));
188 strncpy(fn, "slash", sizeof(fn));
191 strncpy(fn, "space", sizeof(fn));
195 /* check if we have 2 chars after the % */
196 if (strlen(fn2) > num+2)
201 if (sscanf(hex,"%x", &temp))
202 { /* Hex to char convertion successfull */
206 { /* If it is a percent, play it now */
207 strncpy(fn, "percent", sizeof(fn));
211 /* check for invalid characters */
212 if ((temp<32) || (temp>126))
221 default: /* '9' falls through to here, too */
222 ltr = tolower(fn2[num]);
223 snprintf(fn, sizeof(fn), "phonetic/%c_p", ltr);
227 res = ast_streamfile(chan, fn, lang);
229 res = ast_waitstream(chan, ints);
230 ast_stopstream(chan);
237 int ast_say_digit_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
242 while(fn2[num] && !res) {
243 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
244 res = ast_streamfile(chan, fn, lang);
246 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
247 ast_stopstream(chan);
253 int ast_say_character_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
259 while(fn2[num] && !res) {
262 snprintf(fn, sizeof(fn), "digits/star");
265 snprintf(fn, sizeof(fn), "digits/pound");
277 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
280 strncpy(fn, "exclaimation-point", sizeof(fn));
283 strncpy(fn, "at", sizeof(fn));
286 strncpy(fn, "dollar", sizeof(fn));
289 strncpy(fn, "dash", sizeof(fn));
292 strncpy(fn, "dot", sizeof(fn));
295 strncpy(fn, "equals", sizeof(fn));
298 strncpy(fn, "plus", sizeof(fn));
301 strncpy(fn, "slash", sizeof(fn));
304 strncpy(fn, "space", sizeof(fn));
308 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
309 snprintf(fn, sizeof(fn), "letters/%c", ltr);
311 /* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
312 res = ast_streamfile(chan, fn, lang);
314 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
315 ast_stopstream(chan);
321 int ast_say_phonetic_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
327 while(fn2[num] && !res) {
330 snprintf(fn, sizeof(fn), "digits/star");
333 snprintf(fn, sizeof(fn), "digits/pound");
344 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
347 strncpy(fn, "exclaimation-point", sizeof(fn));
350 strncpy(fn, "at", sizeof(fn));
353 strncpy(fn, "dollar", sizeof(fn));
356 strncpy(fn, "dash", sizeof(fn));
359 strncpy(fn, "dot", sizeof(fn));
362 strncpy(fn, "equals", sizeof(fn));
365 strncpy(fn, "plus", sizeof(fn));
368 strncpy(fn, "slash", sizeof(fn));
371 strncpy(fn, "space", sizeof(fn));
373 default: /* '9' falls here... */
375 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
376 snprintf(fn, sizeof(fn), "phonetic/%c", ltr);
378 /* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
379 res = ast_streamfile(chan, fn, lang);
381 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
382 ast_stopstream(chan);
388 int ast_say_digits(struct ast_channel *chan, int num, char *ints, char *lang)
390 /* XXX Should I be merged with say_digits_full XXX */
392 snprintf(fn2, sizeof(fn2), "%d", num);
393 return ast_say_digit_str(chan, fn2, ints, lang);
396 int ast_say_digits_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd)
399 snprintf(fn2, sizeof(fn2), "%d", num);
400 return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd);
403 /* Forward declarations */
404 /* Syntaxes supported, not really language codes.
416 For Portuguese, we're using m & f options to saynumber() to indicate if the gender is masculine or feminine.
417 For Danish, we're using c & n options to saynumber() to indicate if the gender is commune or neutrum.
418 This still needs to be implemented for French, Spanish & German.
420 Date/Time functions currently have less languages supported than saynumber().
422 Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
424 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
426 Portuguese sound files needed for Time/Date functions:
438 /* Forward declarations of language specific variants of ast_say_number_full */
439 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
440 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
441 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
442 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
443 static int ast_say_number_full_fr(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);
447 static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
449 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
450 static int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
451 static int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
452 static int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
454 static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
455 static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
456 static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
457 static int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
458 static int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
459 static int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
460 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
461 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
462 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
463 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
464 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
466 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang)
469 if ((res = ast_streamfile(chan, file, lang)))
470 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
472 res = ast_waitstream(chan, ints);
476 /*--- ast_say_number_full: call language-specific functions */
477 /* Called from AGI */
478 int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
480 char *options=(char *) NULL; /* While waiting for a general hack for agi */
482 if (!strcasecmp(language,"en") ) { /* English syntax */
483 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
484 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
485 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
486 } else if (!strcasecmp(language, "de") ) { /* German syntax */
487 return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
488 } else if (!strcasecmp(language, "es") ) { /* Spanish syntax */
489 return(ast_say_number_full_es(chan, num, ints, language, audiofd, ctrlfd));
490 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
491 return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
492 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
493 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
494 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
495 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
496 } else if (!strcasecmp(language, "pt") ) { /* Portuguese syntax */
497 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
498 } else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
499 return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
502 /* Default to english */
503 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
506 /*--- ast_say_number: call language-specific functions without file descriptors */
507 int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language, char *options)
509 if (!strcasecmp(language,"en") ) { /* English syntax */
510 return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
511 }else if (!strcasecmp(language, "da")) { /* Danish syntax */
512 return(ast_say_number_full_da(chan, num, ints, language, options, -1, -1));
513 } else if (!strcasecmp(language, "de")) { /* German syntax */
514 return(ast_say_number_full_de(chan, num, ints, language, options, -1, -1));
515 } else if (!strcasecmp(language, "es")) { /* Spanish syntax */
516 return(ast_say_number_full_es(chan, num, ints, language, -1, -1));
517 } else if (!strcasecmp(language, "fr")) { /* French syntax */
518 return(ast_say_number_full_fr(chan, num, ints, language, options, -1, -1));
519 } else if (!strcasecmp(language, "it")) { /* Italian syntax */
520 return(ast_say_number_full_it(chan, num, ints, language, -1, -1));
521 } else if (!strcasecmp(language, "nl")) { /* Dutch syntax */
522 return(ast_say_number_full_nl(chan, num, ints, language, -1, -1));
523 } else if (!strcasecmp(language, "pt")) { /* Portuguese syntax */
524 return(ast_say_number_full_pt(chan, num, ints, language, options, -1, -1));
525 } else if (!strcasecmp(language, "se")) { /* Swedish syntax */
526 return(ast_say_number_full_se(chan, num, ints, language, options, -1, -1));
529 /* Default to english */
530 return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
533 /*--- ast_say_number_full_en: English syntax */
534 /* This is the default syntax, if no other syntax defined in this file is used */
535 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
541 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
543 while(!res && (num || playh)) {
545 snprintf(fn, sizeof(fn), "digits/hundred");
547 } else if (num < 20) {
548 snprintf(fn, sizeof(fn), "digits/%d", num);
550 } else if (num < 100) {
551 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
552 num -= ((num / 10) * 10);
555 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
557 num -= ((num / 100) * 100);
559 if (num < 1000000) { /* 1,000,000 */
560 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
564 snprintf(fn, sizeof(fn), "digits/thousand");
566 if (num < 1000000000) { /* 1,000,000,000 */
567 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
571 snprintf(fn, sizeof(fn), "digits/million");
573 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
580 if(!ast_streamfile(chan, fn, language)) {
581 if (audiofd && ctrlfd)
582 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
584 res = ast_waitstream(chan, ints);
586 ast_stopstream(chan);
594 /*--- ast_say_number_full_da: Danish syntax */
596 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
598 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
603 int cn = 1; /* +1 = Commune; -1 = Neutrum */
606 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
608 if (options && !strncasecmp(options, "n",1)) cn = -1;
610 while(!res && (num || playh || playa )) {
611 /* The grammar for Danish numbers is the same as for English except
613 * - 1 exists in both commune ("en", file "1N") and neutrum ("et", file "1")
614 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
615 * "one-and twenty" and 68 is "eight-and sixty".
616 * - "million" is different in singular and plural form
617 * - numbers > 1000 with zero as the third digit from last have an
618 * "and" before the last two digits, i.e. 2034 is "two thousand and
619 * four-and thirty" and 1000012 is "one million and twelve".
622 snprintf(fn, sizeof(fn), "digits/hundred");
625 snprintf(fn, sizeof(fn), "digits/and");
627 } else if (num == 1 && cn == -1) {
628 snprintf(fn, sizeof(fn), "digits/1N");
630 } else if (num < 20) {
631 snprintf(fn, sizeof(fn), "digits/%d", num);
633 } else if (num < 100) {
636 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
639 snprintf(fn, sizeof(fn), "digits/%d", num);
644 int hundreds = num / 100;
646 snprintf(fn, sizeof(fn), "digits/1N");
648 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
651 num -= 100 * hundreds;
657 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
661 snprintf(fn, sizeof(fn), "digits/thousand");
663 if (num < 1000000000) {
664 int millions = num / 1000000;
665 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
669 snprintf(fn, sizeof(fn), "digits/million");
671 snprintf(fn, sizeof(fn), "digits/millions");
674 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
678 if (num && num < 100)
683 if(!ast_streamfile(chan, fn, language)) {
684 if (audiofd && ctrlfd)
685 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
687 res = ast_waitstream(chan, ints);
689 ast_stopstream(chan);
695 /*--- ast_say_number_full_de: German syntax */
697 In addition to English, the following sounds are required: "millions", "and" and "1-and" through "9-and"
699 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
706 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
708 while(!res && (num || playh || playa )) {
709 /* The grammar for German numbers is the same as for English except
711 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
712 * "one-and twenty" and 68 is "eight-and sixty".
713 * - "million" is different in singular and plural form
714 * - numbers > 1000 with zero as the third digit from last have an
715 * "and" before the last two digits, i.e. 2034 is "two thousand and
716 * four-and thirty" and 1000012 is "one million and twelve".
719 snprintf(fn, sizeof(fn), "digits/hundred");
722 snprintf(fn, sizeof(fn), "digits/and");
724 } else if (num < 20) {
725 snprintf(fn, sizeof(fn), "digits/%d", num);
727 } else if (num < 100) {
730 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
733 snprintf(fn, sizeof(fn), "digits/%d", num);
738 int hundreds = num / 100;
740 snprintf(fn, sizeof(fn), "digits/1N");
742 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
744 num -= 100 * hundreds;
749 res = ast_say_number_full_de(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
753 snprintf(fn, sizeof(fn), "digits/thousand");
755 if (num < 1000000000) {
756 int millions = num / 1000000;
757 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
761 snprintf(fn, sizeof(fn), "digits/million");
763 snprintf(fn, sizeof(fn), "digits/millions");
766 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
770 if (num && num < 100)
775 if(!ast_streamfile(chan, fn, language)) {
776 if (audiofd && ctrlfd)
777 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
779 res = ast_waitstream(chan, ints);
781 ast_stopstream(chan);
787 /*--- ast_say_number_full_es: spanish syntax */
789 Requires a few new audios:
790 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
792 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
798 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
799 while (!res && num) {
801 snprintf(fn, sizeof(fn), "digits/y");
803 } else if (num < 31) {
804 snprintf(fn, sizeof(fn), "digits/%d", num);
806 } else if (num < 100) {
807 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
808 num -= ((num/10)*10);
811 } else if (num == 100) {
812 snprintf(fn, sizeof(fn), "digits/cien");
816 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
817 num -= ((num/100)*100);
820 res = ast_say_number_full_es(chan, num / 1000, ints, language, audiofd, ctrlfd);
824 snprintf(fn, sizeof(fn), "digits/mil");
826 if (num < 2147483640) {
827 res = ast_say_number_full_es(chan, num / 1000000, ints, language, audiofd, ctrlfd);
830 if ((num/1000000) == 1) {
831 snprintf(fn, sizeof(fn), "digits/millon");
833 snprintf(fn, sizeof(fn), "digits/millones");
837 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
845 if(!ast_streamfile(chan, fn, language)) {
846 if (audiofd && ctrlfd)
847 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
849 res = ast_waitstream(chan, ints);
851 ast_stopstream(chan);
860 /*--- ast_say_number_full_fr: French syntax */
861 /* Extra sounds needed: */
862 /* 1F: feminin 'une' */
864 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
869 int mf = 1; /* +1 = Masculin; -1 = Feminin */
872 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
874 if (options && !strncasecmp(options, "f",1))
877 while(!res && (num || playh || playa)) {
879 snprintf(fn, sizeof(fn), "digits/hundred");
882 snprintf(fn, sizeof(fn), "digits/et");
884 } else if (num == 1) {
886 snprintf(fn, sizeof(fn), "digits/%dF", num);
888 snprintf(fn, sizeof(fn), "digits/%d", num);
890 } else if (num < 21) {
891 snprintf(fn, sizeof(fn), "digits/%d", num);
893 } else if (num < 70) {
894 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
895 if ((num % 10) == 1) playa++;
897 } else if (num < 80) {
898 snprintf(fn, sizeof(fn), "digits/60");
899 if ((num % 10) == 1) playa++;
901 } else if (num < 100) {
902 snprintf(fn, sizeof(fn), "digits/80");
904 } else if (num < 200) {
905 snprintf(fn, sizeof(fn), "digits/hundred");
907 } else if (num < 1000) {
908 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
911 } else if (num < 2000) {
912 snprintf(fn, sizeof(fn), "digits/thousand");
914 } else if (num < 1000000) {
915 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
918 snprintf(fn, sizeof(fn), "digits/thousand");
920 } else if (num < 1000000000) {
921 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
924 snprintf(fn, sizeof(fn), "digits/million");
927 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
931 if(!ast_streamfile(chan, fn, language)) {
932 if (audiofd && ctrlfd)
933 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
935 res = ast_waitstream(chan, ints);
937 ast_stopstream(chan);
943 /*--- ast_say_number_full_it: Italian */
944 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
952 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
957 Like english, numbers up to 20 are a single 'word', and others
958 compound, but with exceptions.
959 For example 21 is not twenty-one, but there is a single word in 'it'.
960 Idem for 28 (ie when a the 2nd part of a compund number
963 There are exceptions also for hundred, thousand and million.
964 In english 100 = one hundred, 200 is two hundred.
965 In italian 100 = cento , like to say hundred (without one),
966 200 and more are like english.
968 Same applies for thousand:
969 1000 is one thousand in en, 2000 is two thousand.
970 In it we have 1000 = mille , 2000 = 2 mila
972 For million(s) we use the plural, if more than one
973 Also, one million is abbreviated in it, like on-million,
974 or 'un milione', not 'uno milione'.
975 So the right file is provided.
978 while(!res && (num || playh)) {
980 snprintf(fn, sizeof(fn), "digits/hundred");
982 } else if (num < 20) {
983 snprintf(fn, sizeof(fn), "digits/%d", num);
985 } else if (num == 21) {
986 snprintf(fn, sizeof(fn), "digits/%d", num);
988 } else if (num == 28) {
989 snprintf(fn, sizeof(fn), "digits/%d", num);
991 } else if (num == 31) {
992 snprintf(fn, sizeof(fn), "digits/%d", num);
994 } else if (num == 38) {
995 snprintf(fn, sizeof(fn), "digits/%d", num);
997 } else if (num == 41) {
998 snprintf(fn, sizeof(fn), "digits/%d", num);
1000 } else if (num == 48) {
1001 snprintf(fn, sizeof(fn), "digits/%d", num);
1003 } else if (num == 51) {
1004 snprintf(fn, sizeof(fn), "digits/%d", num);
1006 } else if (num == 58) {
1007 snprintf(fn, sizeof(fn), "digits/%d", num);
1009 } else if (num == 61) {
1010 snprintf(fn, sizeof(fn), "digits/%d", num);
1012 } else if (num == 68) {
1013 snprintf(fn, sizeof(fn), "digits/%d", num);
1015 } else if (num == 71) {
1016 snprintf(fn, sizeof(fn), "digits/%d", num);
1018 } else if (num == 78) {
1019 snprintf(fn, sizeof(fn), "digits/%d", num);
1021 } else if (num == 81) {
1022 snprintf(fn, sizeof(fn), "digits/%d", num);
1024 } else if (num == 88) {
1025 snprintf(fn, sizeof(fn), "digits/%d", num);
1027 } else if (num == 91) {
1028 snprintf(fn, sizeof(fn), "digits/%d", num);
1030 } else if (num == 98) {
1031 snprintf(fn, sizeof(fn), "digits/%d", num);
1033 } else if (num < 100) {
1034 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1035 num -= ((num / 10) * 10);
1038 if ((num / 100) > 1) {
1039 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1042 snprintf(fn, sizeof(fn), "digits/hundred");
1044 num -= ((num / 100) * 100);
1046 if (num < 1000000) { /* 1,000,000 */
1048 res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd);
1053 if ((tempnum / 1000) < 2)
1054 snprintf(fn, sizeof(fn), "digits/thousand");
1055 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1056 snprintf(fn, sizeof(fn), "digits/thousands");
1058 if (num < 1000000000) { /* 1,000,000,000 */
1059 if ((num / 1000000) > 1)
1060 res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1064 num = num % 1000000;
1065 if ((tempnum / 1000000) < 2)
1066 snprintf(fn, sizeof(fn), "digits/million");
1068 snprintf(fn, sizeof(fn), "digits/millions");
1070 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1077 if(!ast_streamfile(chan, fn, language)) {
1078 if (audiofd && ctrlfd)
1079 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1081 res = ast_waitstream(chan, ints);
1083 ast_stopstream(chan);
1089 /*--- ast_say_number_full_nl: dutch syntax */
1090 /* New files: digits/nl-en
1092 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
1099 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1100 while (!res && (num || playh )) {
1102 snprintf(fn, sizeof(fn), "digits/hundred");
1104 } else if (num < 20) {
1105 snprintf(fn, sizeof(fn), "digits/%d", num);
1107 } else if (num < 100) {
1110 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1114 snprintf(fn, sizeof(fn), "digits/nl-en");
1116 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1121 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1123 num -= ((num / 100) * 100);
1125 if (num < 1000000) { /* 1,000,000 */
1126 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1130 snprintf(fn, sizeof(fn), "digits/thousand");
1132 if (num < 1000000000) { /* 1,000,000,000 */
1133 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1136 num = num % 1000000;
1137 snprintf(fn, sizeof(fn), "digits/million");
1139 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1147 if(!ast_streamfile(chan, fn, language)) {
1148 if (audiofd && ctrlfd)
1149 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1151 res = ast_waitstream(chan, ints);
1153 ast_stopstream(chan);
1159 /* ast_say_number_full_pt: Portuguese syntax */
1160 /* Extra sounds needed: */
1161 /* For feminin all sound files end with F */
1162 /* 100E for 100+ something */
1163 /* 1000000S for plural */
1164 /* pt-e for 'and' */
1165 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1169 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1173 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1175 if (options && !strncasecmp(options, "f",1))
1178 while(!res && num ) {
1180 if ((num == 1 || num == 2) && (mf < 0))
1181 snprintf(fn, sizeof(fn), "digits/%dF", num);
1183 snprintf(fn, sizeof(fn), "digits/%d", num);
1185 } else if (num < 100) {
1186 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1190 } else if (num < 1000) {
1192 snprintf(fn, sizeof(fn), "digits/100");
1194 snprintf(fn, sizeof(fn), "digits/100E");
1196 if (mf < 0 && num > 199)
1197 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1199 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1204 } else if (num < 1000000) {
1206 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1210 snprintf(fn, sizeof(fn), "digits/1000");
1211 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
1214 } else if (num < 1000000000) {
1215 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
1219 snprintf(fn, sizeof(fn), "digits/1000000");
1221 snprintf(fn, sizeof(fn), "digits/1000000S");
1223 if ((num % 1000000) &&
1225 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
1226 // no hundreds and below
1227 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
1229 num = num % 1000000;
1231 if (!res && playh) {
1232 res = wait_file(chan, ints, "digits/pt-e", language);
1233 ast_stopstream(chan);
1237 if(!ast_streamfile(chan, fn, language)) {
1238 if (audiofd && ctrlfd)
1239 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else
1240 res = ast_waitstream(chan, ints);
1242 ast_stopstream(chan);
1248 /*--- ast_say_number_full_se: Swedish/Norwegian syntax */
1249 static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1254 int cn = 1; /* +1 = Commune; -1 = Neutrum */
1256 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1257 if (options && !strncasecmp(options, "n",1)) cn = -1;
1259 while(!res && (num || playh)) {
1261 snprintf(fn, sizeof(fn), "digits/hundred");
1265 snprintf(fn, sizeof(fn), "digits/%d", num);
1269 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1270 num -= ((num / 10) * 10);
1272 if (num == 1 && cn == -1) { /* En eller ett? */
1273 snprintf(fn, sizeof(fn), "digits/1N");
1277 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1279 num -= ((num / 100) * 100);
1281 if (num < 1000000) { /* 1,000,000 */
1282 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1286 snprintf(fn, sizeof(fn), "digits/thousand");
1288 if (num < 1000000000) { /* 1,000,000,000 */
1289 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1292 num = num % 1000000;
1293 snprintf(fn, sizeof(fn), "digits/million");
1295 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1302 if(!ast_streamfile(chan, fn, language)) {
1303 if (audiofd && ctrlfd)
1304 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1306 res = ast_waitstream(chan, ints);
1308 ast_stopstream(chan);
1316 int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang)
1318 if (!strcasecmp(lang,"en") ) { /* English syntax */
1319 return(ast_say_date_en(chan, t, ints, lang));
1320 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
1321 return(ast_say_date_nl(chan, t, ints, lang));
1322 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
1323 return(ast_say_date_pt(chan, t, ints, lang));
1326 /* Default to English */
1327 return(ast_say_date_en(chan, t, ints, lang));
1331 /* English syntax */
1332 int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
1337 ast_localtime(&t,&tm,NULL);
1339 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1340 res = ast_streamfile(chan, fn, lang);
1342 res = ast_waitstream(chan, ints);
1345 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1346 res = ast_streamfile(chan, fn, lang);
1348 res = ast_waitstream(chan, ints);
1351 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1353 res = ast_waitstream(chan, ints);
1355 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1360 int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
1365 ast_localtime(&t,&tm,NULL);
1367 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1368 res = ast_streamfile(chan, fn, lang);
1370 res = ast_waitstream(chan, ints);
1373 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1375 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1376 res = ast_streamfile(chan, fn, lang);
1378 res = ast_waitstream(chan, ints);
1381 res = ast_waitstream(chan, ints);
1383 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1387 /* Portuguese syntax */
1388 int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
1393 ast_localtime(&t,&tm,NULL);
1394 localtime_r(&t,&tm);
1395 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1397 res = wait_file(chan, ints, fn, lang);
1399 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1401 res = wait_file(chan, ints, "digits/pt-de", lang);
1402 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1404 res = wait_file(chan, ints, fn, lang);
1406 res = wait_file(chan, ints, "digits/pt-de", lang);
1408 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1413 int ast_say_date_with_format(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1415 if (!strcasecmp(lang, "en") ) { /* English syntax */
1416 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1417 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
1418 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
1419 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
1420 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
1423 /* Default to English */
1424 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1427 /* English syntax */
1428 int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1431 int res=0, offset, sndoffset;
1432 char sndfile[256], nextmsg[256];
1434 ast_localtime(&time,&tm,timezone);
1436 for (offset=0 ; format[offset] != '\0' ; offset++) {
1437 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1438 switch (format[offset]) {
1439 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
1441 /* Literal name of a sound file */
1443 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1444 sndfile[sndoffset] = format[offset];
1445 sndfile[sndoffset] = '\0';
1446 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1447 res = wait_file(chan,ints,nextmsg,lang);
1451 /* Sunday - Saturday */
1452 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1453 res = wait_file(chan,ints,nextmsg,lang);
1458 /* January - December */
1459 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1460 res = wait_file(chan,ints,nextmsg,lang);
1464 /* First - Thirtyfirst */
1465 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1466 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
1467 res = wait_file(chan,ints,nextmsg,lang);
1468 } else if (tm.tm_mday == 31) {
1469 /* "Thirty" and "first" */
1470 res = wait_file(chan,ints, "digits/30",lang);
1472 res = wait_file(chan,ints, "digits/h-1",lang);
1475 /* Between 21 and 29 - two sounds */
1476 res = wait_file(chan,ints, "digits/20",lang);
1478 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
1479 res = wait_file(chan,ints,nextmsg,lang);
1485 if (tm.tm_year > 99) {
1486 res = wait_file(chan,ints, "digits/2",lang);
1488 res = wait_file(chan,ints, "digits/thousand",lang);
1490 if (tm.tm_year > 100) {
1492 /* This works until the end of 2020 */
1493 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1494 res = wait_file(chan,ints,nextmsg,lang);
1498 if (tm.tm_year < 1) {
1499 /* I'm not going to handle 1900 and prior */
1500 /* We'll just be silent on the year, instead of bombing out. */
1502 res = wait_file(chan,ints, "digits/19",lang);
1504 if (tm.tm_year <= 9) {
1506 res = wait_file(chan,ints, "digits/oh",lang);
1508 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1509 res = wait_file(chan,ints,nextmsg,lang);
1511 } else if (tm.tm_year <= 20) {
1513 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1514 res = wait_file(chan,ints,nextmsg,lang);
1518 ten = tm.tm_year / 10;
1519 one = tm.tm_year % 10;
1520 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1521 res = wait_file(chan,ints,nextmsg,lang);
1524 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1525 res = wait_file(chan,ints,nextmsg,lang);
1536 if (tm.tm_hour == 0)
1537 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1538 else if (tm.tm_hour > 12)
1539 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1541 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1542 res = wait_file(chan,ints,nextmsg,lang);
1547 if (format[offset] == 'H') {
1549 if (tm.tm_hour < 10) {
1550 res = wait_file(chan,ints, "digits/oh",lang);
1554 if (tm.tm_hour == 0) {
1555 res = wait_file(chan,ints, "digits/oh",lang);
1559 if (tm.tm_hour != 0) {
1560 int remainder = tm.tm_hour;
1561 if (tm.tm_hour > 20) {
1562 res = wait_file(chan,ints, "digits/20",lang);
1566 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1567 res = wait_file(chan,ints,nextmsg,lang);
1574 if (tm.tm_min == 0) {
1575 res = wait_file(chan,ints, "digits/oclock",lang);
1576 } else if (tm.tm_min < 10) {
1577 res = wait_file(chan,ints, "digits/oh",lang);
1579 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1580 res = wait_file(chan,ints,nextmsg,lang);
1582 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
1583 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1584 res = wait_file(chan,ints,nextmsg,lang);
1587 ten = (tm.tm_min / 10) * 10;
1588 one = (tm.tm_min % 10);
1589 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1590 res = wait_file(chan,ints,nextmsg,lang);
1592 /* Fifty, not fifty-zero */
1594 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1595 res = wait_file(chan,ints,nextmsg,lang);
1603 if (tm.tm_hour > 11)
1604 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1606 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1607 res = wait_file(chan,ints,nextmsg,lang);
1610 /* Shorthand for "Today", "Yesterday", or ABdY */
1616 gettimeofday(&now,NULL);
1617 ast_localtime(&now.tv_sec,&tmnow,timezone);
1618 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1619 /* In any case, it saves not having to do ast_mktime() */
1620 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1621 if (beg_today < time) {
1623 res = wait_file(chan,ints, "digits/today",lang);
1624 } else if (beg_today - 86400 < time) {
1626 res = wait_file(chan,ints, "digits/yesterday",lang);
1628 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1633 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1639 gettimeofday(&now,NULL);
1640 ast_localtime(&now.tv_sec,&tmnow,timezone);
1641 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1642 /* In any case, it saves not having to do ast_mktime() */
1643 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1644 if (beg_today < time) {
1646 } else if ((beg_today - 86400) < time) {
1648 res = wait_file(chan,ints, "digits/yesterday",lang);
1649 } else if (beg_today - 86400 * 6 < time) {
1650 /* Within the last week */
1651 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1653 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1658 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1662 if (tm.tm_sec == 0) {
1663 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1664 res = wait_file(chan,ints,nextmsg,lang);
1665 } else if (tm.tm_sec < 10) {
1666 res = wait_file(chan,ints, "digits/oh",lang);
1668 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1669 res = wait_file(chan,ints,nextmsg,lang);
1671 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1672 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1673 res = wait_file(chan,ints,nextmsg,lang);
1676 ten = (tm.tm_sec / 10) * 10;
1677 one = (tm.tm_sec % 10);
1678 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1679 res = wait_file(chan,ints,nextmsg,lang);
1681 /* Fifty, not fifty-zero */
1683 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1684 res = wait_file(chan,ints,nextmsg,lang);
1690 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1694 /* Just ignore spaces and tabs */
1697 /* Unknown character */
1698 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1700 /* Jump out on DTMF */
1709 int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1712 int res=0, offset, sndoffset;
1713 char sndfile[256], nextmsg[256];
1715 ast_localtime(&time,&tm,timezone);
1717 for (offset=0 ; format[offset] != '\0' ; offset++) {
1718 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1719 switch (format[offset]) {
1720 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
1722 /* Literal name of a sound file */
1724 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1725 sndfile[sndoffset] = format[offset];
1726 sndfile[sndoffset] = '\0';
1727 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1728 res = wait_file(chan,ints,nextmsg,lang);
1732 /* Sunday - Saturday */
1733 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1734 res = wait_file(chan,ints,nextmsg,lang);
1739 /* January - December */
1740 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1741 res = wait_file(chan,ints,nextmsg,lang);
1745 /* First - Thirtyfirst */
1746 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
1750 if (tm.tm_year > 99) {
1751 res = wait_file(chan,ints, "digits/2",lang);
1753 res = wait_file(chan,ints, "digits/thousand",lang);
1755 if (tm.tm_year > 100) {
1757 /* This works until the end of 2020 */
1758 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1759 res = wait_file(chan,ints,nextmsg,lang);
1763 if (tm.tm_year < 1) {
1764 /* I'm not going to handle 1900 and prior */
1765 /* We'll just be silent on the year, instead of bombing out. */
1767 res = wait_file(chan,ints, "digits/19",lang);
1769 if (tm.tm_year <= 9) {
1771 res = wait_file(chan,ints, "digits/oh",lang);
1773 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1774 res = wait_file(chan,ints,nextmsg,lang);
1776 } else if (tm.tm_year <= 20) {
1778 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1779 res = wait_file(chan,ints,nextmsg,lang);
1783 ten = tm.tm_year / 10;
1784 one = tm.tm_year % 10;
1785 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1786 res = wait_file(chan,ints,nextmsg,lang);
1789 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1790 res = wait_file(chan,ints,nextmsg,lang);
1801 if (tm.tm_hour == 0)
1802 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1803 else if (tm.tm_hour > 12)
1804 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1806 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1807 res = wait_file(chan,ints,nextmsg,lang);
1812 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
1814 res = wait_file(chan,ints, "digits/nl-uur",lang);
1819 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
1824 if (tm.tm_hour > 11)
1825 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1827 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1828 res = wait_file(chan,ints,nextmsg,lang);
1831 /* Shorthand for "Today", "Yesterday", or ABdY */
1837 gettimeofday(&now,NULL);
1838 ast_localtime(&now.tv_sec,&tmnow,timezone);
1839 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1840 /* In any case, it saves not having to do ast_mktime() */
1841 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1842 if (beg_today < time) {
1844 res = wait_file(chan,ints, "digits/today",lang);
1845 } else if (beg_today - 86400 < time) {
1847 res = wait_file(chan,ints, "digits/yesterday",lang);
1849 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1854 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1860 gettimeofday(&now,NULL);
1861 ast_localtime(&now.tv_sec,&tmnow,timezone);
1862 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1863 /* In any case, it saves not having to do ast_mktime() */
1864 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1865 if (beg_today < time) {
1867 } else if ((beg_today - 86400) < time) {
1869 res = wait_file(chan,ints, "digits/yesterday",lang);
1870 } else if (beg_today - 86400 * 6 < time) {
1871 /* Within the last week */
1872 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1874 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1879 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1883 if (tm.tm_sec == 0) {
1884 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1885 res = wait_file(chan,ints,nextmsg,lang);
1886 } else if (tm.tm_sec < 10) {
1887 res = wait_file(chan,ints, "digits/oh",lang);
1889 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1890 res = wait_file(chan,ints,nextmsg,lang);
1892 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1893 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1894 res = wait_file(chan,ints,nextmsg,lang);
1897 ten = (tm.tm_sec / 10) * 10;
1898 one = (tm.tm_sec % 10);
1899 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1900 res = wait_file(chan,ints,nextmsg,lang);
1902 /* Fifty, not fifty-zero */
1904 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1905 res = wait_file(chan,ints,nextmsg,lang);
1911 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1915 /* Just ignore spaces and tabs */
1918 /* Unknown character */
1919 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1921 /* Jump out on DTMF */
1929 /* Portuguese syntax */
1930 int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1933 int res=0, offset, sndoffset;
1934 char sndfile[256], nextmsg[256];
1936 ast_localtime(&time,&tm,timezone);
1938 for (offset=0 ; format[offset] != '\0' ; offset++) {
1939 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1940 switch (format[offset]) {
1941 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
1943 /* Literal name of a sound file */
1945 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1946 sndfile[sndoffset] = format[offset];
1947 sndfile[sndoffset] = '\0';
1948 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
1949 res = wait_file(chan,ints,nextmsg,lang);
1953 /* Sunday - Saturday */
1954 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1955 res = wait_file(chan,ints,nextmsg,lang);
1960 /* January - December */
1961 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1962 res = wait_file(chan,ints,nextmsg,lang);
1966 /* First - Thirtyfirst */
1967 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1971 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1976 if (tm.tm_hour == 0) {
1977 if (format[offset] == 'I')
1978 res = wait_file(chan, ints, "digits/pt-ah", lang);
1980 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
1982 else if (tm.tm_hour == 12) {
1983 if (format[offset] == 'I')
1984 res = wait_file(chan, ints, "digits/pt-ao", lang);
1986 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
1989 if (format[offset] == 'I') {
1990 res = wait_file(chan, ints, "digits/pt-ah", lang);
1991 if ((tm.tm_hour % 12) != 1)
1993 res = wait_file(chan, ints, "digits/pt-sss", lang);
1996 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
2002 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
2004 if (tm.tm_hour != 0) {
2005 int remainder = tm.tm_hour;
2006 if (tm.tm_hour > 20) {
2007 res = wait_file(chan,ints, "digits/20",lang);
2011 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
2012 res = wait_file(chan,ints,nextmsg,lang);
2019 if (tm.tm_min == 0) {
2020 res = wait_file(chan, ints, "digits/pt-hora", lang);
2021 if (tm.tm_hour != 1)
2023 res = wait_file(chan, ints, "digits/pt-sss", lang); } else {
2024 res = wait_file(chan,ints,"digits/pt-e",lang);
2026 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2032 if (tm.tm_hour > 12)
2033 res = wait_file(chan, ints, "digits/p-m", lang);
2034 else if (tm.tm_hour && tm.tm_hour < 12)
2035 res = wait_file(chan, ints, "digits/a-m", lang);
2038 /* Shorthand for "Today", "Yesterday", or ABdY */
2044 gettimeofday(&now,NULL);
2045 ast_localtime(&now.tv_sec,&tmnow,timezone);
2046 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2047 /* In any case, it saves not having to do ast_mktime() */
2048 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2049 if (beg_today < time) {
2051 res = wait_file(chan,ints, "digits/today",lang);
2052 } else if (beg_today - 86400 < time) {
2054 res = wait_file(chan,ints, "digits/yesterday",lang);
2056 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
2061 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2067 gettimeofday(&now,NULL);
2068 ast_localtime(&now.tv_sec,&tmnow,timezone);
2069 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2070 /* In any case, it saves not having to do ast_mktime() */
2071 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2072 if (beg_today < time) {
2074 } else if ((beg_today - 86400) < time) {
2076 res = wait_file(chan,ints, "digits/yesterday",lang);
2077 } else if (beg_today - 86400 * 6 < time) {
2078 /* Within the last week */
2079 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2081 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
2086 res = ast_say_date_with_format(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
2090 if (tm.tm_sec == 0) {
2091 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2092 res = wait_file(chan,ints,nextmsg,lang);
2093 } else if (tm.tm_sec < 10) {
2094 res = wait_file(chan,ints, "digits/oh",lang);
2096 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2097 res = wait_file(chan,ints,nextmsg,lang);
2099 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2100 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2101 res = wait_file(chan,ints,nextmsg,lang);
2104 ten = (tm.tm_sec / 10) * 10;
2105 one = (tm.tm_sec % 10);
2106 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2107 res = wait_file(chan,ints,nextmsg,lang);
2109 /* Fifty, not fifty-zero */
2111 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2112 res = wait_file(chan,ints,nextmsg,lang);
2118 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2122 /* Just ignore spaces and tabs */
2125 /* Unknown character */
2126 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2128 /* Jump out on DTMF */
2136 int ast_say_time(struct ast_channel *chan, time_t t, char *ints, char *lang)
2138 if (!strcasecmp(lang, "en") ) { /* English syntax */
2139 return(ast_say_time_en(chan, t, ints, lang));
2140 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2141 return(ast_say_time_nl(chan, t, ints, lang));
2142 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2143 return(ast_say_time_pt(chan, t, ints, lang));
2146 /* Default to English */
2147 return(ast_say_time_en(chan, t, ints, lang));
2150 /* English syntax */
2151 int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2156 localtime_r(&t,&tm);
2160 else if (hour == 12)
2162 else if (hour > 12) {
2167 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2169 if (tm.tm_min > 9) {
2171 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2172 } else if (tm.tm_min) {
2174 res = ast_streamfile(chan, "digits/oh", lang);
2176 res = ast_waitstream(chan, ints);
2178 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2181 res = ast_streamfile(chan, "digits/oclock", lang);
2183 res = ast_waitstream(chan, ints);
2187 res = ast_streamfile(chan, "digits/p-m", lang);
2190 res = ast_streamfile(chan, "digits/a-m", lang);
2193 res = ast_waitstream(chan, ints);
2198 int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
2203 localtime_r(&t,&tm);
2206 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2209 res = ast_streamfile(chan, "digits/nl-uur", lang);
2211 res = ast_waitstream(chan, ints);
2214 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
2218 /* Portuguese syntax */
2219 int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2224 localtime_r(&t,&tm);
2227 res = ast_say_number(chan, hour, ints, lang, "f");
2230 res = wait_file(chan, ints, "digits/pt-e", lang);
2232 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2235 res = wait_file(chan, ints, "digits/pt-hora", lang);
2236 if (tm.tm_hour != 1)
2238 res = wait_file(chan, ints, "digits/pt-sss", lang);
2241 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2245 int ast_say_datetime(struct ast_channel *chan, time_t t, char *ints, char *lang)
2247 if (!strcasecmp(lang, "en") ) { /* English syntax */
2248 return(ast_say_datetime_en(chan, t, ints, lang));
2249 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
2250 return(ast_say_datetime_nl(chan, t, ints, lang));
2251 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2252 return(ast_say_datetime_pt(chan, t, ints, lang));
2255 /* Default to English */
2256 return(ast_say_datetime_en(chan, t, ints, lang));
2259 /* English syntax */
2260 int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2266 localtime_r(&t,&tm);
2268 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2269 res = ast_streamfile(chan, fn, lang);
2271 res = ast_waitstream(chan, ints);
2274 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2275 res = ast_streamfile(chan, fn, lang);
2277 res = ast_waitstream(chan, ints);
2280 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2285 else if (hour == 12)
2287 else if (hour > 12) {
2292 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2294 if (tm.tm_min > 9) {
2296 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2297 } else if (tm.tm_min) {
2299 res = ast_streamfile(chan, "digits/oh", lang);
2301 res = ast_waitstream(chan, ints);
2303 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2306 res = ast_streamfile(chan, "digits/oclock", lang);
2308 res = ast_waitstream(chan, ints);
2312 res = ast_streamfile(chan, "digits/p-m", lang);
2315 res = ast_streamfile(chan, "digits/a-m", lang);
2318 res = ast_waitstream(chan, ints);
2320 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2325 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
2329 localtime_r(&t,&tm);
2330 res = ast_say_date(chan, t, ints, lang);
2332 res = ast_streamfile(chan, "digits/nl-om", lang);
2334 res = ast_waitstream(chan, ints);
2337 ast_say_time(chan, t, ints, lang);
2341 /* Portuguese syntax */
2342 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2348 localtime_r(&t,&tm);
2350 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2351 res = ast_streamfile(chan, fn, lang);
2353 res = ast_waitstream(chan, ints);
2356 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2357 res = ast_streamfile(chan, fn, lang);
2359 res = ast_waitstream(chan, ints);
2362 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2367 else if (hour == 12)
2369 else if (hour > 12) {
2374 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2376 if (tm.tm_min > 9) {
2378 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2379 } else if (tm.tm_min) {
2381 res = ast_streamfile(chan, "digits/oh", lang);
2383 res = ast_waitstream(chan, ints);
2385 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2388 res = ast_streamfile(chan, "digits/oclock", lang);
2390 res = ast_waitstream(chan, ints);
2394 res = ast_streamfile(chan, "digits/p-m", lang);
2397 res = ast_streamfile(chan, "digits/a-m", lang);
2400 res = ast_waitstream(chan, ints);
2402 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2406 int ast_say_datetime_from_now(struct ast_channel *chan, time_t t, char *ints, char *lang)
2408 if (!strcasecmp(lang, "en") ) { /* English syntax */
2409 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
2410 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
2411 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
2414 /* Default to English */
2415 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
2418 /* English syntax */
2419 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2430 localtime_r(&t,&tm);
2431 localtime_r(&nowt,&now);
2432 daydiff = now.tm_yday - tm.tm_yday;
2433 if ((daydiff < 0) || (daydiff > 6)) {
2434 /* Day of month and month */
2436 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2437 res = ast_streamfile(chan, fn, lang);
2439 res = ast_waitstream(chan, ints);
2442 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2444 } else if (daydiff) {
2445 /* Just what day of the week */
2447 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2448 res = ast_streamfile(chan, fn, lang);
2450 res = ast_waitstream(chan, ints);
2452 } /* Otherwise, it was today */
2454 res = ast_say_time(chan, t, ints, lang);
2458 /* Portuguese syntax */
2459 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2470 localtime_r(&t,&tm);
2471 localtime_r(&nowt,&now);
2472 daydiff = now.tm_yday - tm.tm_yday;
2473 if ((daydiff < 0) || (daydiff > 6)) {
2474 /* Day of month and month */
2476 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2478 res = wait_file(chan, ints, "digits/pt-de", lang);
2479 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2481 res = wait_file(chan, ints, fn, lang);
2483 } else if (daydiff) {
2484 /* Just what day of the week */
2485 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2487 res = wait_file(chan, ints, fn, lang);
2488 } /* Otherwise, it was today */
2489 snprintf(fn, sizeof(fn), "digits/pt-ah");
2491 res = wait_file(chan, ints, fn, lang);
2492 if (tm.tm_hour != 1)
2494 res = wait_file(chan, ints, "digits/pt-sss", lang);
2496 res = ast_say_time(chan, t, ints, lang);