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