Merge bug 1483 -- advanced voicemail options
[asterisk/asterisk.git] / say.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Say numbers and dates (maybe words one day too)
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <sys/types.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <netinet/in.h>
18 #include <time.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>
25 #include "asterisk.h"
26 #include <stdio.h>
27
28 #define DIGITS_DIR      AST_SOUNDS "/digits/"
29
30 int ast_say_digit_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
31 {
32         /* XXX Merge with full version? XXX */
33         char fn[256] = "";
34         int num = 0;
35         int res = 0;
36         while(fn2[num] && !res) {
37                 fn[0] = '\0';
38                 switch (fn2[num]) {
39                         case ('*'):
40                                 snprintf(fn, sizeof(fn), "digits/star");
41                                 break;
42                         case ('#'):
43                                 snprintf(fn, sizeof(fn), "digits/pound");
44                                 break;
45                         default:
46                                 if((fn2[num] >= '0') && (fn2[num] <= '9')){ /* Must be in {0-9} */       
47                                         snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
48                                 }
49                         }
50                 if(strlen(fn)){ /* if length == 0, then skip this digit as it is invalid */
51                         res = ast_streamfile(chan, fn, lang);
52                         if (!res) 
53                                 res = ast_waitstream(chan, ints);
54                         ast_stopstream(chan);
55                 }
56                 num++;
57         }
58         return res;
59 }
60
61 int ast_say_digit_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
62 {
63         char fn[256] = "";
64         int num = 0;
65         int res = 0;
66         while(fn2[num] && !res) {
67                 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
68                 res = ast_streamfile(chan, fn, lang);
69                 if (!res) 
70                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
71                 ast_stopstream(chan);
72                 num++;
73         }
74         return res;
75 }
76
77 int ast_say_digits(struct ast_channel *chan, int num, char *ints, char *lang)
78 {
79         /* XXX Should I be merged with say_digits_full XXX */
80         char fn2[256];
81         snprintf(fn2, sizeof(fn2), "%d", num);
82         return ast_say_digit_str(chan, fn2, ints, lang);
83 }
84
85 int ast_say_digits_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd)
86 {
87         char fn2[256];
88         snprintf(fn2, sizeof(fn2), "%d", num);
89         return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd);
90 }
91
92 int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
93 {
94         int res = 0;
95         int playh = 0;
96         char fn[256] = "";
97         if (!num) 
98                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
99         if (0) {
100         /* XXX Only works for english XXX */
101         } else {
102                 /* Use english numbers if a given language is supported. */
103                 /* As a special case, Norwegian has the same numerical grammar
104                    as English */
105                 if (strcasecmp(language, "no"))
106                         language = "en";
107                 while(!res && (num || playh)) {
108                         if (playh) {
109                                 snprintf(fn, sizeof(fn), "digits/hundred");
110                                 playh = 0;
111                         } else
112                         if (num < 20) {
113                                 snprintf(fn, sizeof(fn), "digits/%d", num);
114                                 num = 0;
115                         } else
116                         if (num < 100) {
117                                 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
118                                 num -= ((num / 10) * 10);
119                         } else {
120                                 if (num < 1000){
121                                         snprintf(fn, sizeof(fn), "digits/%d", (num/100));
122                                         playh++;
123                                         num -= ((num / 100) * 100);
124                                 } else {
125                                         if (num < 1000000) { /* 1,000,000 */
126                                                 res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd);
127                                                 if (res)
128                                                         return res;
129                                                 num = num % 1000;
130                                                 snprintf(fn, sizeof(fn), "digits/thousand");
131                                         } else {
132                                                 if (num < 1000000000) { /* 1,000,000,000 */
133                                                         res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd);
134                                                         if (res)
135                                                                 return res;
136                                                         num = num % 1000000;
137                                                         snprintf(fn, sizeof(fn), "digits/million");
138                                                 } else {
139                                                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
140                                                         res = -1;
141                                                 }
142                                         }
143                                 }
144                         }
145                         if (!res) {
146                                 res = ast_streamfile(chan, fn, language);
147                                 if (!res) 
148                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
149                                 ast_stopstream(chan);
150                         }
151                         
152                 }
153         }
154         return res;
155 }
156
157 int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language)
158 {
159         /* XXX Should I be merged with ast_say_number_full XXX */
160         int res = 0;
161         int playh = 0;
162         char fn[256] = "";
163         if (!num) 
164                 return ast_say_digits(chan, 0,ints, language);
165         if (0) {
166         /* XXX Only works for english XXX */
167         } else {
168                 /* Use english numbers */
169                 language = "en";
170                 while(!res && (num || playh)) {
171                         if (playh) {
172                                 snprintf(fn, sizeof(fn), "digits/hundred");
173                                 playh = 0;
174                         } else
175                         if (num < 20) {
176                                 snprintf(fn, sizeof(fn), "digits/%d", num);
177                                 num = 0;
178                         } else
179                         if (num < 100) {
180                                 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
181                                 num -= ((num / 10) * 10);
182                         } else {
183                                 if (num < 1000){
184                                         snprintf(fn, sizeof(fn), "digits/%d", (num/100));
185                                         playh++;
186                                         num -= ((num / 100) * 100);
187                                 } else {
188                                         if (num < 1000000) {
189                                                 res = ast_say_number(chan, num / 1000, ints, language);
190                                                 if (res)
191                                                         return res;
192                                                 num = num % 1000;
193                                                 snprintf(fn, sizeof(fn), "digits/thousand");
194                                         } else {
195                                                 if (num < 1000000000) {
196                                                         res = ast_say_number(chan, num / 1000000, ints, language);
197                                                         if (res)
198                                                                 return res;
199                                                         num = num % 1000000;
200                                                         snprintf(fn, sizeof(fn), "digits/million");
201                                                 } else {
202                                                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
203                                                         res = -1;
204                                                 }
205                                         }
206                                 }
207                         }
208                         if (!res) {
209                                 res = ast_streamfile(chan, fn, language);
210                                 if (!res) 
211                                         res = ast_waitstream(chan, ints);
212                                 ast_stopstream(chan);
213                         }
214                         
215                 }
216         }
217         return res;
218 }
219 int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang)
220 {
221         struct tm tm;
222         char fn[256];
223         int res = 0;
224         ast_localtime(&t,&tm,NULL);
225         if (!res) {
226                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
227                 res = ast_streamfile(chan, fn, lang);
228                 if (!res)
229                         res = ast_waitstream(chan, ints);
230         }
231         if (!res) {
232                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
233                 res = ast_streamfile(chan, fn, lang);
234                 if (!res)
235                         res = ast_waitstream(chan, ints);
236         }
237         if (!res)
238                 res = ast_say_number(chan, tm.tm_mday, ints, lang);
239
240         if (!res)
241                 res = ast_waitstream(chan, ints);
242         if (!res)
243                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang);
244         return res;
245 }
246
247 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang) 
248 {
249         int res;
250         if ((res = ast_streamfile(chan, file, lang)))
251                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
252         if (!res)
253                 res = ast_waitstream(chan, ints);
254         return res;
255 }
256
257 int ast_say_date_with_format(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
258 {
259         struct tm tm;
260         int res=0, offset, sndoffset;
261         char sndfile[256], nextmsg[256];
262
263         ast_localtime(&time,&tm,timezone);
264
265         for (offset=0 ; format[offset] != '\0' ; offset++) {
266                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
267                 switch (format[offset]) {
268                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
269                         case '\'':
270                                 /* Literal name of a sound file */
271                                 sndoffset=0;
272                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
273                                         sndfile[sndoffset] = format[offset];
274                                 sndfile[sndoffset] = '\0';
275                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
276                                 res = wait_file(chan,ints,nextmsg,lang);
277                                 break;
278                         case 'A':
279                         case 'a':
280                                 /* Sunday - Saturday */
281                                 snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "day-%d", tm.tm_wday);
282                                 res = wait_file(chan,ints,nextmsg,lang);
283                                 break;
284                         case 'B':
285                         case 'b':
286                         case 'h':
287                                 /* January - December */
288                                 snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "mon-%d", tm.tm_mon);
289                                 res = wait_file(chan,ints,nextmsg,lang);
290                                 break;
291                         case 'd':
292                         case 'e':
293                                 /* First - Thirtyfirst */
294                                 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
295                                         snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "h-%d", tm.tm_mday);
296                                         res = wait_file(chan,ints,nextmsg,lang);
297                                 } else if (tm.tm_mday == 31) {
298                                         /* "Thirty" and "first" */
299                                         res = wait_file(chan,ints,DIGITS_DIR "30",lang);
300                                         if (!res) {
301                                                 res = wait_file(chan,ints,DIGITS_DIR "h-1",lang);
302                                         }
303                                 } else {
304                                         /* Between 21 and 29 - two sounds */
305                                         res = wait_file(chan,ints,DIGITS_DIR "20",lang);
306                                         if (!res) {
307                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "h-%d", tm.tm_mday - 20);
308                                                 res = wait_file(chan,ints,nextmsg,lang);
309                                         }
310                                 }
311                                 break;
312                         case 'Y':
313                                 /* Year */
314                                 if (tm.tm_year > 99) {
315                                         res = wait_file(chan,ints,DIGITS_DIR "2",lang);
316                                         if (!res) {
317                                                 res = wait_file(chan,ints,DIGITS_DIR "thousand",lang);
318                                         }
319                                         if (tm.tm_year > 100) {
320                                                 if (!res) {
321                                                         /* This works until the end of 2020 */
322                                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_year - 100);
323                                                         res = wait_file(chan,ints,nextmsg,lang);
324                                                 }
325                                         }
326                                 } else {
327                                         if (tm.tm_year < 1) {
328                                                 /* I'm not going to handle 1900 and prior */
329                                                 /* We'll just be silent on the year, instead of bombing out. */
330                                         } else {
331                                                 res = wait_file(chan,ints,DIGITS_DIR "19",lang);
332                                                 if (!res) {
333                                                         if (tm.tm_year <= 9) {
334                                                                 /* 1901 - 1909 */
335                                                                 res = wait_file(chan,ints,DIGITS_DIR "oh",lang);
336                                                                 if (!res) {
337                                                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_year);
338                                                                         res = wait_file(chan,ints,nextmsg,lang);
339                                                                 }
340                                                         } else if (tm.tm_year <= 20) {
341                                                                 /* 1910 - 1920 */
342                                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_year);
343                                                                 res = wait_file(chan,ints,nextmsg,lang);
344                                                         } else {
345                                                                 /* 1921 - 1999 */
346                                                                 int ten, one;
347                                                                 ten = tm.tm_year / 10;
348                                                                 one = tm.tm_year % 10;
349                                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", ten * 10);
350                                                                 res = wait_file(chan,ints,nextmsg,lang);
351                                                                 if (!res) {
352                                                                         if (one != 0) {
353                                                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", one);
354                                                                                 res = wait_file(chan,ints,nextmsg,lang);
355                                                                         }
356                                                                 }
357                                                         }
358                                                 }
359                                         }
360                                 }
361                                 break;
362                         case 'I':
363                         case 'l':
364                                 /* 12-Hour */
365                                 if (tm.tm_hour == 0)
366                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "12");
367                                 else if (tm.tm_hour > 12)
368                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_hour - 12);
369                                 else
370                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_hour);
371                                 res = wait_file(chan,ints,nextmsg,lang);
372                                 break;
373                         case 'H':
374                         case 'k':
375                                 /* 24-Hour */
376                                 if (format[offset] == 'H') {
377                                         /* e.g. oh-eight */
378                                         if (tm.tm_hour < 10) {
379                                                 res = wait_file(chan,ints,DIGITS_DIR "oh",lang);
380                                         }
381                                 } else {
382                                         /* e.g. eight */
383                                         if (tm.tm_hour == 0) {
384                                                 res = wait_file(chan,ints,DIGITS_DIR "oh",lang);
385                                         }
386                                 }
387                                 if (!res) {
388                                         if (tm.tm_hour != 0) {
389                                                 int remainder = tm.tm_hour;
390                                                 if (tm.tm_hour > 20) {
391                                                         res = wait_file(chan,ints,AST_SOUNDS "/digits/20",lang);
392                                                         remainder -= 20;
393                                                 }
394                                                 if (!res) {
395                                                         snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/digits/%d", remainder);
396                                                         res = wait_file(chan,ints,nextmsg,lang);
397                                                 }
398                                         }
399                                 }
400                                 break;
401                         case 'M':
402                                 /* Minute */
403                                 if (tm.tm_min == 0) {
404                                         res = wait_file(chan,ints,DIGITS_DIR "oclock",lang);
405                                 } else if (tm.tm_min < 10) {
406                                         res = wait_file(chan,ints,DIGITS_DIR "oh",lang);
407                                         if (!res) {
408                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_min);
409                                                 res = wait_file(chan,ints,nextmsg,lang);
410                                         }
411                                 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
412                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_min);
413                                         res = wait_file(chan,ints,nextmsg,lang);
414                                 } else {
415                                         int ten, one;
416                                         ten = (tm.tm_min / 10) * 10;
417                                         one = (tm.tm_min % 10);
418                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", ten);
419                                         res = wait_file(chan,ints,nextmsg,lang);
420                                         if (!res) {
421                                                 /* Fifty, not fifty-zero */
422                                                 if (one != 0) {
423                                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", one);
424                                                         res = wait_file(chan,ints,nextmsg,lang);
425                                                 }
426                                         }
427                                 }
428                                 break;
429                         case 'P':
430                         case 'p':
431                                 /* AM/PM */
432                                 if (tm.tm_hour > 11)
433                                         snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "p-m");
434                                 else
435                                         snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "a-m");
436                                 res = wait_file(chan,ints,nextmsg,lang);
437                                 break;
438                         case 'Q':
439                                 /* Shorthand for "Today", "Yesterday", or ABdY */
440                                 {
441                                         struct timeval now;
442                                         struct tm tmnow;
443                                         time_t beg_today;
444
445                                         gettimeofday(&now,NULL);
446                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
447                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
448                                         /* In any case, it saves not having to do ast_mktime() */
449                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
450                                         if (beg_today < time) {
451                                                 /* Today */
452                                                 res = wait_file(chan,ints,DIGITS_DIR "today",lang);
453                                         } else if (beg_today - 86400 < time) {
454                                                 /* Yesterday */
455                                                 res = wait_file(chan,ints,DIGITS_DIR "yesterday",lang);
456                                         } else {
457                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
458                                         }
459                                 }
460                                 break;
461                         case 'q':
462                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
463                                 {
464                                         struct timeval now;
465                                         struct tm tmnow;
466                                         time_t beg_today;
467
468                                         gettimeofday(&now,NULL);
469                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
470                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
471                                         /* In any case, it saves not having to do ast_mktime() */
472                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
473                                         if (beg_today < time) {
474                                                 /* Today */
475                                         } else if ((beg_today - 86400) < time) {
476                                                 /* Yesterday */
477                                                 res = wait_file(chan,ints,DIGITS_DIR "yesterday",lang);
478                                         } else if (beg_today - 86400 * 6 < time) {
479                                                 /* Within the last week */
480                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
481                                         } else {
482                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
483                                         }
484                                 }
485                                 break;
486                         case 'R':
487                                 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
488                                 break;
489                         case 'S':
490                                 /* Seconds */
491                                 if (tm.tm_sec == 0) {
492                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_sec);
493                                         res = wait_file(chan,ints,nextmsg,lang);
494                                 } else if (tm.tm_sec < 10) {
495                                         res = wait_file(chan,ints,DIGITS_DIR "oh",lang);
496                                         if (!res) {
497                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_sec);
498                                                 res = wait_file(chan,ints,nextmsg,lang);
499                                         }
500                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
501                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_sec);
502                                         res = wait_file(chan,ints,nextmsg,lang);
503                                 } else {
504                                         int ten, one;
505                                         ten = (tm.tm_sec / 10) * 10;
506                                         one = (tm.tm_sec % 10);
507                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", ten);
508                                         res = wait_file(chan,ints,nextmsg,lang);
509                                         if (!res) {
510                                                 /* Fifty, not fifty-zero */
511                                                 if (one != 0) {
512                                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", one);
513                                                         res = wait_file(chan,ints,nextmsg,lang);
514                                                 }
515                                         }
516                                 }
517                                 break;
518                         case 'T':
519                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
520                                 break;
521                         case ' ':
522                         case '  ':
523                                 /* Just ignore spaces and tabs */
524                                 break;
525                         default:
526                                 /* Unknown character */
527                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
528                 }
529                 /* Jump out on DTMF */
530                 if (res) {
531                         break;
532                 }
533         }
534         return res;
535 }
536
537 int ast_say_time(struct ast_channel *chan, time_t t, char *ints, char *lang)
538 {
539         struct tm tm;
540         int res = 0;
541         int hour, pm=0;
542         localtime_r(&t,&tm);
543         hour = tm.tm_hour;
544         if (!hour)
545                 hour = 12;
546         else if (hour == 12)
547                 pm = 1;
548         else if (hour > 12) {
549                 hour -= 12;
550                 pm = 1;
551         }
552         if (!res)
553                 res = ast_say_number(chan, hour, ints, lang);
554
555         if (tm.tm_min > 9) {
556                 if (!res)
557                         res = ast_say_number(chan, tm.tm_min, ints, lang);
558         } else if (tm.tm_min) {
559                 if (!res)
560                         res = ast_streamfile(chan, "digits/oh", lang);
561                 if (!res)
562                         res = ast_waitstream(chan, ints);
563                 if (!res)
564                         res = ast_say_number(chan, tm.tm_min, ints, lang);
565         } else {
566                 if (!res)
567                         res = ast_streamfile(chan, "digits/oclock", lang);
568                 if (!res)
569                         res = ast_waitstream(chan, ints);
570         }
571         if (pm) {
572                 if (!res)
573                         res = ast_streamfile(chan, "digits/p-m", lang);
574         } else {
575                 if (!res)
576                         res = ast_streamfile(chan, "digits/a-m", lang);
577         }
578         if (!res)
579                 res = ast_waitstream(chan, ints);
580         return res;
581 }
582
583 int ast_say_datetime(struct ast_channel *chan, time_t t, char *ints, char *lang)
584 {
585         struct tm tm;
586         char fn[256];
587         int res = 0;
588         int hour, pm=0;
589         localtime_r(&t,&tm);
590         if (!res) {
591                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
592                 res = ast_streamfile(chan, fn, lang);
593                 if (!res)
594                         res = ast_waitstream(chan, ints);
595         }
596         if (!res) {
597                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
598                 res = ast_streamfile(chan, fn, lang);
599                 if (!res)
600                         res = ast_waitstream(chan, ints);
601         }
602         if (!res)
603                 res = ast_say_number(chan, tm.tm_mday, ints, lang);
604
605         hour = tm.tm_hour;
606         if (!hour)
607                 hour = 12;
608         else if (hour == 12)
609                 pm = 1;
610         else if (hour > 12) {
611                 hour -= 12;
612                 pm = 1;
613         }
614         if (!res)
615                 res = ast_say_number(chan, hour, ints, lang);
616
617         if (tm.tm_min > 9) {
618                 if (!res)
619                         res = ast_say_number(chan, tm.tm_min, ints, lang);
620         } else if (tm.tm_min) {
621                 if (!res)
622                         res = ast_streamfile(chan, "digits/oh", lang);
623                 if (!res)
624                         res = ast_waitstream(chan, ints);
625                 if (!res)
626                         res = ast_say_number(chan, tm.tm_min, ints, lang);
627         } else {
628                 if (!res)
629                         res = ast_streamfile(chan, "digits/oclock", lang);
630                 if (!res)
631                         res = ast_waitstream(chan, ints);
632         }
633         if (pm) {
634                 if (!res)
635                         res = ast_streamfile(chan, "digits/p-m", lang);
636         } else {
637                 if (!res)
638                         res = ast_streamfile(chan, "digits/a-m", lang);
639         }
640         if (!res)
641                 res = ast_waitstream(chan, ints);
642         if (!res)
643                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang);
644         return res;
645 }
646
647 int ast_say_datetime_from_now(struct ast_channel *chan, time_t t, char *ints, char *lang)
648 {
649         int res=0;
650         time_t nowt;
651         int daydiff;
652         struct tm tm;
653         struct tm now;
654         char fn[256];
655
656         time(&nowt);
657
658         localtime_r(&t,&tm);
659         localtime_r(&nowt,&now);
660         daydiff = now.tm_yday - tm.tm_yday;
661         if ((daydiff < 0) || (daydiff > 6)) {
662                 /* Day of month and month */
663                 if (!res) {
664                         snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
665                         res = ast_streamfile(chan, fn, lang);
666                         if (!res)
667                                 res = ast_waitstream(chan, ints);
668                 }
669                 if (!res)
670                         res = ast_say_number(chan, tm.tm_mday, ints, lang);
671
672         } else if (daydiff) {
673                 /* Just what day of the week */
674                 if (!res) {
675                         snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
676                         res = ast_streamfile(chan, fn, lang);
677                         if (!res)
678                                 res = ast_waitstream(chan, ints);
679                 }
680         } /* Otherwise, it was today */
681         if (!res)
682                 res = ast_say_time(chan, t, ints, lang);
683         return res;
684 }
685