Internationalize say_date_time, fix small pbx_config seglet (bug #1537)
[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 <ctype.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.h"
27 #include <stdio.h>
28
29
30 /* Forward declaration */
31 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang);
32
33 int ast_say_digit_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
34 {
35         /* XXX Merge with full version? XXX */
36         char fn[256] = "";
37         int num = 0;
38         int res = 0;
39         while(fn2[num] && !res) {
40                 fn[0] = '\0';
41                 switch (fn2[num]) {
42                         case ('*'):
43                                 snprintf(fn, sizeof(fn), "digits/star");
44                                 break;
45                         case ('#'):
46                                 snprintf(fn, sizeof(fn), "digits/pound");
47                                 break;
48                         default:
49                                 if((fn2[num] >= '0') && (fn2[num] <= '9')){ /* Must be in {0-9} */
50                                         snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
51                                 }
52                 }
53                 if(strlen(fn)){ /* if length == 0, then skip this digit as it is invalid */
54                         res = ast_streamfile(chan, fn, lang);
55                         if (!res)
56                                 res = ast_waitstream(chan, ints);
57                         ast_stopstream(chan);
58                 }
59                 num++;
60         }
61         return res;
62 }
63
64 int ast_say_character_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
65 {
66         /* XXX Merge with full version? XXX */
67         char fn[256] = "";
68         char ltr;
69         int num = 0;
70         int res = 0;
71         while(fn2[num] && !res) {
72                 fn[0] = '\0';
73                 switch (fn2[num]) {
74                         case ('*'):
75                                 snprintf(fn, sizeof(fn), "digits/star");
76                                 break;
77                         case ('#'):
78                                 snprintf(fn, sizeof(fn), "digits/pound");
79                                 break;
80                         case ('0'):
81                         case ('1'):
82                         case ('2'):
83                         case ('3'):
84                         case ('4'):
85                         case ('5'):
86                         case ('6'):
87                         case ('7'):
88                         case ('8'):
89                         case ('9'):
90                                 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
91                                 break;
92                         case ('!'):
93                                 strncpy(fn, "letters/exclaimation-point", sizeof(fn));
94                                 break;          
95                         case ('@'):
96                                 strncpy(fn, "letters/at", sizeof(fn));
97                                 break;
98                         case ('$'):
99                                 strncpy(fn, "letters/dollar", sizeof(fn));
100                                 break;
101                         case ('-'):
102                                 strncpy(fn, "letters/dash", sizeof(fn));
103                                 break;
104                         case ('.'):
105                                 strncpy(fn, "letters/dot", sizeof(fn));
106                                 break;
107                         case ('='):
108                                 strncpy(fn, "letters/equals", sizeof(fn));
109                                 break;
110                         case ('+'):
111                                 strncpy(fn, "letters/plus", sizeof(fn));
112                                 break;
113                         case ('/'):
114                                 strncpy(fn, "letters/slash", sizeof(fn));
115                                 break;
116                         case (' '):
117                                 strncpy(fn, "letters/space", sizeof(fn));
118                                 break;
119                         default:
120                                 ltr = fn2[num];
121                                 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A';         /* file names are all lower-case */
122                                 snprintf(fn, sizeof(fn), "letters/%c", ltr);
123                 }
124                 if(strlen(fn)){ /* if length == 0, then skip this digit as it is invalid */
125                         res = ast_streamfile(chan, fn, lang);
126                         if (!res) 
127                                 res = ast_waitstream(chan, ints);
128                 }       ast_stopstream(chan);
129                 num++;
130         }
131         return res;
132 }
133
134 int ast_say_phonetic_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
135 {
136         /* XXX Merge with full version? XXX */
137         char fn[256] = "";
138         char ltr;
139         int num = 0;
140         int res = 0;
141         int temp;
142         int play;
143         char hex[3];
144 /*      while(fn2[num] && !res) { */
145         while(fn2[num]) {
146                 play=1;
147                 switch (fn2[num]) {
148                         case ('*'):
149                                 snprintf(fn, sizeof(fn), "digits/star");
150                                 break;
151                         case ('#'):
152                                 snprintf(fn, sizeof(fn), "digits/pound");
153                                 break;
154                         case ('0'):
155                         case ('1'):
156                         case ('2'):
157                         case ('3'):
158                         case ('4'):
159                         case ('5'):
160                         case ('6'):
161                         case ('7'):
162                         case ('8'):
163                                 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
164                                 break;
165                         case ('!'):
166                                 strncpy(fn, "exclaimation-point", sizeof(fn));
167                                 break;          
168                         case ('@'):
169                                 strncpy(fn, "at", sizeof(fn));
170                                 break;
171                         case ('$'):
172                                 strncpy(fn, "dollar", sizeof(fn));
173                                 break;  
174                         case ('-'):
175                                 strncpy(fn, "dash", sizeof(fn));
176                                 break;
177                         case ('.'):
178                                 strncpy(fn, "dot", sizeof(fn));
179                                 break;
180                         case ('='):
181                                 strncpy(fn, "equals", sizeof(fn));
182                                 break;
183                         case ('+'):
184                                 strncpy(fn, "plus", sizeof(fn));
185                                 break;
186                         case ('/'):
187                                 strncpy(fn, "slash", sizeof(fn));
188                                 break;
189                         case (' '):
190                                 strncpy(fn, "space", sizeof(fn));
191                                 break;
192                         case ('%'):
193                                 play=0;
194                                 /* check if we have 2 chars after the % */
195                                 if (strlen(fn2)>num+2)
196                                 {
197                                     hex[0]=fn2[num+1];
198                                     hex[1]=fn2[num+2];
199                                     hex[2]='\0';
200                                     if (sscanf(hex,"%x", &temp))
201                                     { /* Hex to char convertion successfull */
202                                         fn2[num+2]=temp;
203                                         num++;
204                                         if (temp==37)
205                                         { /* If it is a percent, play it now */
206                                             strncpy(fn, "percent", sizeof(fn));
207                                                 num++;
208                                                 play=1;
209                                                 }
210                                                 /* check for invalid characters */
211                                                 if ((temp<32) || (temp>126))
212                                                 {
213                                                     num++;
214                                                 }
215                                     }
216                                 }
217                                 else
218                                     num++;
219                                 break;
220                         default:        /* '9' falls through to here, too */
221                                 ltr = tolower(fn2[num]);
222                                 snprintf(fn, sizeof(fn), "phonetic/%c_p", ltr);
223                 }
224                 if (play)
225                 {
226                     res = ast_streamfile(chan, fn, lang);
227                     if (!res) 
228                         res = ast_waitstream(chan, ints);
229                     ast_stopstream(chan);
230                 }
231                 num++;
232         }
233         return res;
234 }
235
236 int ast_say_digit_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
237 {
238         char fn[256] = "";
239         int num = 0;
240         int res = 0;
241         while(fn2[num] && !res) {
242                 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
243                 res = ast_streamfile(chan, fn, lang);
244                 if (!res) 
245                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
246                 ast_stopstream(chan);
247                 num++;
248         }
249         return res;
250 }
251
252 int ast_say_character_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
253 {
254         char fn[256] = "";
255         char ltr;
256         int num = 0;
257         int res = 0;
258         while(fn2[num] && !res) {
259                 switch (fn2[num]) {
260                         case ('*'):
261                                 snprintf(fn, sizeof(fn), "digits/star");
262                                 break;
263                         case ('#'):
264                                 snprintf(fn, sizeof(fn), "digits/pound");
265                                 break;
266                         case ('0'):
267                         case ('1'):
268                         case ('2'):
269                         case ('3'):
270                         case ('4'):
271                         case ('5'):
272                         case ('6'):
273                         case ('7'):
274                         case ('8'):
275                         case ('9'):
276                                 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
277                                 break;
278                         case ('!'):
279                                 strncpy(fn, "exclaimation-point", sizeof(fn));
280                                 break;          
281                         case ('@'):
282                                 strncpy(fn, "at", sizeof(fn));
283                                 break;
284                         case ('$'):
285                                 strncpy(fn, "dollar", sizeof(fn));
286                                 break;
287                         case ('-'):
288                                 strncpy(fn, "dash", sizeof(fn));
289                                 break;
290                         case ('.'):
291                                 strncpy(fn, "dot", sizeof(fn));
292                                 break;
293                         case ('='):
294                                 strncpy(fn, "equals", sizeof(fn));
295                                 break;
296                         case ('+'):
297                                 strncpy(fn, "plus", sizeof(fn));
298                                 break;
299                         case ('/'):
300                                 strncpy(fn, "slash", sizeof(fn));
301                                 break;
302                         case (' '):
303                                 strncpy(fn, "space", sizeof(fn));
304                                 break;
305                         default:
306                                 ltr = fn2[num];
307                                 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A';         /* file names are all lower-case */
308                                 snprintf(fn, sizeof(fn), "letters/%c", ltr);
309                 }
310                 /* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
311                 res = ast_streamfile(chan, fn, lang);
312                 if (!res) 
313                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
314                 ast_stopstream(chan);
315                 num++;
316         }
317         return res;
318 }
319
320 int ast_say_phonetic_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
321 {
322         char fn[256] = "";
323         char ltr;
324         int num = 0;
325         int res = 0;
326         while(fn2[num] && !res) {
327                 switch (fn2[num]) {
328                         case ('*'):
329                                 snprintf(fn, sizeof(fn), "digits/star");
330                                 break;
331                         case ('#'):
332                                 snprintf(fn, sizeof(fn), "digits/pound");
333                                 break;
334                         case ('0'):
335                         case ('1'):
336                         case ('2'):
337                         case ('3'):
338                         case ('4'):
339                         case ('5'):
340                         case ('6'):
341                         case ('7'):
342                         case ('8'):
343                                 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
344                                 break;
345                         case ('!'):
346                                 strncpy(fn, "exclaimation-point", sizeof(fn));
347                                 break;          
348                         case ('@'):
349                                 strncpy(fn, "at", sizeof(fn));
350                                 break;
351                         case ('$'):
352                                 strncpy(fn, "dollar", sizeof(fn));
353                                 break;
354                         case ('-'):
355                                 strncpy(fn, "dash", sizeof(fn));
356                                 break;
357                         case ('.'):
358                                 strncpy(fn, "dot", sizeof(fn));
359                                 break;
360                         case ('='):
361                                 strncpy(fn, "equals", sizeof(fn));
362                                 break;
363                         case ('+'):
364                                 strncpy(fn, "plus", sizeof(fn));
365                                 break;
366                         case ('/'):
367                                 strncpy(fn, "slash", sizeof(fn));
368                                 break;
369                         case (' '):
370                                 strncpy(fn, "space", sizeof(fn));
371                                 break;
372                         default:        /* '9' falls here... */
373                                 ltr = fn2[num];
374                                 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A';         /* file names are all lower-case */
375                                 snprintf(fn, sizeof(fn), "phonetic/%c", ltr);
376                         }
377                 /* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
378                 res = ast_streamfile(chan, fn, lang);
379                 if (!res) 
380                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
381                 ast_stopstream(chan);
382                 num++;
383         }
384         return res;
385 }
386
387 int ast_say_digits(struct ast_channel *chan, int num, char *ints, char *lang)
388 {
389         /* XXX Should I be merged with say_digits_full XXX */
390         char fn2[256];
391         snprintf(fn2, sizeof(fn2), "%d", num);
392         return ast_say_digit_str(chan, fn2, ints, lang);
393 }
394
395 int ast_say_digits_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd)
396 {
397         char fn2[256];
398         snprintf(fn2, sizeof(fn2), "%d", num);
399         return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd);
400 }
401
402 /* Forward declarations */
403 /* Syntaxes supported, not really language codes.
404       da - Danish
405       de - German
406       en - English, Swedish, Norwegian
407       es - Spanish
408       fr - French
409       it - Italian
410       nl - Dutch
411       pt - Portuguese
412
413  Gender:
414  For Portuguese, we're using m & f options to saynumber() to indicate if the gender is masculine or feminine.
415  For Danish, we're using c & n options to saynumber() to indicate if the gender is commune or neutrum.
416  This still needs to be implemented for French, Spanish & German.
417  
418  Date/Time functions currently have less languages supported than saynumber().
419
420  Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
421
422  See contrib/i18n.testsuite.conf for some examples of the different syntaxes
423
424  Portuguese sound files needed for Time/Date functions:
425  pt-ah
426  pt-ao
427  pt-de
428  pt-e
429  pt-ora
430  pt-meianoite
431  pt-meiodia
432  pt-sss
433
434 */
435
436 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
437 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
438 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
439 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
440 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
441 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
442 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
443 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
444 static int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
445 static int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
446 static int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
447 static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
448 static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
449 static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
450 static int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
451 static int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
452 static int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
453 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
454 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
455 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
456 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
457 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
458
459 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang) 
460 {
461         int res;
462         if ((res = ast_streamfile(chan, file, lang)))
463                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
464         if (!res)
465                 res = ast_waitstream(chan, ints);
466         return res;
467 }
468
469 /*--- ast_say_number_full: call language-specific functions */
470 /* Called from AGI */
471 int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
472 {
473         char *options=(char *) NULL;    /* While waiting for a general hack for agi */
474
475         if (!strcasecmp(language, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) {
476            return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
477         } else if (!strcasecmp(language, "fr") ) {      /* French syntax */
478            return(ast_say_number_full_fr(chan, num, ints, language, audiofd, ctrlfd));
479         } else if (!strcasecmp(language, "de") ) {      /* German syntax */
480            return(ast_say_number_full_de(chan, num, ints, language, audiofd, ctrlfd));
481         } else if (!strcasecmp(language, "da") ) {      /* Danish syntax */
482            return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
483         } else if (!strcasecmp(language, "it") ) {      /* Italian syntax */
484            return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
485         } else if (!strcasecmp(language, "pt") ) {      /* Portuguese syntax */
486            return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
487         } else if (!strcasecmp(language, "es") ) {      /* Spanish syntax */
488            return(ast_say_number_full_es(chan, num, ints, language, audiofd, ctrlfd));
489         } else if (!strcasecmp(language, "nl") ) {      /* Dutch syntax */
490            return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
491         }
492
493         /* Default to english */
494         return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
495 }
496
497 /*--- ast_say_number: call language-specific functions without file descriptors */
498 int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language, char *options)
499 {
500         if (!strcasecmp(language, "no") || !strcasecmp(language,"se") || !strcasecmp(language,"en") ) {
501            return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
502         }
503         /* French */
504         if (!strcasecmp(language, "fr")) {              /* French syntax */
505            return(ast_say_number_full_fr(chan, num, ints, language, -1, -1));
506         } else if (!strcasecmp(language, "da")) {       /* Danish syntax */
507            return(ast_say_number_full_da(chan, num, ints, language, options, -1, -1));
508         } else if (!strcasecmp(language, "de")) {       /* German syntax */
509            return(ast_say_number_full_de(chan, num, ints, language, -1, -1));
510         } else if (!strcasecmp(language, "it")) {       /* Italian syntax */
511            return(ast_say_number_full_it(chan, num, ints, language, -1, -1));
512         } else if (!strcasecmp(language, "pt")) {       /* Portuguese syntax */
513            return(ast_say_number_full_pt(chan, num, ints, language, options, -1, -1));
514         } else if (!strcasecmp(language, "nl")) {       /* Dutch syntax */
515            return(ast_say_number_full_nl(chan, num, ints, language, -1, -1));
516         } else if (!strcasecmp(language, "es")) {       /* Spanish syntax */
517            return(ast_say_number_full_es(chan, num, ints, language, -1, -1));
518         }
519
520         /* Default to english */
521         return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
522 }
523
524 /*--- ast_say_number_full_en: English syntax */
525 /* This is the default syntax, if no other syntax defined in this file is used */
526 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
527 {
528         int res = 0;
529         int playh = 0;
530         char fn[256] = "";
531         if (!num) 
532                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
533
534         while(!res && (num || playh)) {
535                         if (playh) {
536                                 snprintf(fn, sizeof(fn), "digits/hundred");
537                                 playh = 0;
538                         } else  if (num < 20) {
539                                 snprintf(fn, sizeof(fn), "digits/%d", num);
540                                 num = 0;
541                         } else  if (num < 100) {
542                                 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
543                                 num -= ((num / 10) * 10);
544                         } else {
545                                 if (num < 1000){
546                                         snprintf(fn, sizeof(fn), "digits/%d", (num/100));
547                                         playh++;
548                                         num -= ((num / 100) * 100);
549                                 } else {
550                                         if (num < 1000000) { /* 1,000,000 */
551                                                 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
552                                                 if (res)
553                                                         return res;
554                                                 num = num % 1000;
555                                                 snprintf(fn, sizeof(fn), "digits/thousand");
556                                         } else {
557                                                 if (num < 1000000000) { /* 1,000,000,000 */
558                                                         res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
559                                                         if (res)
560                                                                 return res;
561                                                         num = num % 1000000;
562                                                         snprintf(fn, sizeof(fn), "digits/million");
563                                                 } else {
564                                                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
565                                                         res = -1;
566                                                 }
567                                         }
568                                 }
569                         }
570                         if (!res) {
571                                 if(!ast_streamfile(chan, fn, language)) {
572                                         if (audiofd && ctrlfd)
573                                                 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
574                                         else
575                                                 res = ast_waitstream(chan, ints);
576                                 }
577                                 ast_stopstream(chan);
578
579                         }
580                         
581         }
582         return res;
583 }
584
585 /*--- ast_say_number_full_da: Danish syntax */
586 /* New files:
587  In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and" 
588  */
589 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
590 {
591         int res = 0;
592         int playh = 0;
593         int playa = 0;
594         int cn = 1;             /* +1 = Commune; -1 = Neutrum */
595         char fn[256] = "";
596         if (!num) 
597                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
598
599         if (options && !strncasecmp(options, "n",1)) cn = -1;
600
601         while(!res && (num || playh || playa )) {
602                 /* The grammar for Danish numbers is the same as for English except
603                 * for the following:
604                 * - 1 exists in both commune ("en", file "1N") and neutrum ("et", file "1")
605                 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
606                 *   "one-and twenty" and 68 is "eight-and sixty".
607                 * - "million" is different in singular and plural form
608                 * - numbers > 1000 with zero as the third digit from last have an
609                 *   "and" before the last two digits, i.e. 2034 is "two thousand and
610                 *   four-and thirty" and 1000012 is "one million and twelve".
611                 */
612                 if (playh) {
613                         snprintf(fn, sizeof(fn), "digits/hundred");
614                         playh = 0;
615                 } else if (playa) {
616                         snprintf(fn, sizeof(fn), "digits/and");
617                         playa = 0;
618                 } else if (num == 1 && cn == -1) {
619                         snprintf(fn, sizeof(fn), "digits/1N");
620                         num = 0;
621                 } else if (num < 20) {
622                         snprintf(fn, sizeof(fn), "digits/%d", num);
623                         num = 0;
624                 } else if (num < 100) {
625                         int ones = num % 10;
626                         if (ones) {
627                                 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
628                                 num -= ones;
629                         } else {
630                                 snprintf(fn, sizeof(fn), "digits/%d", num);
631                                 num = 0;
632                         }
633                 } else {
634                         if (num < 1000) {
635                                 int hundreds = num / 100;
636                                 if (hundreds == 1)
637                                         snprintf(fn, sizeof(fn), "digits/1N");
638                                 else
639                                         snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
640
641                                 playh++;
642                                 num -= 100 * hundreds;
643                                 if (num)
644                                         playa++;
645
646                         } else {
647                                 if (num < 1000000) {
648                                         res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
649                                         if (res)
650                                                 return res;
651                                         num = num % 1000;
652                                         snprintf(fn, sizeof(fn), "digits/thousand");
653                                 } else {
654                                         if (num < 1000000000) {
655                                                 int millions = num / 1000000;
656                                                 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
657                                                 if (res)
658                                                         return res;
659                                                 if (millions == 1)
660                                                         snprintf(fn, sizeof(fn), "digits/million");
661                                                 else
662                                                         snprintf(fn, sizeof(fn), "digits/millions");
663                                                 num = num % 1000000;
664                                         } else {
665                                                 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
666                                                 res = -1;
667                                         }
668                                 }
669                                 if (num && num < 100)
670                                         playa++;
671                         }
672                 }
673                 if (!res) {
674                         if(!ast_streamfile(chan, fn, language)) {
675                                 if (audiofd && ctrlfd) 
676                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
677                                 else  
678                                         res = ast_waitstream(chan, ints);
679                         }
680                         ast_stopstream(chan);
681                 }
682         }
683         return res;
684 }
685
686 /*--- ast_say_number_full_de: German syntax */
687 /* New files:
688  In addition to English, the following sounds are required: "millions", "and" and "1-and" through "9-and" 
689  */
690 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
691 {
692         int res = 0;
693         int playh = 0;
694         int playa = 0;
695         char fn[256] = "";
696         if (!num) 
697                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
698
699         while(!res && (num || playh || playa )) {
700                 /* The grammar for German numbers is the same as for English except
701                 * for the following:
702                 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
703                 *   "one-and twenty" and 68 is "eight-and sixty".
704                 * - "million" is different in singular and plural form
705                 * - numbers > 1000 with zero as the third digit from last have an
706                 *   "and" before the last two digits, i.e. 2034 is "two thousand and
707                 *   four-and thirty" and 1000012 is "one million and twelve".
708                 */
709                 if (playh) {
710                         snprintf(fn, sizeof(fn), "digits/hundred");
711                         playh = 0;
712                 } else if (playa) {
713                         snprintf(fn, sizeof(fn), "digits/and");
714                         playa = 0;
715                 } else if (num < 20) {
716                         snprintf(fn, sizeof(fn), "digits/%d", num);
717                         num = 0;
718                 } else if (num < 100) {
719                         int ones = num % 10;
720                         if (ones) {
721                                 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
722                                 num -= ones;
723                         } else {
724                                 snprintf(fn, sizeof(fn), "digits/%d", num);
725                                 num = 0;
726                         }
727                 } else {
728                         if (num < 1000) {
729                                 int hundreds = num / 100;
730                                 if (hundreds == 1)
731                                         snprintf(fn, sizeof(fn), "digits/1N");
732                                 else
733                                         snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
734                                 playh++;
735                                 num -= 100 * hundreds;
736                                 if (num)
737                                         playa++;
738                         } else {
739                                 if (num < 1000000) {
740                                         res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
741                                         if (res)
742                                                 return res;
743                                         num = num % 1000;
744                                         snprintf(fn, sizeof(fn), "digits/thousand");
745                                 } else {
746                                         if (num < 1000000000) {
747                                                 int millions = num / 1000000;
748                                                 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
749                                                 if (res)
750                                                         return res;
751                                                 if (millions == 1)
752                                                         snprintf(fn, sizeof(fn), "digits/million");
753                                                 else
754                                                         snprintf(fn, sizeof(fn), "digits/millions");
755                                                 num = num % 1000000;
756                                         } else {
757                                                 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
758                                                 res = -1;
759                                         }
760                                 }
761                                 if (num && num < 100)
762                                         playa++;
763                         }
764                 }
765                 if (!res) {
766                         if(!ast_streamfile(chan, fn, language)) {
767                                 if (audiofd && ctrlfd) 
768                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
769                                 else  
770                                         res = ast_waitstream(chan, ints);
771                         }
772                         ast_stopstream(chan);
773                 }
774         }
775         return res;
776 }
777
778 /*--- ast_say_number_full_es: spanish syntax */
779 /* New files:
780  Requires a few new audios:
781    21.gsm thru 29.gsm, cien.gsm, mil.gsm, millon.gsm, millones.gsm, 100.gsm, 200.gsm, 300.gsm, 400.gsm, 500.gsm, 600.gsm, 700.gsm, 800.gsm, 900.gsm, y.gsm 
782  */
783 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
784 {
785         int res = 0;
786         int playa = 0;
787         char fn[256] = "";
788         if (!num) 
789                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
790         while (!res && num) {
791                 if (playa) {
792                         snprintf(fn, sizeof(fn), "digits/y");
793                         playa = 0;
794                 } else if (num < 31) {
795                         snprintf(fn, sizeof(fn), "digits/%d", num);
796                         num = 0;
797                 } else if (num < 100) {
798                         snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
799                         num -= ((num/10)*10);
800                         if (num)
801                                 playa++;
802                 } else if (num == 100) {
803                         snprintf(fn, sizeof(fn), "digits/cien");
804                         num = 0;
805                 } else {
806                         if (num < 1000) {
807                                 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
808                                 num -= ((num/100)*100);
809                         } else {
810                                 if (num < 1000000) {
811                                         res = ast_say_number_full_es(chan, num / 1000, ints, language, audiofd, ctrlfd);
812                                         if (res)
813                                                 return res;
814                                         num = num % 1000;
815                                         snprintf(fn, sizeof(fn), "digits/mil");
816                                 } else {
817                                         if (num < 2147483640) {
818                                                 res = ast_say_number_full_es(chan, num / 1000000, ints, language, audiofd, ctrlfd);
819                                                 if (res)
820                                                         return res;
821                                                 if ((num/1000000) == 1) {
822                                                         snprintf(fn, sizeof(fn), "digits/millon");
823                                                 } else {
824                                                         snprintf(fn, sizeof(fn), "digits/millones");
825                                                 }
826                                                 num = num % 1000000;
827                                         } else {
828                                                 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
829                                                 res = -1;
830                                         }
831                                 }
832                         }
833                 }
834
835                 if (!res) {
836                         if(!ast_streamfile(chan, fn, language)) {
837                                 if (audiofd && ctrlfd)
838                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
839                                 else
840                                         res = ast_waitstream(chan, ints);
841                         }
842                         ast_stopstream(chan);
843
844                 }
845                         
846         }
847         return res;
848 }
849
850
851 /*--- ast_say_number_full_fr: French syntax */
852 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
853 {
854         int res = 0;
855         int playh = 0;
856         int playa = 0;
857         char fn[256] = "";
858         if (!num) 
859                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
860         while(!res && (num || playh || playa)) {
861                 if (playh) {
862                         snprintf(fn, sizeof(fn), "digits/hundred");
863                         playh = 0;
864                 } else if (playa) {
865                         snprintf(fn, sizeof(fn), "digits/et");
866                         playa = 0;
867                 } else if (num < 21) {
868                         snprintf(fn, sizeof(fn), "digits/%d", num);
869                         num = 0;
870                 } else if (num < 70) {
871                         snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
872                         if ((num % 10) == 1) playa++;
873                         num = num % 10;
874                 } else if (num < 80) {
875                         snprintf(fn, sizeof(fn), "digits/60");
876                         if ((num % 10) == 1) playa++;
877                         num = num - 60;
878                 } else if (num < 100) {
879                         snprintf(fn, sizeof(fn), "digits/80");
880                         num = num - 80;
881                 } else if (num < 200) {
882                         snprintf(fn, sizeof(fn), "digits/hundred");
883                         num = num - 100;
884                 } else if (num < 1000) {
885                         snprintf(fn, sizeof(fn), "digits/%d", (num/100));
886                         playh++;
887                         num = num % 100;
888                 } else if (num < 2000) {
889                         snprintf(fn, sizeof(fn), "digits/thousand");
890                         num = num - 1000;
891                 } else if (num < 1000000) {
892                         res = ast_say_number_full_fr(chan, num / 1000, ints, language, audiofd, ctrlfd);
893                         if (res)
894                                 return res;
895                         snprintf(fn, sizeof(fn), "digits/thousand");
896                         num = num % 1000;
897                 } else  if (num < 1000000000) {
898                         res = ast_say_number_full_fr(chan, num / 1000000, ints, language, audiofd, ctrlfd);
899                         if (res)
900                                 return res;
901                         snprintf(fn, sizeof(fn), "digits/million");
902                         num = num % 1000000;
903                 } else {
904                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
905                         res = -1;
906                 }
907                 if (!res) {
908                         if(!ast_streamfile(chan, fn, language)) {
909                                 if (audiofd && ctrlfd)
910                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
911                                 else
912                                         res = ast_waitstream(chan, ints);
913                         }
914                         ast_stopstream(chan);
915                 }
916         }
917         return res;
918 }
919
920 /*--- ast_say_number_full_it:  Italian */
921 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
922 {
923         int res = 0;
924         int playh = 0;
925         int tempnum = 0;
926         char fn[256] = "";
927
928         if (!num)
929                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
930
931                 /*
932                 Italian support
933
934                 Like english, numbers up to 20 are a single 'word', and others
935                 compound, but with exceptions.
936                 For example 21 is not twenty-one, but there is a single word in 'it'.
937                 Idem for 28 (ie when a the 2nd part of a compund number
938                 starts with a vowel)
939
940                 There are exceptions also for hundred, thousand and million.
941                 In english 100 = one hundred, 200 is two hundred.
942                 In italian 100 = cento , like to say hundred (without one),
943                 200 and more are like english.
944                 
945                 Same applies for thousand:
946                 1000 is one thousand in en, 2000 is two thousand.
947                 In it we have 1000 = mille , 2000 = 2 mila 
948
949                 For million(s) we use the plural, if more than one
950                 Also, one million is abbreviated in it, like on-million,
951                 or 'un milione', not 'uno milione'.
952                 So the right file is provided.
953                 */
954
955                 while(!res && (num || playh)) {
956                         if (playh) {
957                                 snprintf(fn, sizeof(fn), "digits/hundred");
958                                 playh = 0;
959                         } else if (num < 20) {
960                                 snprintf(fn, sizeof(fn), "digits/%d", num);
961                                 num = 0;
962                         } else if (num == 21) {
963                                 snprintf(fn, sizeof(fn), "digits/%d", num);
964                                 num = 0;
965                         } else if (num == 28) {
966                                 snprintf(fn, sizeof(fn), "digits/%d", num);
967                                 num = 0;
968                         } else if (num == 31) {
969                                 snprintf(fn, sizeof(fn), "digits/%d", num);
970                                 num = 0;
971                         } else if (num == 38) {
972                                 snprintf(fn, sizeof(fn), "digits/%d", num);
973                                 num = 0;
974                         } else if (num == 41) {
975                                 snprintf(fn, sizeof(fn), "digits/%d", num);
976                                 num = 0;
977                         } else if (num == 48) {
978                                 snprintf(fn, sizeof(fn), "digits/%d", num);
979                                 num = 0;
980                         } else if (num == 51) {
981                                 snprintf(fn, sizeof(fn), "digits/%d", num);
982                                 num = 0;
983                         } else if (num == 58) {
984                                 snprintf(fn, sizeof(fn), "digits/%d", num);
985                                 num = 0;
986                         } else if (num == 61) {
987                                 snprintf(fn, sizeof(fn), "digits/%d", num);
988                                 num = 0;
989                         } else if (num == 68) {
990                                 snprintf(fn, sizeof(fn), "digits/%d", num);
991                                 num = 0;
992                         } else if (num == 71) {
993                                 snprintf(fn, sizeof(fn), "digits/%d", num);
994                                 num = 0;
995                         } else if (num == 78) {
996                                 snprintf(fn, sizeof(fn), "digits/%d", num);
997                                 num = 0;
998                         } else if (num == 81) {
999                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1000                                 num = 0;
1001                         } else if (num == 88) {
1002                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1003                                 num = 0;
1004                         } else if (num == 91) {
1005                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1006                                 num = 0;
1007                         } else if (num == 98) {
1008                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1009                                 num = 0;
1010                         } else if (num < 100) {
1011                                 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1012                                 num -= ((num / 10) * 10);
1013                         } else {
1014                                 if (num < 1000) {
1015                                         if ((num / 100) > 1) {
1016                                                 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1017                                                 playh++;
1018                                         } else {
1019                                                 snprintf(fn, sizeof(fn), "digits/hundred");
1020                                         }
1021                                         num -= ((num / 100) * 100);
1022                                 } else {
1023                                         if (num < 1000000) { /* 1,000,000 */
1024                                                 if ((num/1000) > 1)
1025                                                         res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd);
1026                                                 if (res)
1027                                                         return res;
1028                                                 tempnum = num;
1029                                                 num = num % 1000;
1030                                                 if ((tempnum / 1000) < 2)
1031                                                         snprintf(fn, sizeof(fn), "digits/thousand");
1032                                                 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1033                                                         snprintf(fn, sizeof(fn), "digits/thousands");
1034                                         } else {
1035                                                 if (num < 1000000000) { /* 1,000,000,000 */
1036                                                         if ((num / 1000000) > 1)
1037                                                                 res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1038                                                         if (res)
1039                                                                 return res;
1040                                                         tempnum = num;
1041                                                         num = num % 1000000;
1042                                                         if ((tempnum / 1000000) < 2)
1043                                                                 snprintf(fn, sizeof(fn), "digits/million");
1044                                                         else
1045                                                                 snprintf(fn, sizeof(fn), "digits/millions");
1046                                                 } else {
1047                                                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1048                                                         res = -1;
1049                                                 }
1050                                         }
1051                                 }
1052                         }
1053                         if (!res) {
1054                                 if(!ast_streamfile(chan, fn, language)) {
1055                                         if (audiofd && ctrlfd)
1056                                                 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1057                                         else
1058                                                 res = ast_waitstream(chan, ints);
1059                                 }
1060                                 ast_stopstream(chan);
1061                         }
1062                 }
1063         return res;
1064 }
1065
1066 /*--- ast_say_number_full_nl: dutch syntax */
1067 /* New files: digits/nl-en
1068  */
1069 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
1070 {
1071         int res = 0;
1072         int playh = 0;
1073         int units = 0;
1074         char fn[256] = "";
1075         if (!num) 
1076                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1077         while (!res && (num || playh )) {
1078                 if (playh) {
1079                         snprintf(fn, sizeof(fn), "digits/hundred");
1080                         playh = 0;
1081                 } else if (num < 20) {
1082                         snprintf(fn, sizeof(fn), "digits/%d", num);
1083                         num = 0;
1084                 } else if (num < 100) {
1085                         units = num % 10;
1086                         if (units > 0) {
1087                                 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1088                                 if (res)
1089                                         return res;
1090                                 num = num - units;
1091                                 snprintf(fn, sizeof(fn), "digits/nl-en");
1092                         } else {
1093                                 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1094                                 num = 0;
1095                         }
1096                 } else {
1097                         if (num < 1000) {
1098                                 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1099                                 playh++;
1100                                 num -= ((num / 100) * 100);
1101                         } else {
1102                                 if (num < 1000000) { /* 1,000,000 */
1103                                         res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1104                                         if (res)
1105                                                 return res;
1106                                         num = num % 1000;
1107                                         snprintf(fn, sizeof(fn), "digits/thousand");
1108                                 } else {
1109                                         if (num < 1000000000) { /* 1,000,000,000 */
1110                                                 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1111                                                 if (res)
1112                                                         return res;
1113                                                 num = num % 1000000;
1114                                                 snprintf(fn, sizeof(fn), "digits/million");
1115                                         } else {
1116                                                 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1117                                                 res = -1;
1118                                         }
1119                                 }
1120                         }
1121                 }
1122
1123                 if (!res) {
1124                         if(!ast_streamfile(chan, fn, language)) {
1125                                 if (audiofd && ctrlfd)
1126                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1127                                 else
1128                                         res = ast_waitstream(chan, ints);
1129                         }
1130                         ast_stopstream(chan);
1131                 }
1132         }
1133         return res;
1134 }
1135
1136 /* ast_say_number_full_pt: Portuguese syntax */
1137 /*      Extra sounds needed: */
1138 /*      For feminin all sound files end with F */
1139 /*      100E for 100+ something */
1140 /*      1000000S for plural */
1141 /*      pt-e for 'and' */
1142 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1143 {
1144         int res = 0;
1145         int playh = 0;
1146         int mf = 1;                            /* +1 = Masculin; -1 = Feminin */
1147         char fn[256] = "";
1148
1149         if (!num) 
1150                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1151
1152         if (options && !strncasecmp(options, "f",1))
1153                 mf = -1;
1154
1155         while(!res && num ) {
1156                 if (num < 20) {
1157                         if ((num == 1 || num == 2) && (mf < 0))
1158                                 snprintf(fn, sizeof(fn), "digits/%dF", num);
1159                         else
1160                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1161                         num = 0;
1162                 } else if (num < 100) {
1163                         snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1164                         if (num % 10)
1165                                 playh = 1;
1166                         num = num % 10;
1167                 } else if (num < 1000) {
1168                         if (num == 100)
1169                                 snprintf(fn, sizeof(fn), "digits/100");
1170                         else if (num < 200)
1171                                 snprintf(fn, sizeof(fn), "digits/100E");
1172                         else {
1173                                 if (mf < 0 && num > 199)
1174                                         snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1175                                 else
1176                                         snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1177                                 if (num % 100)
1178                                         playh = 1;
1179                         }
1180                         num = num % 100;
1181                 } else if (num < 1000000) {
1182                         if (num > 1999) {
1183                                 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1184                                 if (res)
1185                                         return res;
1186                         }
1187                         snprintf(fn, sizeof(fn), "digits/1000");
1188                         if ((num % 1000) && ((num % 1000) < 100  || !(num % 100)))
1189                                 playh = 1;
1190                         num = num % 1000;
1191                 } else if (num < 1000000000) {
1192                         res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
1193                         if (res)
1194                                 return res;
1195                         if (num < 2000000)
1196                                 snprintf(fn, sizeof(fn), "digits/1000000");
1197                         else
1198                                 snprintf(fn, sizeof(fn), "digits/1000000S");
1199  
1200                         if ((num % 1000000) &&
1201                                 // no thousands
1202                                 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
1203                                 // no hundreds and below
1204                                 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
1205                                 playh = 1;
1206                         num = num % 1000000;
1207                 }
1208                 if (!res && playh) {
1209                         res = wait_file(chan, ints, "digits/pt-e", language);
1210                         ast_stopstream(chan);
1211                         playh = 0;
1212                 }
1213                 if (!res) {
1214                         if(!ast_streamfile(chan, fn, language)) {
1215                                 if (audiofd && ctrlfd)
1216                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);                 else
1217                                         res = ast_waitstream(chan, ints);
1218                         }
1219                         ast_stopstream(chan);
1220                 }
1221         }
1222         return res;
1223 }
1224
1225 int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang)
1226 {
1227         if (!strcasecmp(lang,"en") ) {  /* English syntax */
1228                 return(ast_say_date_en(chan, t, ints, lang));
1229         } else if (!strcasecmp(lang, "nl") ) {  /* Dutch syntax */
1230                 return(ast_say_date_nl(chan, t, ints, lang));
1231         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
1232                 return(ast_say_date_pt(chan, t, ints, lang));
1233         }
1234
1235         /* Default to English */
1236         return(ast_say_date_en(chan, t, ints, lang));
1237 }
1238
1239
1240 /* English syntax */
1241 int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
1242 {
1243         struct tm tm;
1244         char fn[256];
1245         int res = 0;
1246         ast_localtime(&t,&tm,NULL);
1247         if (!res) {
1248                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1249                 res = ast_streamfile(chan, fn, lang);
1250                 if (!res)
1251                         res = ast_waitstream(chan, ints);
1252         }
1253         if (!res) {
1254                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1255                 res = ast_streamfile(chan, fn, lang);
1256                 if (!res)
1257                         res = ast_waitstream(chan, ints);
1258         }
1259         if (!res)
1260                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1261         if (!res)
1262                 res = ast_waitstream(chan, ints);
1263         if (!res)
1264                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1265         return res;
1266 }
1267
1268 /* Dutch syntax */
1269 int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
1270 {
1271         struct tm tm;
1272         char fn[256];
1273         int res = 0;
1274         ast_localtime(&t,&tm,NULL);
1275         if (!res) {
1276                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1277                 res = ast_streamfile(chan, fn, lang);
1278                 if (!res)
1279                         res = ast_waitstream(chan, ints);
1280         }
1281         if (!res)
1282                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1283         if (!res) {
1284                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1285                 res = ast_streamfile(chan, fn, lang);
1286                 if (!res)
1287                         res = ast_waitstream(chan, ints);
1288         }
1289         if (!res)
1290                 res = ast_waitstream(chan, ints);
1291         if (!res)
1292                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1293         return res;
1294 }
1295
1296 /* Portuguese syntax */
1297 int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
1298 {
1299         struct tm tm;
1300         char fn[256];
1301         int res = 0;
1302         ast_localtime(&t,&tm,NULL);
1303         localtime_r(&t,&tm);
1304         snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1305         if (!res)
1306                 res = wait_file(chan, ints, fn, lang);
1307         if (!res)
1308                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1309         if (!res)
1310                 res = wait_file(chan, ints, "digits/pt-de", lang);
1311         snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1312         if (!res)
1313                 res = wait_file(chan, ints, fn, lang);
1314         if (!res)
1315                 res = wait_file(chan, ints, "digits/pt-de", lang);
1316         if (!res)
1317                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1318
1319         return res;
1320 }
1321
1322 int ast_say_date_with_format(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1323 {
1324         if (!strcasecmp(lang, "en") ) { /* English syntax */
1325                 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1326         } else if (!strcasecmp(lang, "nl") ) {  /* Dutch syntax */
1327                 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
1328         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
1329                 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
1330         }
1331
1332         /* Default to English */
1333         return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1334 }
1335
1336 /* English syntax */
1337 int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1338 {
1339         struct tm tm;
1340         int res=0, offset, sndoffset;
1341         char sndfile[256], nextmsg[256];
1342
1343         ast_localtime(&time,&tm,timezone);
1344
1345         for (offset=0 ; format[offset] != '\0' ; offset++) {
1346                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1347                 switch (format[offset]) {
1348                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
1349                         case '\'':
1350                                 /* Literal name of a sound file */
1351                                 sndoffset=0;
1352                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1353                                         sndfile[sndoffset] = format[offset];
1354                                 sndfile[sndoffset] = '\0';
1355                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1356                                 res = wait_file(chan,ints,nextmsg,lang);
1357                                 break;
1358                         case 'A':
1359                         case 'a':
1360                                 /* Sunday - Saturday */
1361                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1362                                 res = wait_file(chan,ints,nextmsg,lang);
1363                                 break;
1364                         case 'B':
1365                         case 'b':
1366                         case 'h':
1367                                 /* January - December */
1368                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1369                                 res = wait_file(chan,ints,nextmsg,lang);
1370                                 break;
1371                         case 'd':
1372                         case 'e':
1373                                 /* First - Thirtyfirst */
1374                                 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1375                                         snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
1376                                         res = wait_file(chan,ints,nextmsg,lang);
1377                                 } else if (tm.tm_mday == 31) {
1378                                         /* "Thirty" and "first" */
1379                                         res = wait_file(chan,ints, "digits/30",lang);
1380                                         if (!res) {
1381                                                 res = wait_file(chan,ints, "digits/h-1",lang);
1382                                         }
1383                                 } else {
1384                                         /* Between 21 and 29 - two sounds */
1385                                         res = wait_file(chan,ints, "digits/20",lang);
1386                                         if (!res) {
1387                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
1388                                                 res = wait_file(chan,ints,nextmsg,lang);
1389                                         }
1390                                 }
1391                                 break;
1392                         case 'Y':
1393                                 /* Year */
1394                                 if (tm.tm_year > 99) {
1395                                         res = wait_file(chan,ints, "digits/2",lang);
1396                                         if (!res) {
1397                                                 res = wait_file(chan,ints, "digits/thousand",lang);
1398                                         }
1399                                         if (tm.tm_year > 100) {
1400                                                 if (!res) {
1401                                                         /* This works until the end of 2020 */
1402                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1403                                                         res = wait_file(chan,ints,nextmsg,lang);
1404                                                 }
1405                                         }
1406                                 } else {
1407                                         if (tm.tm_year < 1) {
1408                                                 /* I'm not going to handle 1900 and prior */
1409                                                 /* We'll just be silent on the year, instead of bombing out. */
1410                                         } else {
1411                                                 res = wait_file(chan,ints, "digits/19",lang);
1412                                                 if (!res) {
1413                                                         if (tm.tm_year <= 9) {
1414                                                                 /* 1901 - 1909 */
1415                                                                 res = wait_file(chan,ints, "digits/oh",lang);
1416                                                                 if (!res) {
1417                                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1418                                                                         res = wait_file(chan,ints,nextmsg,lang);
1419                                                                 }
1420                                                         } else if (tm.tm_year <= 20) {
1421                                                                 /* 1910 - 1920 */
1422                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1423                                                                 res = wait_file(chan,ints,nextmsg,lang);
1424                                                         } else {
1425                                                                 /* 1921 - 1999 */
1426                                                                 int ten, one;
1427                                                                 ten = tm.tm_year / 10;
1428                                                                 one = tm.tm_year % 10;
1429                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1430                                                                 res = wait_file(chan,ints,nextmsg,lang);
1431                                                                 if (!res) {
1432                                                                         if (one != 0) {
1433                                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1434                                                                                 res = wait_file(chan,ints,nextmsg,lang);
1435                                                                         }
1436                                                                 }
1437                                                         }
1438                                                 }
1439                                         }
1440                                 }
1441                                 break;
1442                         case 'I':
1443                         case 'l':
1444                                 /* 12-Hour */
1445                                 if (tm.tm_hour == 0)
1446                                         snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1447                                 else if (tm.tm_hour > 12)
1448                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1449                                 else
1450                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1451                                 res = wait_file(chan,ints,nextmsg,lang);
1452                                 break;
1453                         case 'H':
1454                         case 'k':
1455                                 /* 24-Hour */
1456                                 if (format[offset] == 'H') {
1457                                         /* e.g. oh-eight */
1458                                         if (tm.tm_hour < 10) {
1459                                                 res = wait_file(chan,ints, "digits/oh",lang);
1460                                         }
1461                                 } else {
1462                                         /* e.g. eight */
1463                                         if (tm.tm_hour == 0) {
1464                                                 res = wait_file(chan,ints, "digits/oh",lang);
1465                                         }
1466                                 }
1467                                 if (!res) {
1468                                         if (tm.tm_hour != 0) {
1469                                                 int remainder = tm.tm_hour;
1470                                                 if (tm.tm_hour > 20) {
1471                                                         res = wait_file(chan,ints, "digits/20",lang);
1472                                                         remainder -= 20;
1473                                                 }
1474                                                 if (!res) {
1475                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1476                                                         res = wait_file(chan,ints,nextmsg,lang);
1477                                                 }
1478                                         }
1479                                 }
1480                                 break;
1481                         case 'M':
1482                                 /* Minute */
1483                                 if (tm.tm_min == 0) {
1484                                         res = wait_file(chan,ints, "digits/oclock",lang);
1485                                 } else if (tm.tm_min < 10) {
1486                                         res = wait_file(chan,ints, "digits/oh",lang);
1487                                         if (!res) {
1488                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1489                                                 res = wait_file(chan,ints,nextmsg,lang);
1490                                         }
1491                                 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
1492                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1493                                         res = wait_file(chan,ints,nextmsg,lang);
1494                                 } else {
1495                                         int ten, one;
1496                                         ten = (tm.tm_min / 10) * 10;
1497                                         one = (tm.tm_min % 10);
1498                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1499                                         res = wait_file(chan,ints,nextmsg,lang);
1500                                         if (!res) {
1501                                                 /* Fifty, not fifty-zero */
1502                                                 if (one != 0) {
1503                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1504                                                         res = wait_file(chan,ints,nextmsg,lang);
1505                                                 }
1506                                         }
1507                                 }
1508                                 break;
1509                         case 'P':
1510                         case 'p':
1511                                 /* AM/PM */
1512                                 if (tm.tm_hour > 11)
1513                                         snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1514                                 else
1515                                         snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1516                                 res = wait_file(chan,ints,nextmsg,lang);
1517                                 break;
1518                         case 'Q':
1519                                 /* Shorthand for "Today", "Yesterday", or ABdY */
1520                                 {
1521                                         struct timeval now;
1522                                         struct tm tmnow;
1523                                         time_t beg_today;
1524
1525                                         gettimeofday(&now,NULL);
1526                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1527                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1528                                         /* In any case, it saves not having to do ast_mktime() */
1529                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1530                                         if (beg_today < time) {
1531                                                 /* Today */
1532                                                 res = wait_file(chan,ints, "digits/today",lang);
1533                                         } else if (beg_today - 86400 < time) {
1534                                                 /* Yesterday */
1535                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1536                                         } else {
1537                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1538                                         }
1539                                 }
1540                                 break;
1541                         case 'q':
1542                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1543                                 {
1544                                         struct timeval now;
1545                                         struct tm tmnow;
1546                                         time_t beg_today;
1547
1548                                         gettimeofday(&now,NULL);
1549                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1550                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1551                                         /* In any case, it saves not having to do ast_mktime() */
1552                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1553                                         if (beg_today < time) {
1554                                                 /* Today */
1555                                         } else if ((beg_today - 86400) < time) {
1556                                                 /* Yesterday */
1557                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1558                                         } else if (beg_today - 86400 * 6 < time) {
1559                                                 /* Within the last week */
1560                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1561                                         } else {
1562                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1563                                         }
1564                                 }
1565                                 break;
1566                         case 'R':
1567                                 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1568                                 break;
1569                         case 'S':
1570                                 /* Seconds */
1571                                 if (tm.tm_sec == 0) {
1572                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1573                                         res = wait_file(chan,ints,nextmsg,lang);
1574                                 } else if (tm.tm_sec < 10) {
1575                                         res = wait_file(chan,ints, "digits/oh",lang);
1576                                         if (!res) {
1577                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1578                                                 res = wait_file(chan,ints,nextmsg,lang);
1579                                         }
1580                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1581                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1582                                         res = wait_file(chan,ints,nextmsg,lang);
1583                                 } else {
1584                                         int ten, one;
1585                                         ten = (tm.tm_sec / 10) * 10;
1586                                         one = (tm.tm_sec % 10);
1587                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1588                                         res = wait_file(chan,ints,nextmsg,lang);
1589                                         if (!res) {
1590                                                 /* Fifty, not fifty-zero */
1591                                                 if (one != 0) {
1592                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1593                                                         res = wait_file(chan,ints,nextmsg,lang);
1594                                                 }
1595                                         }
1596                                 }
1597                                 break;
1598                         case 'T':
1599                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1600                                 break;
1601                         case ' ':
1602                         case '  ':
1603                                 /* Just ignore spaces and tabs */
1604                                 break;
1605                         default:
1606                                 /* Unknown character */
1607                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1608                 }
1609                 /* Jump out on DTMF */
1610                 if (res) {
1611                         break;
1612                 }
1613         }
1614         return res;
1615 }
1616
1617 /* Dutch syntax */
1618 int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1619 {
1620         struct tm tm;
1621         int res=0, offset, sndoffset;
1622         char sndfile[256], nextmsg[256];
1623
1624         ast_localtime(&time,&tm,timezone);
1625
1626         for (offset=0 ; format[offset] != '\0' ; offset++) {
1627                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1628                 switch (format[offset]) {
1629                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
1630                         case '\'':
1631                                 /* Literal name of a sound file */
1632                                 sndoffset=0;
1633                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1634                                         sndfile[sndoffset] = format[offset];
1635                                 sndfile[sndoffset] = '\0';
1636                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1637                                 res = wait_file(chan,ints,nextmsg,lang);
1638                                 break;
1639                         case 'A':
1640                         case 'a':
1641                                 /* Sunday - Saturday */
1642                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1643                                 res = wait_file(chan,ints,nextmsg,lang);
1644                                 break;
1645                         case 'B':
1646                         case 'b':
1647                         case 'h':
1648                                 /* January - December */
1649                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1650                                 res = wait_file(chan,ints,nextmsg,lang);
1651                                 break;
1652                         case 'd':
1653                         case 'e':
1654                                 /* First - Thirtyfirst */
1655                                 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
1656                                 break;
1657                         case 'Y':
1658                                 /* Year */
1659                                 if (tm.tm_year > 99) {
1660                                         res = wait_file(chan,ints, "digits/2",lang);
1661                                         if (!res) {
1662                                                 res = wait_file(chan,ints, "digits/thousand",lang);
1663                                         }
1664                                         if (tm.tm_year > 100) {
1665                                                 if (!res) {
1666                                                         /* This works until the end of 2020 */
1667                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1668                                                         res = wait_file(chan,ints,nextmsg,lang);
1669                                                 }
1670                                         }
1671                                 } else {
1672                                         if (tm.tm_year < 1) {
1673                                                 /* I'm not going to handle 1900 and prior */
1674                                                 /* We'll just be silent on the year, instead of bombing out. */
1675                                         } else {
1676                                                 res = wait_file(chan,ints, "digits/19",lang);
1677                                                 if (!res) {
1678                                                         if (tm.tm_year <= 9) {
1679                                                                 /* 1901 - 1909 */
1680                                                                 res = wait_file(chan,ints, "digits/oh",lang);
1681                                                                 if (!res) {
1682                                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1683                                                                         res = wait_file(chan,ints,nextmsg,lang);
1684                                                                 }
1685                                                         } else if (tm.tm_year <= 20) {
1686                                                                 /* 1910 - 1920 */
1687                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1688                                                                 res = wait_file(chan,ints,nextmsg,lang);
1689                                                         } else {
1690                                                                 /* 1921 - 1999 */
1691                                                                 int ten, one;
1692                                                                 ten = tm.tm_year / 10;
1693                                                                 one = tm.tm_year % 10;
1694                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1695                                                                 res = wait_file(chan,ints,nextmsg,lang);
1696                                                                 if (!res) {
1697                                                                         if (one != 0) {
1698                                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1699                                                                                 res = wait_file(chan,ints,nextmsg,lang);
1700                                                                         }
1701                                                                 }
1702                                                         }
1703                                                 }
1704                                         }
1705                                 }
1706                                 break;
1707                         case 'I':
1708                         case 'l':
1709                                 /* 12-Hour */
1710                                 if (tm.tm_hour == 0)
1711                                         snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1712                                 else if (tm.tm_hour > 12)
1713                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1714                                 else
1715                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1716                                 res = wait_file(chan,ints,nextmsg,lang);
1717                                 break;
1718                         case 'H':
1719                         case 'k':
1720                                 /* 24-Hour */
1721                                 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
1722                                 if (!res) {
1723                                         res = wait_file(chan,ints, "digits/nl-uur",lang);
1724                                 }
1725                                 break;
1726                         case 'M':
1727                                 /* Minute */
1728                                 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
1729                                 break;
1730                         case 'P':
1731                         case 'p':
1732                                 /* AM/PM */
1733                                 if (tm.tm_hour > 11)
1734                                         snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1735                                 else
1736                                         snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1737                                 res = wait_file(chan,ints,nextmsg,lang);
1738                                 break;
1739                         case 'Q':
1740                                 /* Shorthand for "Today", "Yesterday", or ABdY */
1741                                 {
1742                                         struct timeval now;
1743                                         struct tm tmnow;
1744                                         time_t beg_today;
1745
1746                                         gettimeofday(&now,NULL);
1747                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1748                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1749                                         /* In any case, it saves not having to do ast_mktime() */
1750                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1751                                         if (beg_today < time) {
1752                                                 /* Today */
1753                                                 res = wait_file(chan,ints, "digits/today",lang);
1754                                         } else if (beg_today - 86400 < time) {
1755                                                 /* Yesterday */
1756                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1757                                         } else {
1758                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1759                                         }
1760                                 }
1761                                 break;
1762                         case 'q':
1763                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1764                                 {
1765                                         struct timeval now;
1766                                         struct tm tmnow;
1767                                         time_t beg_today;
1768
1769                                         gettimeofday(&now,NULL);
1770                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1771                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1772                                         /* In any case, it saves not having to do ast_mktime() */
1773                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1774                                         if (beg_today < time) {
1775                                                 /* Today */
1776                                         } else if ((beg_today - 86400) < time) {
1777                                                 /* Yesterday */
1778                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1779                                         } else if (beg_today - 86400 * 6 < time) {
1780                                                 /* Within the last week */
1781                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1782                                         } else {
1783                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1784                                         }
1785                                 }
1786                                 break;
1787                         case 'R':
1788                                 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1789                                 break;
1790                         case 'S':
1791                                 /* Seconds */
1792                                 if (tm.tm_sec == 0) {
1793                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1794                                         res = wait_file(chan,ints,nextmsg,lang);
1795                                 } else if (tm.tm_sec < 10) {
1796                                         res = wait_file(chan,ints, "digits/oh",lang);
1797                                         if (!res) {
1798                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1799                                                 res = wait_file(chan,ints,nextmsg,lang);
1800                                         }
1801                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1802                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1803                                         res = wait_file(chan,ints,nextmsg,lang);
1804                                 } else {
1805                                         int ten, one;
1806                                         ten = (tm.tm_sec / 10) * 10;
1807                                         one = (tm.tm_sec % 10);
1808                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1809                                         res = wait_file(chan,ints,nextmsg,lang);
1810                                         if (!res) {
1811                                                 /* Fifty, not fifty-zero */
1812                                                 if (one != 0) {
1813                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1814                                                         res = wait_file(chan,ints,nextmsg,lang);
1815                                                 }
1816                                         }
1817                                 }
1818                                 break;
1819                         case 'T':
1820                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1821                                 break;
1822                         case ' ':
1823                         case '  ':
1824                                 /* Just ignore spaces and tabs */
1825                                 break;
1826                         default:
1827                                 /* Unknown character */
1828                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1829                 }
1830                 /* Jump out on DTMF */
1831                 if (res) {
1832                         break;
1833                 }
1834         }
1835         return res;
1836 }
1837
1838 /* Portuguese syntax */
1839 int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1840 {
1841         struct tm tm;
1842         int res=0, offset, sndoffset;
1843         char sndfile[256], nextmsg[256];
1844
1845         ast_localtime(&time,&tm,timezone);
1846
1847         for (offset=0 ; format[offset] != '\0' ; offset++) {
1848                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1849                 switch (format[offset]) {
1850                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
1851                         case '\'':
1852                                 /* Literal name of a sound file */
1853                                 sndoffset=0;
1854                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1855                                         sndfile[sndoffset] = format[offset];
1856                                 sndfile[sndoffset] = '\0';
1857                                 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
1858                                 res = wait_file(chan,ints,nextmsg,lang);
1859                                 break;
1860                         case 'A':
1861                         case 'a':
1862                                 /* Sunday - Saturday */
1863                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1864                                 res = wait_file(chan,ints,nextmsg,lang);
1865                                 break;
1866                         case 'B':
1867                         case 'b':
1868                         case 'h':
1869                                 /* January - December */
1870                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1871                                 res = wait_file(chan,ints,nextmsg,lang);
1872                                 break;
1873                         case 'd':
1874                         case 'e':
1875                                 /* First - Thirtyfirst */
1876                                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1877                                 break;
1878                         case 'Y':
1879                                 /* Year */
1880                                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1881                                 break;
1882                         case 'I':
1883                         case 'l':
1884                                 /* 12-Hour */
1885                                 if (tm.tm_hour == 0) {
1886                                         if (format[offset] == 'I')
1887                                                 res = wait_file(chan, ints, "digits/pt-ah", lang);
1888                                         if (!res)
1889                                                 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
1890                                 }
1891                                 else if (tm.tm_hour == 12) {
1892                                         if (format[offset] == 'I')
1893                                                 res = wait_file(chan, ints, "digits/pt-ao", lang);
1894                                         if (!res)
1895                                                 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
1896                                 }
1897                                 else {
1898                                         if (format[offset] == 'I') {
1899                                                 res = wait_file(chan, ints, "digits/pt-ah", lang);
1900                                                 if ((tm.tm_hour % 12) != 1)
1901                                                         if (!res)
1902                                                                 res = wait_file(chan, ints, "digits/pt-sss", lang);
1903                                         }
1904                                         if (!res)
1905                                                 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
1906                                 }
1907                                 break;
1908                         case 'H':
1909                         case 'k':
1910                                 /* 24-Hour */
1911                                 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
1912                                 if (!res) {
1913                                         if (tm.tm_hour != 0) {
1914                                                 int remainder = tm.tm_hour;
1915                                                 if (tm.tm_hour > 20) {
1916                                                         res = wait_file(chan,ints, "digits/20",lang);
1917                                                         remainder -= 20;
1918                                                 }
1919                                                 if (!res) {
1920                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1921                                                         res = wait_file(chan,ints,nextmsg,lang);
1922                                                 }
1923                                         }
1924                                 }
1925                                 break;
1926                         case 'M':
1927                                 /* Minute */
1928                                 if (tm.tm_min == 0) {
1929                                         res = wait_file(chan, ints, "digits/pt-hora", lang);
1930                                         if (tm.tm_hour != 1)
1931                                                 if (!res)
1932                                                         res = wait_file(chan, ints, "digits/pt-sss", lang);                     } else {
1933                                         res = wait_file(chan,ints,"digits/pt-e",lang);
1934                                         if (!res)
1935                                                 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);       
1936                                 }
1937                                 break;
1938                         case 'P':
1939                         case 'p':
1940                                 /* AM/PM */
1941                                 if (tm.tm_hour > 12)
1942                                         res = wait_file(chan, ints, "digits/p-m", lang);
1943                                 else if (tm.tm_hour  && tm.tm_hour < 12)
1944                                         res = wait_file(chan, ints, "digits/a-m", lang);
1945                                 break;
1946                         case 'Q':
1947                                 /* Shorthand for "Today", "Yesterday", or ABdY */
1948                                 {
1949                                         struct timeval now;
1950                                         struct tm tmnow;
1951                                         time_t beg_today;
1952
1953                                         gettimeofday(&now,NULL);
1954                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1955                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1956                                         /* In any case, it saves not having to do ast_mktime() */
1957                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1958                                         if (beg_today < time) {
1959                                                 /* Today */
1960                                                 res = wait_file(chan,ints, "digits/today",lang);
1961                                         } else if (beg_today - 86400 < time) {
1962                                                 /* Yesterday */
1963                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1964                                         } else {
1965                                                 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
1966                                         }
1967                                 }
1968                                 break;
1969                         case 'q':
1970                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1971                                 {
1972                                         struct timeval now;
1973                                         struct tm tmnow;
1974                                         time_t beg_today;
1975
1976                                         gettimeofday(&now,NULL);
1977                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1978                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1979                                         /* In any case, it saves not having to do ast_mktime() */
1980                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1981                                         if (beg_today < time) {
1982                                                 /* Today */
1983                                         } else if ((beg_today - 86400) < time) {
1984                                                 /* Yesterday */
1985                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1986                                         } else if (beg_today - 86400 * 6 < time) {
1987                                                 /* Within the last week */
1988                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1989                                         } else {
1990                                                 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
1991                                         }
1992                                 }
1993                                 break;
1994                         case 'R':
1995                                 res = ast_say_date_with_format(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
1996                                 break;
1997                         case 'S':
1998                                 /* Seconds */
1999                                 if (tm.tm_sec == 0) {
2000                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2001                                         res = wait_file(chan,ints,nextmsg,lang);
2002                                 } else if (tm.tm_sec < 10) {
2003                                         res = wait_file(chan,ints, "digits/oh",lang);
2004                                         if (!res) {
2005                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2006                                                 res = wait_file(chan,ints,nextmsg,lang);
2007                                         }
2008                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2009                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2010                                         res = wait_file(chan,ints,nextmsg,lang);
2011                                 } else {
2012                                         int ten, one;
2013                                         ten = (tm.tm_sec / 10) * 10;
2014                                         one = (tm.tm_sec % 10);
2015                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2016                                         res = wait_file(chan,ints,nextmsg,lang);
2017                                         if (!res) {
2018                                                 /* Fifty, not fifty-zero */
2019                                                 if (one != 0) {
2020                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2021                                                         res = wait_file(chan,ints,nextmsg,lang);
2022                                                 }
2023                                         }
2024                                 }
2025                                 break;
2026                         case 'T':
2027                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2028                                 break;
2029                         case ' ':
2030                         case '  ':
2031                                 /* Just ignore spaces and tabs */
2032                                 break;
2033                         default:
2034                                 /* Unknown character */
2035                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2036                 }
2037                 /* Jump out on DTMF */
2038                 if (res) {
2039                         break;
2040                 }
2041         }
2042         return res;
2043 }
2044
2045 int ast_say_time(struct ast_channel *chan, time_t t, char *ints, char *lang)
2046 {
2047         if (!strcasecmp(lang, "en") ) { /* English syntax */
2048                 return(ast_say_time_en(chan, t, ints, lang));
2049         } else if (!strcasecmp(lang, "nl") ) {  /* Dutch syntax */
2050                 return(ast_say_time_nl(chan, t, ints, lang));
2051         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
2052                 return(ast_say_time_pt(chan, t, ints, lang));
2053         }
2054
2055         /* Default to English */
2056         return(ast_say_time_en(chan, t, ints, lang));
2057 }
2058
2059 /* English syntax */
2060 int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2061 {
2062         struct tm tm;
2063         int res = 0;
2064         int hour, pm=0;
2065         localtime_r(&t,&tm);
2066         hour = tm.tm_hour;
2067         if (!hour)
2068                 hour = 12;
2069         else if (hour == 12)
2070                 pm = 1;
2071         else if (hour > 12) {
2072                 hour -= 12;
2073                 pm = 1;
2074         }
2075         if (!res)
2076                 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2077
2078         if (tm.tm_min > 9) {
2079                 if (!res)
2080                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2081         } else if (tm.tm_min) {
2082                 if (!res)
2083                         res = ast_streamfile(chan, "digits/oh", lang);
2084                 if (!res)
2085                         res = ast_waitstream(chan, ints);
2086                 if (!res)
2087                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2088         } else {
2089                 if (!res)
2090                         res = ast_streamfile(chan, "digits/oclock", lang);
2091                 if (!res)
2092                         res = ast_waitstream(chan, ints);
2093         }
2094         if (pm) {
2095                 if (!res)
2096                         res = ast_streamfile(chan, "digits/p-m", lang);
2097         } else {
2098                 if (!res)
2099                         res = ast_streamfile(chan, "digits/a-m", lang);
2100         }
2101         if (!res)
2102                 res = ast_waitstream(chan, ints);
2103         return res;
2104 }
2105
2106 /* Dutch syntax */
2107 int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
2108 {
2109         struct tm tm;
2110         int res = 0;
2111         int hour;
2112         localtime_r(&t,&tm);
2113         hour = tm.tm_hour;
2114         if (!res)
2115                 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2116
2117         if (!res)
2118                 res = ast_streamfile(chan, "digits/nl-uur", lang);
2119         if (!res)
2120                 res = ast_waitstream(chan, ints);
2121         if (!res)
2122             if (tm.tm_min > 0) 
2123                 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
2124         return res;
2125 }
2126
2127 /* Portuguese syntax */
2128 int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2129 {
2130         struct tm tm;
2131         int res = 0;
2132         int hour;
2133         localtime_r(&t,&tm);
2134         hour = tm.tm_hour;
2135         if (!res)
2136                 res = ast_say_number(chan, hour, ints, lang, "f");
2137         if (tm.tm_min) {
2138                 if (!res)
2139                         res = wait_file(chan, ints, "digits/pt-e", lang);
2140                 if (!res)
2141                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2142         } else {
2143                 if (!res)
2144                         res = wait_file(chan, ints, "digits/pt-hora", lang);
2145                 if (tm.tm_hour != 1)
2146                         if (!res)
2147                                 res = wait_file(chan, ints, "digits/pt-sss", lang);
2148         }
2149         if (!res)
2150                 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2151         return res;
2152 }
2153
2154 int ast_say_datetime(struct ast_channel *chan, time_t t, char *ints, char *lang)
2155 {
2156         if (!strcasecmp(lang, "en") ) { /* English syntax */
2157                 return(ast_say_datetime_en(chan, t, ints, lang));
2158         } else if (!strcasecmp(lang, "nl") ) {  /* Dutch syntax */
2159                 return(ast_say_datetime_nl(chan, t, ints, lang));
2160         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
2161                 return(ast_say_datetime_pt(chan, t, ints, lang));
2162         }
2163
2164         /* Default to English */
2165         return(ast_say_datetime_en(chan, t, ints, lang));
2166 }
2167
2168 /* English syntax */
2169 int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2170 {
2171         struct tm tm;
2172         char fn[256];
2173         int res = 0;
2174         int hour, pm=0;
2175         localtime_r(&t,&tm);
2176         if (!res) {
2177                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2178                 res = ast_streamfile(chan, fn, lang);
2179                 if (!res)
2180                         res = ast_waitstream(chan, ints);
2181         }
2182         if (!res) {
2183                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2184                 res = ast_streamfile(chan, fn, lang);
2185                 if (!res)
2186                         res = ast_waitstream(chan, ints);
2187         }
2188         if (!res)
2189                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2190
2191         hour = tm.tm_hour;
2192         if (!hour)
2193                 hour = 12;
2194         else if (hour == 12)
2195                 pm = 1;
2196         else if (hour > 12) {
2197                 hour -= 12;
2198                 pm = 1;
2199         }
2200         if (!res)
2201                 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2202
2203         if (tm.tm_min > 9) {
2204                 if (!res)
2205                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2206         } else if (tm.tm_min) {
2207                 if (!res)
2208                         res = ast_streamfile(chan, "digits/oh", lang);
2209                 if (!res)
2210                         res = ast_waitstream(chan, ints);
2211                 if (!res)
2212                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2213         } else {
2214                 if (!res)
2215                         res = ast_streamfile(chan, "digits/oclock", lang);
2216                 if (!res)
2217                         res = ast_waitstream(chan, ints);
2218         }
2219         if (pm) {
2220                 if (!res)
2221                         res = ast_streamfile(chan, "digits/p-m", lang);
2222         } else {
2223                 if (!res)
2224                         res = ast_streamfile(chan, "digits/a-m", lang);
2225         }
2226         if (!res)
2227                 res = ast_waitstream(chan, ints);
2228         if (!res)
2229                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2230         return res;
2231 }
2232
2233 /* Dutch syntax */
2234 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
2235 {
2236         struct tm tm;
2237         int res = 0;
2238         localtime_r(&t,&tm);
2239         res = ast_say_date(chan, t, ints, lang);
2240         if (!res) {
2241                 res = ast_streamfile(chan, "digits/nl-om", lang);
2242                 if (!res)
2243                         res = ast_waitstream(chan, ints);
2244         }
2245         if (!res) 
2246                 ast_say_time(chan, t, ints, lang);
2247         return res;
2248 }
2249
2250 /* Portuguese syntax */
2251 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2252 {
2253         struct tm tm;
2254         char fn[256];
2255         int res = 0;
2256         int hour, pm=0;
2257         localtime_r(&t,&tm);
2258         if (!res) {
2259                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2260                 res = ast_streamfile(chan, fn, lang);
2261                 if (!res)
2262                         res = ast_waitstream(chan, ints);
2263         }
2264         if (!res) {
2265                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2266                 res = ast_streamfile(chan, fn, lang);
2267                 if (!res)
2268                         res = ast_waitstream(chan, ints);
2269         }
2270         if (!res)
2271                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2272
2273         hour = tm.tm_hour;
2274         if (!hour)
2275                 hour = 12;
2276         else if (hour == 12)
2277                 pm = 1;
2278         else if (hour > 12) {
2279                 hour -= 12;
2280                 pm = 1;
2281         }
2282         if (!res)
2283                 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2284
2285         if (tm.tm_min > 9) {
2286                 if (!res)
2287                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2288         } else if (tm.tm_min) {
2289                 if (!res)
2290                         res = ast_streamfile(chan, "digits/oh", lang);
2291                 if (!res)
2292                         res = ast_waitstream(chan, ints);
2293                 if (!res)
2294                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2295         } else {
2296                 if (!res)
2297                         res = ast_streamfile(chan, "digits/oclock", lang);
2298                 if (!res)
2299                         res = ast_waitstream(chan, ints);
2300         }
2301         if (pm) {
2302                 if (!res)
2303                         res = ast_streamfile(chan, "digits/p-m", lang);
2304         } else {
2305                 if (!res)
2306                         res = ast_streamfile(chan, "digits/a-m", lang);
2307         }
2308         if (!res)
2309                 res = ast_waitstream(chan, ints);
2310         if (!res)
2311                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2312         return res;
2313 }
2314
2315 int ast_say_datetime_from_now(struct ast_channel *chan, time_t t, char *ints, char *lang)
2316 {
2317         if (!strcasecmp(lang, "en") ) { /* English syntax */
2318                 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
2319         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
2320                 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
2321         }
2322
2323         /* Default to English */
2324         return(ast_say_datetime_from_now_en(chan, t, ints, lang));
2325 }
2326
2327 /* English syntax */
2328 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2329 {
2330         int res=0;
2331         time_t nowt;
2332         int daydiff;
2333         struct tm tm;
2334         struct tm now;
2335         char fn[256];
2336
2337         time(&nowt);
2338
2339         localtime_r(&t,&tm);
2340         localtime_r(&nowt,&now);
2341         daydiff = now.tm_yday - tm.tm_yday;
2342         if ((daydiff < 0) || (daydiff > 6)) {
2343                 /* Day of month and month */
2344                 if (!res) {
2345                         snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2346                         res = ast_streamfile(chan, fn, lang);
2347                         if (!res)
2348                                 res = ast_waitstream(chan, ints);
2349                 }
2350                 if (!res)
2351                         res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2352
2353         } else if (daydiff) {
2354                 /* Just what day of the week */
2355                 if (!res) {
2356                         snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2357                         res = ast_streamfile(chan, fn, lang);
2358                         if (!res)
2359                                 res = ast_waitstream(chan, ints);
2360                 }
2361         } /* Otherwise, it was today */
2362         if (!res)
2363                 res = ast_say_time(chan, t, ints, lang);
2364         return res;
2365 }
2366
2367 /* Portuguese syntax */
2368 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2369 {
2370         int res=0;
2371         time_t nowt;
2372         int daydiff;
2373         struct tm tm;
2374         struct tm now;
2375         char fn[256];
2376
2377         time(&nowt);
2378
2379         localtime_r(&t,&tm);
2380         localtime_r(&nowt,&now);
2381         daydiff = now.tm_yday - tm.tm_yday;
2382         if ((daydiff < 0) || (daydiff > 6)) {
2383                 /* Day of month and month */
2384                 if (!res)
2385                         res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2386                 if (!res)
2387                         res = wait_file(chan, ints, "digits/pt-de", lang);
2388                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2389                 if (!res)
2390                         res = wait_file(chan, ints, fn, lang);
2391         
2392         } else if (daydiff) {
2393                 /* Just what day of the week */
2394                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2395                 if (!res)
2396                         res = wait_file(chan, ints, fn, lang);
2397         }       /* Otherwise, it was today */
2398         snprintf(fn, sizeof(fn), "digits/pt-ah");
2399         if (!res)
2400                 res = wait_file(chan, ints, fn, lang);
2401         if (tm.tm_hour != 1)
2402         if (!res)
2403                 res = wait_file(chan, ints, "digits/pt-sss", lang);
2404         if (!res)
2405                 res = ast_say_time(chan, t, ints, lang);
2406         return res;
2407 }