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>
19 #include <asterisk/file.h>
20 #include <asterisk/channel.h>
21 #include <asterisk/logger.h>
22 #include <asterisk/say.h>
23 #include <asterisk/lock.h>
24 #include <asterisk/localtime.h>
29 /* Forward declaration */
30 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang);
32 int ast_say_digit_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
34 /* XXX Merge with full version? XXX */
38 while(fn2[num] && !res) {
42 snprintf(fn, sizeof(fn), "digits/star");
45 snprintf(fn, sizeof(fn), "digits/pound");
48 if((fn2[num] >= '0') && (fn2[num] <= '9')){ /* Must be in {0-9} */
49 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
52 if(strlen(fn)){ /* if length == 0, then skip this digit as it is invalid */
53 res = ast_streamfile(chan, fn, lang);
55 res = ast_waitstream(chan, ints);
63 int ast_say_digit_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
68 while(fn2[num] && !res) {
69 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
70 res = ast_streamfile(chan, fn, lang);
72 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
79 int ast_say_digits(struct ast_channel *chan, int num, char *ints, char *lang)
81 /* XXX Should I be merged with say_digits_full XXX */
83 snprintf(fn2, sizeof(fn2), "%d", num);
84 return ast_say_digit_str(chan, fn2, ints, lang);
87 int ast_say_digits_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd)
90 snprintf(fn2, sizeof(fn2), "%d", num);
91 return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd);
94 /* Forward declarations */
95 /* Syntaxes supported, not really language codes.
96 en - English, Swedish, Norwegian
98 da - Danish (maybe German - please check)
104 For portuguese, we're using an option to saynumber() to indicate if the gender is male of female
105 This should also be implemented in _full version, really.
110 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
111 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
112 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
113 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
114 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
115 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
116 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
117 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
119 /*--- ast_say_number_full: call language-specific functions */
120 /* Called from AGI */
121 int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
123 char *options=(char *) NULL; /* While waiting for a general hack for agi */
125 if (!strcasecmp(language, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) {
126 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
127 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
128 return(ast_say_number_full_fr(chan, num, ints, language, audiofd, ctrlfd));
129 } else if (!strcasecmp(language, "de") ) { /* German syntax */
130 return(ast_say_number_full_de(chan, num, ints, language, audiofd, ctrlfd));
131 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
132 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
133 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
134 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
135 } else if (!strcasecmp(language, "pt") ) { /* Portuguese syntax */
136 return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
137 } else if (!strcasecmp(language, "es") ) { /* Spanish syntax */
138 return(ast_say_number_full_es(chan, num, ints, language, audiofd, ctrlfd));
139 } else if (!strcasecmp(language, "nl") ) { /* Spanish syntax */
140 return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
143 /* Default to english */
144 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
147 /*--- ast_say_number: call language-specific functions without file descriptors */
148 int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language, char *options)
150 if (!strcasecmp(language, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) {
151 return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
154 if (!strcasecmp(language, "fr")) { /* French syntax */
155 return(ast_say_number_full_fr(chan, num, ints, language, -1, -1));
156 } else if (!strcasecmp(language, "da")) { /* Danish syntax */
157 return(ast_say_number_full_da(chan, num, ints, language, options, -1, -1));
158 } else if (!strcasecmp(language, "it")) { /* Italian syntax */
159 return(ast_say_number_full_it(chan, num, ints, language, -1, -1));
160 } else if (!strcasecmp(language, "pt")) { /* Portuguese syntax */
161 return(ast_say_number_full_pt(chan, num, ints, language, options, -1, -1));
162 } else if (!strcasecmp(language, "nl")) { /* Spanish syntax */
163 return(ast_say_number_full_nl(chan, num, ints, language, -1, -1));
164 } else if (!strcasecmp(language, "es")) { /* Spanish syntax */
165 return(ast_say_number_full_es(chan, num, ints, language, -1, -1));
168 /* Default to english */
169 return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
172 /*--- ast_say_number_full_en: English syntax */
173 /* This is the default syntax, if no other syntax defined in this file is used */
174 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
180 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
182 while(!res && (num || playh)) {
184 snprintf(fn, sizeof(fn), "digits/hundred");
188 snprintf(fn, sizeof(fn), "digits/%d", num);
192 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
193 num -= ((num / 10) * 10);
196 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
198 num -= ((num / 100) * 100);
200 if (num < 1000000) { /* 1,000,000 */
201 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
205 snprintf(fn, sizeof(fn), "digits/thousand");
207 if (num < 1000000000) { /* 1,000,000,000 */
208 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
212 snprintf(fn, sizeof(fn), "digits/million");
214 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
221 if(!ast_streamfile(chan, fn, language)) {
222 if (audiofd && ctrlfd)
223 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
225 res = ast_waitstream(chan, ints);
227 ast_stopstream(chan);
236 /*--- ast_say_number_full_fr: French syntax */
237 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
241 int playa = 0; /* For french */
244 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
245 while(!res && (num || playh || playa)) {
247 snprintf(fn, sizeof(fn), "digits/hundred");
251 snprintf(fn, sizeof(fn), "digits/et");
255 snprintf(fn, sizeof(fn), "digits/%d", num);
259 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
260 if ((num % 10) == 1) playa++;
264 snprintf(fn, sizeof(fn), "digits/60");
265 if ((num % 10) == 1) playa++;
269 snprintf(fn, sizeof(fn), "digits/80");
273 snprintf(fn, sizeof(fn), "digits/hundred");
277 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
282 snprintf(fn, sizeof(fn), "digits/thousand");
286 res = ast_say_number_full_fr(chan, num / 1000, ints, language, audiofd, ctrlfd);
288 snprintf(fn, sizeof(fn), "digits/thousand");
291 if (num < 1000000000) {
292 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, audiofd, ctrlfd);
294 snprintf(fn, sizeof(fn), "digits/million");
297 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
301 if(!ast_streamfile(chan, fn, language)) {
302 if (audiofd && ctrlfd)
303 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
305 res = ast_waitstream(chan, ints);
307 ast_stopstream(chan);
316 /*--- ast_say_number_full_da: Danish syntax */
318 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
320 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
325 int cn = 1; /* +1 = Commune; -1 = Neutrum */
328 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
330 if (options && !strncasecmp(options, "n",1)) cn = -1;
332 while(!res && (num || playh || playa )) {
333 /* The grammer for Danish numbers is the same as for English except
335 * - 1 exists in both commune ("en", file "1N") and neutrum ("et", file "1")
336 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
337 * "one-and twenty" and 68 is "eight-and sixty".
338 * - "million" is different in singular and plural form
339 * - numbers > 1000 with zero as the third digit from last have an
340 * "and" before the last two digits, i.e. 2034 is "two thousand and
341 * four-and thirty" and 1000012 is "one million and twelve".
344 snprintf(fn, sizeof(fn), "digits/hundred");
348 snprintf(fn, sizeof(fn), "digits/and");
351 if (num == 1 && cn == -1) {
352 snprintf(fn, sizeof(fn), "digits/1N");
356 snprintf(fn, sizeof(fn), "digits/%d", num);
362 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
365 snprintf(fn, sizeof(fn), "digits/%d", num);
370 int hundreds = num / 100;
372 snprintf(fn, sizeof(fn), "digits/1N");
374 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
377 num -= 100 * hundreds;
383 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
387 snprintf(fn, sizeof(fn), "digits/thousand");
389 if (num < 1000000000) {
390 int millions = num / 1000000;
391 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
395 snprintf(fn, sizeof(fn), "digits/million");
397 snprintf(fn, sizeof(fn), "digits/millions");
400 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
404 if (num && num < 100)
409 if(!ast_streamfile(chan, fn, language)) {
410 if (audiofd && ctrlfd)
411 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
413 res = ast_waitstream(chan, ints);
415 ast_stopstream(chan);
423 /*--- ast_say_number_full_de: German syntax */
425 In addition to English, the following sounds are required: "millions", "and" and "1-and" through "9-and"
427 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
434 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
436 while(!res && (num || playh || playa )) {
437 /* The grammer for German numbers is the same as for English except
439 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
440 * "one-and twenty" and 68 is "eight-and sixty".
441 * - "million" is different in singular and plural form
442 * - numbers > 1000 with zero as the third digit from last have an
443 * "and" before the last two digits, i.e. 2034 is "two thousand and
444 * four-and thirty" and 1000012 is "one million and twelve".
447 snprintf(fn, sizeof(fn), "digits/hundred");
451 snprintf(fn, sizeof(fn), "digits/and");
455 snprintf(fn, sizeof(fn), "digits/%d", num);
461 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
464 snprintf(fn, sizeof(fn), "digits/%d", num);
469 int hundreds = num / 100;
471 snprintf(fn, sizeof(fn), "digits/1N");
473 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
476 num -= 100 * hundreds;
482 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
486 snprintf(fn, sizeof(fn), "digits/thousand");
488 if (num < 1000000000) {
489 int millions = num / 1000000;
490 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
494 snprintf(fn, sizeof(fn), "digits/million");
496 snprintf(fn, sizeof(fn), "digits/millions");
499 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
503 if (num && num < 100)
508 if(!ast_streamfile(chan, fn, language)) {
509 if (audiofd && ctrlfd)
510 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
512 res = ast_waitstream(chan, ints);
514 ast_stopstream(chan);
522 /*------------ Portuguese ----------------------*/
523 /* ast_say_number_full_pt: Portuguese syntax */
524 /* For feminin all sound files end with F */
525 /* 100E for 100+ something */
527 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
531 int mf = 1; /* +1 = Masculin; -1 = Feminin */
536 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
538 if (options && !strncasecmp(options, "f",1)) mf = -1;
540 while(!res && num ) {
543 if ((num == 1 || num == 2) && (mf < 0))
544 snprintf(fn, sizeof(fn), "digits/%dF", num);
546 snprintf(fn, sizeof(fn), "digits/%d", num);
550 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
557 snprintf(fn, sizeof(fn), "digits/100");
559 snprintf(fn, sizeof(fn), "digits/100E");
561 if (mf < 0 && num > 199)
562 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
564 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
572 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
576 snprintf(fn, sizeof(fn), "digits/1000");
577 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
581 if (num < 1000000000) {
582 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
587 snprintf(fn, sizeof(fn), "digits/1000000");
589 snprintf(fn, sizeof(fn), "digits/1000000S");
591 if ((num % 1000000) &&
593 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
594 // no hundreds and below
595 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
600 res = wait_file(chan, ints, "digits/pt-e", language);
601 ast_stopstream(chan);
605 if(!ast_streamfile(chan, fn, language)) {
606 if (audiofd && ctrlfd)
607 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
609 res = ast_waitstream(chan, ints);
611 ast_stopstream(chan);
620 /*--- ast_say_number_full_it: italian */
621 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
629 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
634 Like english, numbers till 20 are a single 'word', and other
635 compound, but with exceptions.
636 For example 21 is not twenty-one, but is a single word in it.
637 Idem for 28 (ie when a the 2nd part of a compund number
640 There're exceptions also for hundred, thounsand and million.
641 In english 100 = one hundred, 200 is two hundred.
642 In italian 100 = cento , like to say hundred (without one),
643 200 and more are like english.
645 Same apply for thousand:
646 1000 is one thousand in en, 2000 is two thousand.
647 In it we have 1000 = mille , 2000 = 2 mila
649 For million(s) we use the plural, if more than one
650 Also, one million is abbreviated in it, like on-million,
651 or 'un milione', not 'uno milione'.
652 So the right file is provided.
655 while(!res && (num || playh)) {
657 snprintf(fn, sizeof(fn), "digits/hundred");
661 snprintf(fn, sizeof(fn), "digits/%d", num);
665 snprintf(fn, sizeof(fn), "digits/%d", num);
669 snprintf(fn, sizeof(fn), "digits/%d", num);
673 snprintf(fn, sizeof(fn), "digits/%d", num);
677 snprintf(fn, sizeof(fn), "digits/%d", num);
681 snprintf(fn, sizeof(fn), "digits/%d", num);
685 snprintf(fn, sizeof(fn), "digits/%d", num);
689 snprintf(fn, sizeof(fn), "digits/%d", num);
693 snprintf(fn, sizeof(fn), "digits/%d", num);
697 snprintf(fn, sizeof(fn), "digits/%d", num);
701 snprintf(fn, sizeof(fn), "digits/%d", num);
705 snprintf(fn, sizeof(fn), "digits/%d", num);
709 snprintf(fn, sizeof(fn), "digits/%d", num);
713 snprintf(fn, sizeof(fn), "digits/%d", num);
717 snprintf(fn, sizeof(fn), "digits/%d", num);
721 snprintf(fn, sizeof(fn), "digits/%d", num);
725 snprintf(fn, sizeof(fn), "digits/%d", num);
729 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
730 num -= ((num / 10) * 10);
733 if ((num / 100) > 1) {
734 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
738 snprintf(fn, sizeof(fn), "digits/hundred");
740 num -= ((num / 100) * 100);
742 if (num < 1000000) { /* 1,000,000 */
744 res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd);
749 if ((tempnum / 1000) < 2)
750 snprintf(fn, sizeof(fn), "digits/thousand");
751 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
752 snprintf(fn, sizeof(fn), "digits/thousands");
754 if (num < 1000000000) { /* 1,000,000,000 */
755 if ((num / 1000000) > 1)
756 res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd);
761 if ((tempnum / 1000000) < 2)
762 snprintf(fn, sizeof(fn), "digits/million");
764 snprintf(fn, sizeof(fn), "digits/millions");
766 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
773 if(!ast_streamfile(chan, fn, language)) {
774 if (audiofd && ctrlfd)
775 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
777 res = ast_waitstream(chan, ints);
779 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");
805 snprintf(fn, sizeof(fn), "digits/%d", num);
809 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
810 num -= ((num/10)*10);
815 snprintf(fn, sizeof(fn), "digits/cien");
819 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
820 num -= ((num/100)*100);
823 res = ast_say_number_full_es(chan, num / 1000, ints, language, audiofd, ctrlfd);
827 snprintf(fn, sizeof(fn), "digits/mil");
829 if (num < 2147483640) {
830 res = ast_say_number_full_es(chan, num / 1000000, ints, language, audiofd, ctrlfd);
833 if ((num/1000000) == 1) {
834 snprintf(fn, sizeof(fn), "digits/millon");
836 snprintf(fn, sizeof(fn), "digits/millones");
840 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
848 if(!ast_streamfile(chan, fn, language)) {
849 if (audiofd && ctrlfd)
850 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
852 res = ast_waitstream(chan, ints);
854 ast_stopstream(chan);
862 /*--- ast_say_number_full_nl: dutch syntax */
865 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
872 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
873 while (!res && (num || playh )) {
875 snprintf(fn, sizeof(fn), "digits/hundred");
879 snprintf(fn, sizeof(fn), "digits/%d", num);
885 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
889 snprintf(fn, sizeof(fn), "digits/nl-en");
891 snprintf(fn, sizeof(fn), "digits/%d", num - units);
896 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
898 num -= ((num / 100) * 100);
900 if (num < 1000000) { /* 1,000,000 */
901 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
905 snprintf(fn, sizeof(fn), "digits/thousand");
907 if (num < 1000000000) { /* 1,000,000,000 */
908 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
912 snprintf(fn, sizeof(fn), "digits/million");
914 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
922 if(!ast_streamfile(chan, fn, language)) {
923 if (audiofd && ctrlfd)
924 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
926 res = ast_waitstream(chan, ints);
928 ast_stopstream(chan);
937 int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang)
942 ast_localtime(&t,&tm,NULL);
944 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
945 res = ast_streamfile(chan, fn, lang);
947 res = ast_waitstream(chan, ints);
950 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
951 res = ast_streamfile(chan, fn, lang);
953 res = ast_waitstream(chan, ints);
956 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
957 /* Should portuguese add a gender here? Defaults to masculin */
960 res = ast_waitstream(chan, ints);
962 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
966 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang)
969 if ((res = ast_streamfile(chan, file, lang)))
970 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
972 res = ast_waitstream(chan, ints);
976 int ast_say_date_with_format(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
979 int res=0, offset, sndoffset;
980 char sndfile[256], nextmsg[256];
982 ast_localtime(&time,&tm,timezone);
984 for (offset=0 ; format[offset] != '\0' ; offset++) {
985 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
986 switch (format[offset]) {
987 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
989 /* Literal name of a sound file */
991 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
992 sndfile[sndoffset] = format[offset];
993 sndfile[sndoffset] = '\0';
994 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
995 res = wait_file(chan,ints,nextmsg,lang);
999 /* Sunday - Saturday */
1000 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1001 res = wait_file(chan,ints,nextmsg,lang);
1006 /* January - December */
1007 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1008 res = wait_file(chan,ints,nextmsg,lang);
1012 /* First - Thirtyfirst */
1013 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1014 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
1015 res = wait_file(chan,ints,nextmsg,lang);
1016 } else if (tm.tm_mday == 31) {
1017 /* "Thirty" and "first" */
1018 res = wait_file(chan,ints, "digits/30",lang);
1020 res = wait_file(chan,ints, "digits/h-1",lang);
1023 /* Between 21 and 29 - two sounds */
1024 res = wait_file(chan,ints, "digits/20",lang);
1026 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
1027 res = wait_file(chan,ints,nextmsg,lang);
1033 if (tm.tm_year > 99) {
1034 res = wait_file(chan,ints, "digits/2",lang);
1036 res = wait_file(chan,ints, "digits/thousand",lang);
1038 if (tm.tm_year > 100) {
1040 /* This works until the end of 2020 */
1041 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1042 res = wait_file(chan,ints,nextmsg,lang);
1046 if (tm.tm_year < 1) {
1047 /* I'm not going to handle 1900 and prior */
1048 /* We'll just be silent on the year, instead of bombing out. */
1050 res = wait_file(chan,ints, "digits/19",lang);
1052 if (tm.tm_year <= 9) {
1054 res = wait_file(chan,ints, "digits/oh",lang);
1056 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1057 res = wait_file(chan,ints,nextmsg,lang);
1059 } else if (tm.tm_year <= 20) {
1061 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1062 res = wait_file(chan,ints,nextmsg,lang);
1066 ten = tm.tm_year / 10;
1067 one = tm.tm_year % 10;
1068 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1069 res = wait_file(chan,ints,nextmsg,lang);
1072 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1073 res = wait_file(chan,ints,nextmsg,lang);
1084 if (tm.tm_hour == 0)
1085 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1086 else if (tm.tm_hour > 12)
1087 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1089 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1090 res = wait_file(chan,ints,nextmsg,lang);
1095 if (format[offset] == 'H') {
1097 if (tm.tm_hour < 10) {
1098 res = wait_file(chan,ints, "digits/oh",lang);
1102 if (tm.tm_hour == 0) {
1103 res = wait_file(chan,ints, "digits/oh",lang);
1107 if (tm.tm_hour != 0) {
1108 int remainder = tm.tm_hour;
1109 if (tm.tm_hour > 20) {
1110 res = wait_file(chan,ints, "digits/20",lang);
1114 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1115 res = wait_file(chan,ints,nextmsg,lang);
1122 if (tm.tm_min == 0) {
1123 res = wait_file(chan,ints, "digits/oclock",lang);
1124 } else if (tm.tm_min < 10) {
1125 res = wait_file(chan,ints, "digits/oh",lang);
1127 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1128 res = wait_file(chan,ints,nextmsg,lang);
1130 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
1131 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1132 res = wait_file(chan,ints,nextmsg,lang);
1135 ten = (tm.tm_min / 10) * 10;
1136 one = (tm.tm_min % 10);
1137 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1138 res = wait_file(chan,ints,nextmsg,lang);
1140 /* Fifty, not fifty-zero */
1142 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1143 res = wait_file(chan,ints,nextmsg,lang);
1151 if (tm.tm_hour > 11)
1152 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1154 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1155 res = wait_file(chan,ints,nextmsg,lang);
1158 /* Shorthand for "Today", "Yesterday", or ABdY */
1164 gettimeofday(&now,NULL);
1165 ast_localtime(&now.tv_sec,&tmnow,timezone);
1166 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1167 /* In any case, it saves not having to do ast_mktime() */
1168 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1169 if (beg_today < time) {
1171 res = wait_file(chan,ints, "digits/today",lang);
1172 } else if (beg_today - 86400 < time) {
1174 res = wait_file(chan,ints, "digits/yesterday",lang);
1176 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1181 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1187 gettimeofday(&now,NULL);
1188 ast_localtime(&now.tv_sec,&tmnow,timezone);
1189 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1190 /* In any case, it saves not having to do ast_mktime() */
1191 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1192 if (beg_today < time) {
1194 } else if ((beg_today - 86400) < time) {
1196 res = wait_file(chan,ints, "digits/yesterday",lang);
1197 } else if (beg_today - 86400 * 6 < time) {
1198 /* Within the last week */
1199 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1201 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1206 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1210 if (tm.tm_sec == 0) {
1211 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1212 res = wait_file(chan,ints,nextmsg,lang);
1213 } else if (tm.tm_sec < 10) {
1214 res = wait_file(chan,ints, "digits/oh",lang);
1216 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1217 res = wait_file(chan,ints,nextmsg,lang);
1219 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1220 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1221 res = wait_file(chan,ints,nextmsg,lang);
1224 ten = (tm.tm_sec / 10) * 10;
1225 one = (tm.tm_sec % 10);
1226 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1227 res = wait_file(chan,ints,nextmsg,lang);
1229 /* Fifty, not fifty-zero */
1231 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1232 res = wait_file(chan,ints,nextmsg,lang);
1238 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1242 /* Just ignore spaces and tabs */
1245 /* Unknown character */
1246 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1248 /* Jump out on DTMF */
1256 int ast_say_time(struct ast_channel *chan, time_t t, char *ints, char *lang)
1261 localtime_r(&t,&tm);
1265 else if (hour == 12)
1267 else if (hour > 12) {
1272 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
1274 if (tm.tm_min > 9) {
1276 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
1277 } else if (tm.tm_min) {
1279 res = ast_streamfile(chan, "digits/oh", lang); /* This is very english ! */
1281 res = ast_waitstream(chan, ints);
1283 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
1286 res = ast_streamfile(chan, "digits/oclock", lang); /* This is very english ! */
1288 res = ast_waitstream(chan, ints);
1292 res = ast_streamfile(chan, "digits/p-m", lang);
1295 res = ast_streamfile(chan, "digits/a-m", lang);
1298 res = ast_waitstream(chan, ints);
1302 int ast_say_datetime(struct ast_channel *chan, time_t t, char *ints, char *lang)
1308 localtime_r(&t,&tm);
1310 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1311 res = ast_streamfile(chan, fn, lang);
1313 res = ast_waitstream(chan, ints);
1316 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1317 res = ast_streamfile(chan, fn, lang);
1319 res = ast_waitstream(chan, ints);
1322 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1327 else if (hour == 12)
1329 else if (hour > 12) {
1334 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
1336 if (tm.tm_min > 9) {
1338 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
1339 } else if (tm.tm_min) {
1341 res = ast_streamfile(chan, "digits/oh", lang);
1343 res = ast_waitstream(chan, ints);
1345 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
1348 res = ast_streamfile(chan, "digits/oclock", lang);
1350 res = ast_waitstream(chan, ints);
1354 res = ast_streamfile(chan, "digits/p-m", lang);
1357 res = ast_streamfile(chan, "digits/a-m", lang);
1360 res = ast_waitstream(chan, ints);
1362 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1366 int ast_say_datetime_from_now(struct ast_channel *chan, time_t t, char *ints, char *lang)
1377 localtime_r(&t,&tm);
1378 localtime_r(&nowt,&now);
1379 daydiff = now.tm_yday - tm.tm_yday;
1380 if ((daydiff < 0) || (daydiff > 6)) {
1381 /* Day of month and month */
1383 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1384 res = ast_streamfile(chan, fn, lang);
1386 res = ast_waitstream(chan, ints);
1389 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1391 } else if (daydiff) {
1392 /* Just what day of the week */
1394 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1395 res = ast_streamfile(chan, fn, lang);
1397 res = ast_waitstream(chan, ints);
1399 } /* Otherwise, it was today */
1401 res = ast_say_time(chan, t, ints, lang);