Enhance Spanish and Mexican support (bug #1566) thanks!
[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, Mexican
409       fr - French
410       it - Italian
411       nl - Dutch
412       pt - Portuguese
413       se - Swedish
414
415  Gender:
416  For Portuguese, French & Spanish, 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 German (although the option is passed to the function, it currently does nothing with it).
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, char *options, 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") || !strcasecmp(language, "mx")) {        /* Spanish syntax */
489            return(ast_say_number_full_es(chan, num, ints, language, options, 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") || !strcasecmp(language, "mx")) {        /* Spanish syntax */
516            return(ast_say_number_full_es(chan, num, ints, language, options, -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    1F.gsm: feminine 'una'
791    21.gsm thru 29.gsm, cien.gsm, mil.gsm, millon.gsm, millones.gsm, 100.gsm, 200.gsm, 300.gsm, 400.gsm, 500.gsm, 600.gsm, 700.gsm, 800.gsm, 900.gsm, y.gsm 
792  */
793 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
794 {
795         int res = 0;
796         int playa = 0;
797         int mf = 1;                            /* +1 = Masculin; -1 = Feminin */
798         char fn[256] = "";
799         if (!num) 
800                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
801
802         if (options && !strncasecmp(options, "f",1))
803                 mf = -1;
804
805         while (!res && num) {
806                 if (playa) {
807                         snprintf(fn, sizeof(fn), "digits/y");
808                         playa = 0;
809                 } else if (num == 1) {
810                         if (mf < 0)
811                                 snprintf(fn, sizeof(fn), "digits/%dF", num);
812                         else
813                                 snprintf(fn, sizeof(fn), "digits/%d", num);
814                         num = 0;
815                 } else if (num < 31) {
816                         snprintf(fn, sizeof(fn), "digits/%d", num);
817                         num = 0;
818                 } else if (num < 100) {
819                         snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
820                         num -= ((num/10)*10);
821                         if (num)
822                                 playa++;
823                 } else if (num == 100) {
824                         snprintf(fn, sizeof(fn), "digits/cien");
825                         num = 0;
826                 } else {
827                         if (num < 1000) {
828                                 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
829                                 num -= ((num/100)*100);
830                         } else {
831                                 if (num < 1000000) {
832                                         res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
833                                         if (res)
834                                                 return res;
835                                         num = num % 1000;
836                                         snprintf(fn, sizeof(fn), "digits/mil");
837                                 } else {
838                                         if (num < 2147483640) {
839                                                 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
840                                                 if (res)
841                                                         return res;
842                                                 if ((num/1000000) == 1) {
843                                                         snprintf(fn, sizeof(fn), "digits/millon");
844                                                 } else {
845                                                         snprintf(fn, sizeof(fn), "digits/millones");
846                                                 }
847                                                 num = num % 1000000;
848                                         } else {
849                                                 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
850                                                 res = -1;
851                                         }
852                                 }
853                         }
854                 }
855
856                 if (!res) {
857                         if(!ast_streamfile(chan, fn, language)) {
858                                 if (audiofd && ctrlfd)
859                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
860                                 else
861                                         res = ast_waitstream(chan, ints);
862                         }
863                         ast_stopstream(chan);
864
865                 }
866                         
867         }
868         return res;
869 }
870
871
872 /*--- ast_say_number_full_fr: French syntax */
873 /*      Extra sounds needed:
874         1F: feminin 'une'
875         et: 'and' */
876 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
877 {
878         int res = 0;
879         int playh = 0;
880         int playa = 0;
881         int mf = 1;                            /* +1 = Masculin; -1 = Feminin */
882         char fn[256] = "";
883         if (!num) 
884                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
885         
886         if (options && !strncasecmp(options, "f",1))
887                 mf = -1;
888
889         while(!res && (num || playh || playa)) {
890                 if (playh) {
891                         snprintf(fn, sizeof(fn), "digits/hundred");
892                         playh = 0;
893                 } else if (playa) {
894                         snprintf(fn, sizeof(fn), "digits/et");
895                         playa = 0;
896                 } else if (num == 1) {
897                         if (mf < 0)
898                                 snprintf(fn, sizeof(fn), "digits/%dF", num);
899                         else
900                                 snprintf(fn, sizeof(fn), "digits/%d", num);
901                         num = 0;
902                 } else if (num < 21) {
903                         snprintf(fn, sizeof(fn), "digits/%d", num);
904                         num = 0;
905                 } else if (num < 70) {
906                         snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
907                         if ((num % 10) == 1) playa++;
908                         num = num % 10;
909                 } else if (num < 80) {
910                         snprintf(fn, sizeof(fn), "digits/60");
911                         if ((num % 10) == 1) playa++;
912                         num = num - 60;
913                 } else if (num < 100) {
914                         snprintf(fn, sizeof(fn), "digits/80");
915                         num = num - 80;
916                 } else if (num < 200) {
917                         snprintf(fn, sizeof(fn), "digits/hundred");
918                         num = num - 100;
919                 } else if (num < 1000) {
920                         snprintf(fn, sizeof(fn), "digits/%d", (num/100));
921                         playh++;
922                         num = num % 100;
923                 } else if (num < 2000) {
924                         snprintf(fn, sizeof(fn), "digits/thousand");
925                         num = num - 1000;
926                 } else if (num < 1000000) {
927                         res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
928                         if (res)
929                                 return res;
930                         snprintf(fn, sizeof(fn), "digits/thousand");
931                         num = num % 1000;
932                 } else  if (num < 1000000000) {
933                         res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
934                         if (res)
935                                 return res;
936                         snprintf(fn, sizeof(fn), "digits/million");
937                         num = num % 1000000;
938                 } else {
939                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
940                         res = -1;
941                 }
942                 if (!res) {
943                         if(!ast_streamfile(chan, fn, language)) {
944                                 if (audiofd && ctrlfd)
945                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
946                                 else
947                                         res = ast_waitstream(chan, ints);
948                         }
949                         ast_stopstream(chan);
950                 }
951         }
952         return res;
953 }
954
955 /*--- ast_say_number_full_it:  Italian */
956 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
957 {
958         int res = 0;
959         int playh = 0;
960         int tempnum = 0;
961         char fn[256] = "";
962
963         if (!num)
964                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
965
966                 /*
967                 Italian support
968
969                 Like english, numbers up to 20 are a single 'word', and others
970                 compound, but with exceptions.
971                 For example 21 is not twenty-one, but there is a single word in 'it'.
972                 Idem for 28 (ie when a the 2nd part of a compund number
973                 starts with a vowel)
974
975                 There are exceptions also for hundred, thousand and million.
976                 In english 100 = one hundred, 200 is two hundred.
977                 In italian 100 = cento , like to say hundred (without one),
978                 200 and more are like english.
979                 
980                 Same applies for thousand:
981                 1000 is one thousand in en, 2000 is two thousand.
982                 In it we have 1000 = mille , 2000 = 2 mila 
983
984                 For million(s) we use the plural, if more than one
985                 Also, one million is abbreviated in it, like on-million,
986                 or 'un milione', not 'uno milione'.
987                 So the right file is provided.
988                 */
989
990                 while(!res && (num || playh)) {
991                         if (playh) {
992                                 snprintf(fn, sizeof(fn), "digits/hundred");
993                                 playh = 0;
994                         } else if (num < 20) {
995                                 snprintf(fn, sizeof(fn), "digits/%d", num);
996                                 num = 0;
997                         } else if (num == 21) {
998                                 snprintf(fn, sizeof(fn), "digits/%d", num);
999                                 num = 0;
1000                         } else if (num == 28) {
1001                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1002                                 num = 0;
1003                         } else if (num == 31) {
1004                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1005                                 num = 0;
1006                         } else if (num == 38) {
1007                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1008                                 num = 0;
1009                         } else if (num == 41) {
1010                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1011                                 num = 0;
1012                         } else if (num == 48) {
1013                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1014                                 num = 0;
1015                         } else if (num == 51) {
1016                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1017                                 num = 0;
1018                         } else if (num == 58) {
1019                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1020                                 num = 0;
1021                         } else if (num == 61) {
1022                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1023                                 num = 0;
1024                         } else if (num == 68) {
1025                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1026                                 num = 0;
1027                         } else if (num == 71) {
1028                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1029                                 num = 0;
1030                         } else if (num == 78) {
1031                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1032                                 num = 0;
1033                         } else if (num == 81) {
1034                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1035                                 num = 0;
1036                         } else if (num == 88) {
1037                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1038                                 num = 0;
1039                         } else if (num == 91) {
1040                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1041                                 num = 0;
1042                         } else if (num == 98) {
1043                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1044                                 num = 0;
1045                         } else if (num < 100) {
1046                                 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1047                                 num -= ((num / 10) * 10);
1048                         } else {
1049                                 if (num < 1000) {
1050                                         if ((num / 100) > 1) {
1051                                                 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1052                                                 playh++;
1053                                         } else {
1054                                                 snprintf(fn, sizeof(fn), "digits/hundred");
1055                                         }
1056                                         num -= ((num / 100) * 100);
1057                                 } else {
1058                                         if (num < 1000000) { /* 1,000,000 */
1059                                                 if ((num/1000) > 1)
1060                                                         res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd);
1061                                                 if (res)
1062                                                         return res;
1063                                                 tempnum = num;
1064                                                 num = num % 1000;
1065                                                 if ((tempnum / 1000) < 2)
1066                                                         snprintf(fn, sizeof(fn), "digits/thousand");
1067                                                 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1068                                                         snprintf(fn, sizeof(fn), "digits/thousands");
1069                                         } else {
1070                                                 if (num < 1000000000) { /* 1,000,000,000 */
1071                                                         if ((num / 1000000) > 1)
1072                                                                 res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1073                                                         if (res)
1074                                                                 return res;
1075                                                         tempnum = num;
1076                                                         num = num % 1000000;
1077                                                         if ((tempnum / 1000000) < 2)
1078                                                                 snprintf(fn, sizeof(fn), "digits/million");
1079                                                         else
1080                                                                 snprintf(fn, sizeof(fn), "digits/millions");
1081                                                 } else {
1082                                                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1083                                                         res = -1;
1084                                                 }
1085                                         }
1086                                 }
1087                         }
1088                         if (!res) {
1089                                 if(!ast_streamfile(chan, fn, language)) {
1090                                         if (audiofd && ctrlfd)
1091                                                 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1092                                         else
1093                                                 res = ast_waitstream(chan, ints);
1094                                 }
1095                                 ast_stopstream(chan);
1096                         }
1097                 }
1098         return res;
1099 }
1100
1101 /*--- ast_say_number_full_nl: dutch syntax */
1102 /* New files: digits/nl-en
1103  */
1104 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
1105 {
1106         int res = 0;
1107         int playh = 0;
1108         int units = 0;
1109         char fn[256] = "";
1110         if (!num) 
1111                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1112         while (!res && (num || playh )) {
1113                 if (playh) {
1114                         snprintf(fn, sizeof(fn), "digits/hundred");
1115                         playh = 0;
1116                 } else if (num < 20) {
1117                         snprintf(fn, sizeof(fn), "digits/%d", num);
1118                         num = 0;
1119                 } else if (num < 100) {
1120                         units = num % 10;
1121                         if (units > 0) {
1122                                 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1123                                 if (res)
1124                                         return res;
1125                                 num = num - units;
1126                                 snprintf(fn, sizeof(fn), "digits/nl-en");
1127                         } else {
1128                                 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1129                                 num = 0;
1130                         }
1131                 } else {
1132                         if (num < 1000) {
1133                                 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1134                                 playh++;
1135                                 num -= ((num / 100) * 100);
1136                         } else {
1137                                 if (num < 1000000) { /* 1,000,000 */
1138                                         res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1139                                         if (res)
1140                                                 return res;
1141                                         num = num % 1000;
1142                                         snprintf(fn, sizeof(fn), "digits/thousand");
1143                                 } else {
1144                                         if (num < 1000000000) { /* 1,000,000,000 */
1145                                                 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1146                                                 if (res)
1147                                                         return res;
1148                                                 num = num % 1000000;
1149                                                 snprintf(fn, sizeof(fn), "digits/million");
1150                                         } else {
1151                                                 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1152                                                 res = -1;
1153                                         }
1154                                 }
1155                         }
1156                 }
1157
1158                 if (!res) {
1159                         if(!ast_streamfile(chan, fn, language)) {
1160                                 if (audiofd && ctrlfd)
1161                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1162                                 else
1163                                         res = ast_waitstream(chan, ints);
1164                         }
1165                         ast_stopstream(chan);
1166                 }
1167         }
1168         return res;
1169 }
1170
1171 /* ast_say_number_full_pt: Portuguese syntax */
1172 /*      Extra sounds needed: */
1173 /*      For feminin all sound files end with F */
1174 /*      100E for 100+ something */
1175 /*      1000000S for plural */
1176 /*      pt-e for 'and' */
1177 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1178 {
1179         int res = 0;
1180         int playh = 0;
1181         int mf = 1;                            /* +1 = Masculin; -1 = Feminin */
1182         char fn[256] = "";
1183
1184         if (!num) 
1185                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1186
1187         if (options && !strncasecmp(options, "f",1))
1188                 mf = -1;
1189
1190         while(!res && num ) {
1191                 if (num < 20) {
1192                         if ((num == 1 || num == 2) && (mf < 0))
1193                                 snprintf(fn, sizeof(fn), "digits/%dF", num);
1194                         else
1195                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1196                         num = 0;
1197                 } else if (num < 100) {
1198                         snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1199                         if (num % 10)
1200                                 playh = 1;
1201                         num = num % 10;
1202                 } else if (num < 1000) {
1203                         if (num == 100)
1204                                 snprintf(fn, sizeof(fn), "digits/100");
1205                         else if (num < 200)
1206                                 snprintf(fn, sizeof(fn), "digits/100E");
1207                         else {
1208                                 if (mf < 0 && num > 199)
1209                                         snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1210                                 else
1211                                         snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1212                                 if (num % 100)
1213                                         playh = 1;
1214                         }
1215                         num = num % 100;
1216                 } else if (num < 1000000) {
1217                         if (num > 1999) {
1218                                 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1219                                 if (res)
1220                                         return res;
1221                         }
1222                         snprintf(fn, sizeof(fn), "digits/1000");
1223                         if ((num % 1000) && ((num % 1000) < 100  || !(num % 100)))
1224                                 playh = 1;
1225                         num = num % 1000;
1226                 } else if (num < 1000000000) {
1227                         res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
1228                         if (res)
1229                                 return res;
1230                         if (num < 2000000)
1231                                 snprintf(fn, sizeof(fn), "digits/1000000");
1232                         else
1233                                 snprintf(fn, sizeof(fn), "digits/1000000S");
1234  
1235                         if ((num % 1000000) &&
1236                                 // no thousands
1237                                 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
1238                                 // no hundreds and below
1239                                 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
1240                                 playh = 1;
1241                         num = num % 1000000;
1242                 }
1243                 if (!res && playh) {
1244                         res = wait_file(chan, ints, "digits/pt-e", language);
1245                         ast_stopstream(chan);
1246                         playh = 0;
1247                 }
1248                 if (!res) {
1249                         if(!ast_streamfile(chan, fn, language)) {
1250                                 if (audiofd && ctrlfd)
1251                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);                 else
1252                                         res = ast_waitstream(chan, ints);
1253                         }
1254                         ast_stopstream(chan);
1255                 }
1256         }
1257         return res;
1258 }
1259
1260 /*--- ast_say_number_full_se: Swedish/Norwegian syntax */
1261 static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1262 {
1263         int res = 0;
1264         int playh = 0;
1265         char fn[256] = "";
1266         int cn = 1;             /* +1 = Commune; -1 = Neutrum */
1267         if (!num) 
1268                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1269         if (options && !strncasecmp(options, "n",1)) cn = -1;
1270
1271         while(!res && (num || playh)) {
1272                         if (playh) {
1273                                 snprintf(fn, sizeof(fn), "digits/hundred");
1274                                 playh = 0;
1275                         } else
1276                         if (num < 20) {
1277                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1278                                 num = 0;
1279                         } else
1280                         if (num < 100) {
1281                                 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1282                                 num -= ((num / 10) * 10);
1283                         } else 
1284                         if (num == 1 && cn == -1) {     /* En eller ett? */
1285                                 snprintf(fn, sizeof(fn), "digits/1N");
1286                                 num = 0;
1287                         } else {
1288                                 if (num < 1000){
1289                                         snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1290                                         playh++;
1291                                         num -= ((num / 100) * 100);
1292                                 } else {
1293                                         if (num < 1000000) { /* 1,000,000 */
1294                                                 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1295                                                 if (res)
1296                                                         return res;
1297                                                 num = num % 1000;
1298                                                 snprintf(fn, sizeof(fn), "digits/thousand");
1299                                         } else {
1300                                                 if (num < 1000000000) { /* 1,000,000,000 */
1301                                                         res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1302                                                         if (res)
1303                                                                 return res;
1304                                                         num = num % 1000000;
1305                                                         snprintf(fn, sizeof(fn), "digits/million");
1306                                                 } else {
1307                                                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1308                                                         res = -1;
1309                                                 }
1310                                         }
1311                                 }
1312                         }
1313                          if (!res) {
1314                                 if(!ast_streamfile(chan, fn, language)) {
1315                                     if (audiofd && ctrlfd)
1316                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1317                                     else
1318                                          res = ast_waitstream(chan, ints);
1319                                 }
1320                                 ast_stopstream(chan);
1321
1322                         }
1323                         
1324         }
1325         return res;
1326 }
1327
1328 int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang)
1329 {
1330         if (!strcasecmp(lang,"en") ) {  /* English syntax */
1331                 return(ast_say_date_en(chan, t, ints, lang));
1332         } else if (!strcasecmp(lang, "nl") ) {  /* Dutch syntax */
1333                 return(ast_say_date_nl(chan, t, ints, lang));
1334         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
1335                 return(ast_say_date_pt(chan, t, ints, lang));
1336         }
1337
1338         /* Default to English */
1339         return(ast_say_date_en(chan, t, ints, lang));
1340 }
1341
1342
1343 /* English syntax */
1344 int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
1345 {
1346         struct tm tm;
1347         char fn[256];
1348         int res = 0;
1349         ast_localtime(&t,&tm,NULL);
1350         if (!res) {
1351                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1352                 res = ast_streamfile(chan, fn, lang);
1353                 if (!res)
1354                         res = ast_waitstream(chan, ints);
1355         }
1356         if (!res) {
1357                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1358                 res = ast_streamfile(chan, fn, lang);
1359                 if (!res)
1360                         res = ast_waitstream(chan, ints);
1361         }
1362         if (!res)
1363                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1364         if (!res)
1365                 res = ast_waitstream(chan, ints);
1366         if (!res)
1367                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1368         return res;
1369 }
1370
1371 /* Dutch syntax */
1372 int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
1373 {
1374         struct tm tm;
1375         char fn[256];
1376         int res = 0;
1377         ast_localtime(&t,&tm,NULL);
1378         if (!res) {
1379                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1380                 res = ast_streamfile(chan, fn, lang);
1381                 if (!res)
1382                         res = ast_waitstream(chan, ints);
1383         }
1384         if (!res)
1385                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1386         if (!res) {
1387                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1388                 res = ast_streamfile(chan, fn, lang);
1389                 if (!res)
1390                         res = ast_waitstream(chan, ints);
1391         }
1392         if (!res)
1393                 res = ast_waitstream(chan, ints);
1394         if (!res)
1395                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1396         return res;
1397 }
1398
1399 /* Portuguese syntax */
1400 int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
1401 {
1402         struct tm tm;
1403         char fn[256];
1404         int res = 0;
1405         ast_localtime(&t,&tm,NULL);
1406         localtime_r(&t,&tm);
1407         snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1408         if (!res)
1409                 res = wait_file(chan, ints, fn, lang);
1410         if (!res)
1411                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1412         if (!res)
1413                 res = wait_file(chan, ints, "digits/pt-de", lang);
1414         snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1415         if (!res)
1416                 res = wait_file(chan, ints, fn, lang);
1417         if (!res)
1418                 res = wait_file(chan, ints, "digits/pt-de", lang);
1419         if (!res)
1420                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1421
1422         return res;
1423 }
1424
1425 int ast_say_date_with_format(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1426 {
1427         if (!strcasecmp(lang, "en") ) { /* English syntax */
1428                 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1429         } else if (!strcasecmp(lang, "nl") ) {  /* Dutch syntax */
1430                 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
1431         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
1432                 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
1433         }
1434
1435         /* Default to English */
1436         return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1437 }
1438
1439 /* English syntax */
1440 int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1441 {
1442         struct tm tm;
1443         int res=0, offset, sndoffset;
1444         char sndfile[256], nextmsg[256];
1445
1446         ast_localtime(&time,&tm,timezone);
1447
1448         for (offset=0 ; format[offset] != '\0' ; offset++) {
1449                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1450                 switch (format[offset]) {
1451                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
1452                         case '\'':
1453                                 /* Literal name of a sound file */
1454                                 sndoffset=0;
1455                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1456                                         sndfile[sndoffset] = format[offset];
1457                                 sndfile[sndoffset] = '\0';
1458                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1459                                 res = wait_file(chan,ints,nextmsg,lang);
1460                                 break;
1461                         case 'A':
1462                         case 'a':
1463                                 /* Sunday - Saturday */
1464                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1465                                 res = wait_file(chan,ints,nextmsg,lang);
1466                                 break;
1467                         case 'B':
1468                         case 'b':
1469                         case 'h':
1470                                 /* January - December */
1471                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1472                                 res = wait_file(chan,ints,nextmsg,lang);
1473                                 break;
1474                         case 'd':
1475                         case 'e':
1476                                 /* First - Thirtyfirst */
1477                                 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1478                                         snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
1479                                         res = wait_file(chan,ints,nextmsg,lang);
1480                                 } else if (tm.tm_mday == 31) {
1481                                         /* "Thirty" and "first" */
1482                                         res = wait_file(chan,ints, "digits/30",lang);
1483                                         if (!res) {
1484                                                 res = wait_file(chan,ints, "digits/h-1",lang);
1485                                         }
1486                                 } else {
1487                                         /* Between 21 and 29 - two sounds */
1488                                         res = wait_file(chan,ints, "digits/20",lang);
1489                                         if (!res) {
1490                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
1491                                                 res = wait_file(chan,ints,nextmsg,lang);
1492                                         }
1493                                 }
1494                                 break;
1495                         case 'Y':
1496                                 /* Year */
1497                                 if (tm.tm_year > 99) {
1498                                         res = wait_file(chan,ints, "digits/2",lang);
1499                                         if (!res) {
1500                                                 res = wait_file(chan,ints, "digits/thousand",lang);
1501                                         }
1502                                         if (tm.tm_year > 100) {
1503                                                 if (!res) {
1504                                                         /* This works until the end of 2020 */
1505                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1506                                                         res = wait_file(chan,ints,nextmsg,lang);
1507                                                 }
1508                                         }
1509                                 } else {
1510                                         if (tm.tm_year < 1) {
1511                                                 /* I'm not going to handle 1900 and prior */
1512                                                 /* We'll just be silent on the year, instead of bombing out. */
1513                                         } else {
1514                                                 res = wait_file(chan,ints, "digits/19",lang);
1515                                                 if (!res) {
1516                                                         if (tm.tm_year <= 9) {
1517                                                                 /* 1901 - 1909 */
1518                                                                 res = wait_file(chan,ints, "digits/oh",lang);
1519                                                                 if (!res) {
1520                                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1521                                                                         res = wait_file(chan,ints,nextmsg,lang);
1522                                                                 }
1523                                                         } else if (tm.tm_year <= 20) {
1524                                                                 /* 1910 - 1920 */
1525                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1526                                                                 res = wait_file(chan,ints,nextmsg,lang);
1527                                                         } else {
1528                                                                 /* 1921 - 1999 */
1529                                                                 int ten, one;
1530                                                                 ten = tm.tm_year / 10;
1531                                                                 one = tm.tm_year % 10;
1532                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1533                                                                 res = wait_file(chan,ints,nextmsg,lang);
1534                                                                 if (!res) {
1535                                                                         if (one != 0) {
1536                                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1537                                                                                 res = wait_file(chan,ints,nextmsg,lang);
1538                                                                         }
1539                                                                 }
1540                                                         }
1541                                                 }
1542                                         }
1543                                 }
1544                                 break;
1545                         case 'I':
1546                         case 'l':
1547                                 /* 12-Hour */
1548                                 if (tm.tm_hour == 0)
1549                                         snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1550                                 else if (tm.tm_hour > 12)
1551                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1552                                 else
1553                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1554                                 res = wait_file(chan,ints,nextmsg,lang);
1555                                 break;
1556                         case 'H':
1557                         case 'k':
1558                                 /* 24-Hour */
1559                                 if (format[offset] == 'H') {
1560                                         /* e.g. oh-eight */
1561                                         if (tm.tm_hour < 10) {
1562                                                 res = wait_file(chan,ints, "digits/oh",lang);
1563                                         }
1564                                 } else {
1565                                         /* e.g. eight */
1566                                         if (tm.tm_hour == 0) {
1567                                                 res = wait_file(chan,ints, "digits/oh",lang);
1568                                         }
1569                                 }
1570                                 if (!res) {
1571                                         if (tm.tm_hour != 0) {
1572                                                 int remainder = tm.tm_hour;
1573                                                 if (tm.tm_hour > 20) {
1574                                                         res = wait_file(chan,ints, "digits/20",lang);
1575                                                         remainder -= 20;
1576                                                 }
1577                                                 if (!res) {
1578                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1579                                                         res = wait_file(chan,ints,nextmsg,lang);
1580                                                 }
1581                                         }
1582                                 }
1583                                 break;
1584                         case 'M':
1585                                 /* Minute */
1586                                 if (tm.tm_min == 0) {
1587                                         res = wait_file(chan,ints, "digits/oclock",lang);
1588                                 } else if (tm.tm_min < 10) {
1589                                         res = wait_file(chan,ints, "digits/oh",lang);
1590                                         if (!res) {
1591                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1592                                                 res = wait_file(chan,ints,nextmsg,lang);
1593                                         }
1594                                 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
1595                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1596                                         res = wait_file(chan,ints,nextmsg,lang);
1597                                 } else {
1598                                         int ten, one;
1599                                         ten = (tm.tm_min / 10) * 10;
1600                                         one = (tm.tm_min % 10);
1601                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1602                                         res = wait_file(chan,ints,nextmsg,lang);
1603                                         if (!res) {
1604                                                 /* Fifty, not fifty-zero */
1605                                                 if (one != 0) {
1606                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1607                                                         res = wait_file(chan,ints,nextmsg,lang);
1608                                                 }
1609                                         }
1610                                 }
1611                                 break;
1612                         case 'P':
1613                         case 'p':
1614                                 /* AM/PM */
1615                                 if (tm.tm_hour > 11)
1616                                         snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1617                                 else
1618                                         snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1619                                 res = wait_file(chan,ints,nextmsg,lang);
1620                                 break;
1621                         case 'Q':
1622                                 /* Shorthand for "Today", "Yesterday", or ABdY */
1623                                 {
1624                                         struct timeval now;
1625                                         struct tm tmnow;
1626                                         time_t beg_today;
1627
1628                                         gettimeofday(&now,NULL);
1629                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1630                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1631                                         /* In any case, it saves not having to do ast_mktime() */
1632                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1633                                         if (beg_today < time) {
1634                                                 /* Today */
1635                                                 res = wait_file(chan,ints, "digits/today",lang);
1636                                         } else if (beg_today - 86400 < time) {
1637                                                 /* Yesterday */
1638                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1639                                         } else {
1640                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1641                                         }
1642                                 }
1643                                 break;
1644                         case 'q':
1645                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1646                                 {
1647                                         struct timeval now;
1648                                         struct tm tmnow;
1649                                         time_t beg_today;
1650
1651                                         gettimeofday(&now,NULL);
1652                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1653                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1654                                         /* In any case, it saves not having to do ast_mktime() */
1655                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1656                                         if (beg_today < time) {
1657                                                 /* Today */
1658                                         } else if ((beg_today - 86400) < time) {
1659                                                 /* Yesterday */
1660                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1661                                         } else if (beg_today - 86400 * 6 < time) {
1662                                                 /* Within the last week */
1663                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1664                                         } else {
1665                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1666                                         }
1667                                 }
1668                                 break;
1669                         case 'R':
1670                                 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1671                                 break;
1672                         case 'S':
1673                                 /* Seconds */
1674                                 if (tm.tm_sec == 0) {
1675                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1676                                         res = wait_file(chan,ints,nextmsg,lang);
1677                                 } else if (tm.tm_sec < 10) {
1678                                         res = wait_file(chan,ints, "digits/oh",lang);
1679                                         if (!res) {
1680                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1681                                                 res = wait_file(chan,ints,nextmsg,lang);
1682                                         }
1683                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1684                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1685                                         res = wait_file(chan,ints,nextmsg,lang);
1686                                 } else {
1687                                         int ten, one;
1688                                         ten = (tm.tm_sec / 10) * 10;
1689                                         one = (tm.tm_sec % 10);
1690                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1691                                         res = wait_file(chan,ints,nextmsg,lang);
1692                                         if (!res) {
1693                                                 /* Fifty, not fifty-zero */
1694                                                 if (one != 0) {
1695                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1696                                                         res = wait_file(chan,ints,nextmsg,lang);
1697                                                 }
1698                                         }
1699                                 }
1700                                 break;
1701                         case 'T':
1702                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1703                                 break;
1704                         case ' ':
1705                         case '  ':
1706                                 /* Just ignore spaces and tabs */
1707                                 break;
1708                         default:
1709                                 /* Unknown character */
1710                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1711                 }
1712                 /* Jump out on DTMF */
1713                 if (res) {
1714                         break;
1715                 }
1716         }
1717         return res;
1718 }
1719
1720 /* Dutch syntax */
1721 int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1722 {
1723         struct tm tm;
1724         int res=0, offset, sndoffset;
1725         char sndfile[256], nextmsg[256];
1726
1727         ast_localtime(&time,&tm,timezone);
1728
1729         for (offset=0 ; format[offset] != '\0' ; offset++) {
1730                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1731                 switch (format[offset]) {
1732                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
1733                         case '\'':
1734                                 /* Literal name of a sound file */
1735                                 sndoffset=0;
1736                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1737                                         sndfile[sndoffset] = format[offset];
1738                                 sndfile[sndoffset] = '\0';
1739                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1740                                 res = wait_file(chan,ints,nextmsg,lang);
1741                                 break;
1742                         case 'A':
1743                         case 'a':
1744                                 /* Sunday - Saturday */
1745                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1746                                 res = wait_file(chan,ints,nextmsg,lang);
1747                                 break;
1748                         case 'B':
1749                         case 'b':
1750                         case 'h':
1751                                 /* January - December */
1752                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1753                                 res = wait_file(chan,ints,nextmsg,lang);
1754                                 break;
1755                         case 'd':
1756                         case 'e':
1757                                 /* First - Thirtyfirst */
1758                                 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
1759                                 break;
1760                         case 'Y':
1761                                 /* Year */
1762                                 if (tm.tm_year > 99) {
1763                                         res = wait_file(chan,ints, "digits/2",lang);
1764                                         if (!res) {
1765                                                 res = wait_file(chan,ints, "digits/thousand",lang);
1766                                         }
1767                                         if (tm.tm_year > 100) {
1768                                                 if (!res) {
1769                                                         /* This works until the end of 2020 */
1770                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1771                                                         res = wait_file(chan,ints,nextmsg,lang);
1772                                                 }
1773                                         }
1774                                 } else {
1775                                         if (tm.tm_year < 1) {
1776                                                 /* I'm not going to handle 1900 and prior */
1777                                                 /* We'll just be silent on the year, instead of bombing out. */
1778                                         } else {
1779                                                 res = wait_file(chan,ints, "digits/19",lang);
1780                                                 if (!res) {
1781                                                         if (tm.tm_year <= 9) {
1782                                                                 /* 1901 - 1909 */
1783                                                                 res = wait_file(chan,ints, "digits/oh",lang);
1784                                                                 if (!res) {
1785                                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1786                                                                         res = wait_file(chan,ints,nextmsg,lang);
1787                                                                 }
1788                                                         } else if (tm.tm_year <= 20) {
1789                                                                 /* 1910 - 1920 */
1790                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1791                                                                 res = wait_file(chan,ints,nextmsg,lang);
1792                                                         } else {
1793                                                                 /* 1921 - 1999 */
1794                                                                 int ten, one;
1795                                                                 ten = tm.tm_year / 10;
1796                                                                 one = tm.tm_year % 10;
1797                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1798                                                                 res = wait_file(chan,ints,nextmsg,lang);
1799                                                                 if (!res) {
1800                                                                         if (one != 0) {
1801                                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1802                                                                                 res = wait_file(chan,ints,nextmsg,lang);
1803                                                                         }
1804                                                                 }
1805                                                         }
1806                                                 }
1807                                         }
1808                                 }
1809                                 break;
1810                         case 'I':
1811                         case 'l':
1812                                 /* 12-Hour */
1813                                 if (tm.tm_hour == 0)
1814                                         snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1815                                 else if (tm.tm_hour > 12)
1816                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1817                                 else
1818                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1819                                 res = wait_file(chan,ints,nextmsg,lang);
1820                                 break;
1821                         case 'H':
1822                         case 'k':
1823                                 /* 24-Hour */
1824                                 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
1825                                 if (!res) {
1826                                         res = wait_file(chan,ints, "digits/nl-uur",lang);
1827                                 }
1828                                 break;
1829                         case 'M':
1830                                 /* Minute */
1831                                 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
1832                                 break;
1833                         case 'P':
1834                         case 'p':
1835                                 /* AM/PM */
1836                                 if (tm.tm_hour > 11)
1837                                         snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1838                                 else
1839                                         snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1840                                 res = wait_file(chan,ints,nextmsg,lang);
1841                                 break;
1842                         case 'Q':
1843                                 /* Shorthand for "Today", "Yesterday", or ABdY */
1844                                 {
1845                                         struct timeval now;
1846                                         struct tm tmnow;
1847                                         time_t beg_today;
1848
1849                                         gettimeofday(&now,NULL);
1850                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1851                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1852                                         /* In any case, it saves not having to do ast_mktime() */
1853                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1854                                         if (beg_today < time) {
1855                                                 /* Today */
1856                                                 res = wait_file(chan,ints, "digits/today",lang);
1857                                         } else if (beg_today - 86400 < time) {
1858                                                 /* Yesterday */
1859                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1860                                         } else {
1861                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1862                                         }
1863                                 }
1864                                 break;
1865                         case 'q':
1866                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1867                                 {
1868                                         struct timeval now;
1869                                         struct tm tmnow;
1870                                         time_t beg_today;
1871
1872                                         gettimeofday(&now,NULL);
1873                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1874                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1875                                         /* In any case, it saves not having to do ast_mktime() */
1876                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1877                                         if (beg_today < time) {
1878                                                 /* Today */
1879                                         } else if ((beg_today - 86400) < time) {
1880                                                 /* Yesterday */
1881                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1882                                         } else if (beg_today - 86400 * 6 < time) {
1883                                                 /* Within the last week */
1884                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1885                                         } else {
1886                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1887                                         }
1888                                 }
1889                                 break;
1890                         case 'R':
1891                                 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1892                                 break;
1893                         case 'S':
1894                                 /* Seconds */
1895                                 if (tm.tm_sec == 0) {
1896                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1897                                         res = wait_file(chan,ints,nextmsg,lang);
1898                                 } else if (tm.tm_sec < 10) {
1899                                         res = wait_file(chan,ints, "digits/oh",lang);
1900                                         if (!res) {
1901                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1902                                                 res = wait_file(chan,ints,nextmsg,lang);
1903                                         }
1904                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1905                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1906                                         res = wait_file(chan,ints,nextmsg,lang);
1907                                 } else {
1908                                         int ten, one;
1909                                         ten = (tm.tm_sec / 10) * 10;
1910                                         one = (tm.tm_sec % 10);
1911                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1912                                         res = wait_file(chan,ints,nextmsg,lang);
1913                                         if (!res) {
1914                                                 /* Fifty, not fifty-zero */
1915                                                 if (one != 0) {
1916                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1917                                                         res = wait_file(chan,ints,nextmsg,lang);
1918                                                 }
1919                                         }
1920                                 }
1921                                 break;
1922                         case 'T':
1923                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1924                                 break;
1925                         case ' ':
1926                         case '  ':
1927                                 /* Just ignore spaces and tabs */
1928                                 break;
1929                         default:
1930                                 /* Unknown character */
1931                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1932                 }
1933                 /* Jump out on DTMF */
1934                 if (res) {
1935                         break;
1936                 }
1937         }
1938         return res;
1939 }
1940
1941 /* Portuguese syntax */
1942 int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1943 {
1944         struct tm tm;
1945         int res=0, offset, sndoffset;
1946         char sndfile[256], nextmsg[256];
1947
1948         ast_localtime(&time,&tm,timezone);
1949
1950         for (offset=0 ; format[offset] != '\0' ; offset++) {
1951                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1952                 switch (format[offset]) {
1953                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
1954                         case '\'':
1955                                 /* Literal name of a sound file */
1956                                 sndoffset=0;
1957                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1958                                         sndfile[sndoffset] = format[offset];
1959                                 sndfile[sndoffset] = '\0';
1960                                 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
1961                                 res = wait_file(chan,ints,nextmsg,lang);
1962                                 break;
1963                         case 'A':
1964                         case 'a':
1965                                 /* Sunday - Saturday */
1966                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1967                                 res = wait_file(chan,ints,nextmsg,lang);
1968                                 break;
1969                         case 'B':
1970                         case 'b':
1971                         case 'h':
1972                                 /* January - December */
1973                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1974                                 res = wait_file(chan,ints,nextmsg,lang);
1975                                 break;
1976                         case 'd':
1977                         case 'e':
1978                                 /* First - Thirtyfirst */
1979                                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1980                                 break;
1981                         case 'Y':
1982                                 /* Year */
1983                                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1984                                 break;
1985                         case 'I':
1986                         case 'l':
1987                                 /* 12-Hour */
1988                                 if (tm.tm_hour == 0) {
1989                                         if (format[offset] == 'I')
1990                                                 res = wait_file(chan, ints, "digits/pt-ah", lang);
1991                                         if (!res)
1992                                                 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
1993                                 }
1994                                 else if (tm.tm_hour == 12) {
1995                                         if (format[offset] == 'I')
1996                                                 res = wait_file(chan, ints, "digits/pt-ao", lang);
1997                                         if (!res)
1998                                                 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
1999                                 }
2000                                 else {
2001                                         if (format[offset] == 'I') {
2002                                                 res = wait_file(chan, ints, "digits/pt-ah", lang);
2003                                                 if ((tm.tm_hour % 12) != 1)
2004                                                         if (!res)
2005                                                                 res = wait_file(chan, ints, "digits/pt-sss", lang);
2006                                         }
2007                                         if (!res)
2008                                                 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
2009                                 }
2010                                 break;
2011                         case 'H':
2012                         case 'k':
2013                                 /* 24-Hour */
2014                                 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
2015                                 if (!res) {
2016                                         if (tm.tm_hour != 0) {
2017                                                 int remainder = tm.tm_hour;
2018                                                 if (tm.tm_hour > 20) {
2019                                                         res = wait_file(chan,ints, "digits/20",lang);
2020                                                         remainder -= 20;
2021                                                 }
2022                                                 if (!res) {
2023                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
2024                                                         res = wait_file(chan,ints,nextmsg,lang);
2025                                                 }
2026                                         }
2027                                 }
2028                                 break;
2029                         case 'M':
2030                                 /* Minute */
2031                                 if (tm.tm_min == 0) {
2032                                         res = wait_file(chan, ints, "digits/pt-hora", lang);
2033                                         if (tm.tm_hour != 1)
2034                                                 if (!res)
2035                                                         res = wait_file(chan, ints, "digits/pt-sss", lang);                     } else {
2036                                         res = wait_file(chan,ints,"digits/pt-e",lang);
2037                                         if (!res)
2038                                                 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);       
2039                                 }
2040                                 break;
2041                         case 'P':
2042                         case 'p':
2043                                 /* AM/PM */
2044                                 if (tm.tm_hour > 12)
2045                                         res = wait_file(chan, ints, "digits/p-m", lang);
2046                                 else if (tm.tm_hour  && tm.tm_hour < 12)
2047                                         res = wait_file(chan, ints, "digits/a-m", lang);
2048                                 break;
2049                         case 'Q':
2050                                 /* Shorthand for "Today", "Yesterday", or ABdY */
2051                                 {
2052                                         struct timeval now;
2053                                         struct tm tmnow;
2054                                         time_t beg_today;
2055
2056                                         gettimeofday(&now,NULL);
2057                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2058                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2059                                         /* In any case, it saves not having to do ast_mktime() */
2060                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2061                                         if (beg_today < time) {
2062                                                 /* Today */
2063                                                 res = wait_file(chan,ints, "digits/today",lang);
2064                                         } else if (beg_today - 86400 < time) {
2065                                                 /* Yesterday */
2066                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2067                                         } else {
2068                                                 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
2069                                         }
2070                                 }
2071                                 break;
2072                         case 'q':
2073                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2074                                 {
2075                                         struct timeval now;
2076                                         struct tm tmnow;
2077                                         time_t beg_today;
2078
2079                                         gettimeofday(&now,NULL);
2080                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2081                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2082                                         /* In any case, it saves not having to do ast_mktime() */
2083                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2084                                         if (beg_today < time) {
2085                                                 /* Today */
2086                                         } else if ((beg_today - 86400) < time) {
2087                                                 /* Yesterday */
2088                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2089                                         } else if (beg_today - 86400 * 6 < time) {
2090                                                 /* Within the last week */
2091                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2092                                         } else {
2093                                                 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
2094                                         }
2095                                 }
2096                                 break;
2097                         case 'R':
2098                                 res = ast_say_date_with_format(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
2099                                 break;
2100                         case 'S':
2101                                 /* Seconds */
2102                                 if (tm.tm_sec == 0) {
2103                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2104                                         res = wait_file(chan,ints,nextmsg,lang);
2105                                 } else if (tm.tm_sec < 10) {
2106                                         res = wait_file(chan,ints, "digits/oh",lang);
2107                                         if (!res) {
2108                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2109                                                 res = wait_file(chan,ints,nextmsg,lang);
2110                                         }
2111                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2112                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2113                                         res = wait_file(chan,ints,nextmsg,lang);
2114                                 } else {
2115                                         int ten, one;
2116                                         ten = (tm.tm_sec / 10) * 10;
2117                                         one = (tm.tm_sec % 10);
2118                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2119                                         res = wait_file(chan,ints,nextmsg,lang);
2120                                         if (!res) {
2121                                                 /* Fifty, not fifty-zero */
2122                                                 if (one != 0) {
2123                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2124                                                         res = wait_file(chan,ints,nextmsg,lang);
2125                                                 }
2126                                         }
2127                                 }
2128                                 break;
2129                         case 'T':
2130                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2131                                 break;
2132                         case ' ':
2133                         case '  ':
2134                                 /* Just ignore spaces and tabs */
2135                                 break;
2136                         default:
2137                                 /* Unknown character */
2138                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2139                 }
2140                 /* Jump out on DTMF */
2141                 if (res) {
2142                         break;
2143                 }
2144         }
2145         return res;
2146 }
2147
2148 int ast_say_time(struct ast_channel *chan, time_t t, char *ints, char *lang)
2149 {
2150         if (!strcasecmp(lang, "en") ) { /* English syntax */
2151                 return(ast_say_time_en(chan, t, ints, lang));
2152         } else if (!strcasecmp(lang, "nl") ) {  /* Dutch syntax */
2153                 return(ast_say_time_nl(chan, t, ints, lang));
2154         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
2155                 return(ast_say_time_pt(chan, t, ints, lang));
2156         }
2157
2158         /* Default to English */
2159         return(ast_say_time_en(chan, t, ints, lang));
2160 }
2161
2162 /* English syntax */
2163 int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2164 {
2165         struct tm tm;
2166         int res = 0;
2167         int hour, pm=0;
2168         localtime_r(&t,&tm);
2169         hour = tm.tm_hour;
2170         if (!hour)
2171                 hour = 12;
2172         else if (hour == 12)
2173                 pm = 1;
2174         else if (hour > 12) {
2175                 hour -= 12;
2176                 pm = 1;
2177         }
2178         if (!res)
2179                 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2180
2181         if (tm.tm_min > 9) {
2182                 if (!res)
2183                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2184         } else if (tm.tm_min) {
2185                 if (!res)
2186                         res = ast_streamfile(chan, "digits/oh", lang);
2187                 if (!res)
2188                         res = ast_waitstream(chan, ints);
2189                 if (!res)
2190                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2191         } else {
2192                 if (!res)
2193                         res = ast_streamfile(chan, "digits/oclock", lang);
2194                 if (!res)
2195                         res = ast_waitstream(chan, ints);
2196         }
2197         if (pm) {
2198                 if (!res)
2199                         res = ast_streamfile(chan, "digits/p-m", lang);
2200         } else {
2201                 if (!res)
2202                         res = ast_streamfile(chan, "digits/a-m", lang);
2203         }
2204         if (!res)
2205                 res = ast_waitstream(chan, ints);
2206         return res;
2207 }
2208
2209 /* Dutch syntax */
2210 int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
2211 {
2212         struct tm tm;
2213         int res = 0;
2214         int hour;
2215         localtime_r(&t,&tm);
2216         hour = tm.tm_hour;
2217         if (!res)
2218                 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2219
2220         if (!res)
2221                 res = ast_streamfile(chan, "digits/nl-uur", lang);
2222         if (!res)
2223                 res = ast_waitstream(chan, ints);
2224         if (!res)
2225             if (tm.tm_min > 0) 
2226                 res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
2227         return res;
2228 }
2229
2230 /* Portuguese syntax */
2231 int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2232 {
2233         struct tm tm;
2234         int res = 0;
2235         int hour;
2236         localtime_r(&t,&tm);
2237         hour = tm.tm_hour;
2238         if (!res)
2239                 res = ast_say_number(chan, hour, ints, lang, "f");
2240         if (tm.tm_min) {
2241                 if (!res)
2242                         res = wait_file(chan, ints, "digits/pt-e", lang);
2243                 if (!res)
2244                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2245         } else {
2246                 if (!res)
2247                         res = wait_file(chan, ints, "digits/pt-hora", lang);
2248                 if (tm.tm_hour != 1)
2249                         if (!res)
2250                                 res = wait_file(chan, ints, "digits/pt-sss", lang);
2251         }
2252         if (!res)
2253                 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2254         return res;
2255 }
2256
2257 int ast_say_datetime(struct ast_channel *chan, time_t t, char *ints, char *lang)
2258 {
2259         if (!strcasecmp(lang, "en") ) { /* English syntax */
2260                 return(ast_say_datetime_en(chan, t, ints, lang));
2261         } else if (!strcasecmp(lang, "nl") ) {  /* Dutch syntax */
2262                 return(ast_say_datetime_nl(chan, t, ints, lang));
2263         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
2264                 return(ast_say_datetime_pt(chan, t, ints, lang));
2265         }
2266
2267         /* Default to English */
2268         return(ast_say_datetime_en(chan, t, ints, lang));
2269 }
2270
2271 /* English syntax */
2272 int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2273 {
2274         struct tm tm;
2275         char fn[256];
2276         int res = 0;
2277         int hour, pm=0;
2278         localtime_r(&t,&tm);
2279         if (!res) {
2280                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2281                 res = ast_streamfile(chan, fn, lang);
2282                 if (!res)
2283                         res = ast_waitstream(chan, ints);
2284         }
2285         if (!res) {
2286                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2287                 res = ast_streamfile(chan, fn, lang);
2288                 if (!res)
2289                         res = ast_waitstream(chan, ints);
2290         }
2291         if (!res)
2292                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2293
2294         hour = tm.tm_hour;
2295         if (!hour)
2296                 hour = 12;
2297         else if (hour == 12)
2298                 pm = 1;
2299         else if (hour > 12) {
2300                 hour -= 12;
2301                 pm = 1;
2302         }
2303         if (!res)
2304                 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2305
2306         if (tm.tm_min > 9) {
2307                 if (!res)
2308                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2309         } else if (tm.tm_min) {
2310                 if (!res)
2311                         res = ast_streamfile(chan, "digits/oh", lang);
2312                 if (!res)
2313                         res = ast_waitstream(chan, ints);
2314                 if (!res)
2315                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2316         } else {
2317                 if (!res)
2318                         res = ast_streamfile(chan, "digits/oclock", lang);
2319                 if (!res)
2320                         res = ast_waitstream(chan, ints);
2321         }
2322         if (pm) {
2323                 if (!res)
2324                         res = ast_streamfile(chan, "digits/p-m", lang);
2325         } else {
2326                 if (!res)
2327                         res = ast_streamfile(chan, "digits/a-m", lang);
2328         }
2329         if (!res)
2330                 res = ast_waitstream(chan, ints);
2331         if (!res)
2332                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2333         return res;
2334 }
2335
2336 /* Dutch syntax */
2337 int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
2338 {
2339         struct tm tm;
2340         int res = 0;
2341         localtime_r(&t,&tm);
2342         res = ast_say_date(chan, t, ints, lang);
2343         if (!res) {
2344                 res = ast_streamfile(chan, "digits/nl-om", lang);
2345                 if (!res)
2346                         res = ast_waitstream(chan, ints);
2347         }
2348         if (!res) 
2349                 ast_say_time(chan, t, ints, lang);
2350         return res;
2351 }
2352
2353 /* Portuguese syntax */
2354 int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2355 {
2356         struct tm tm;
2357         char fn[256];
2358         int res = 0;
2359         int hour, pm=0;
2360         localtime_r(&t,&tm);
2361         if (!res) {
2362                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2363                 res = ast_streamfile(chan, fn, lang);
2364                 if (!res)
2365                         res = ast_waitstream(chan, ints);
2366         }
2367         if (!res) {
2368                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2369                 res = ast_streamfile(chan, fn, lang);
2370                 if (!res)
2371                         res = ast_waitstream(chan, ints);
2372         }
2373         if (!res)
2374                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2375
2376         hour = tm.tm_hour;
2377         if (!hour)
2378                 hour = 12;
2379         else if (hour == 12)
2380                 pm = 1;
2381         else if (hour > 12) {
2382                 hour -= 12;
2383                 pm = 1;
2384         }
2385         if (!res)
2386                 res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
2387
2388         if (tm.tm_min > 9) {
2389                 if (!res)
2390                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2391         } else if (tm.tm_min) {
2392                 if (!res)
2393                         res = ast_streamfile(chan, "digits/oh", lang);
2394                 if (!res)
2395                         res = ast_waitstream(chan, ints);
2396                 if (!res)
2397                         res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2398         } else {
2399                 if (!res)
2400                         res = ast_streamfile(chan, "digits/oclock", lang);
2401                 if (!res)
2402                         res = ast_waitstream(chan, ints);
2403         }
2404         if (pm) {
2405                 if (!res)
2406                         res = ast_streamfile(chan, "digits/p-m", lang);
2407         } else {
2408                 if (!res)
2409                         res = ast_streamfile(chan, "digits/a-m", lang);
2410         }
2411         if (!res)
2412                 res = ast_waitstream(chan, ints);
2413         if (!res)
2414                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2415         return res;
2416 }
2417
2418 int ast_say_datetime_from_now(struct ast_channel *chan, time_t t, char *ints, char *lang)
2419 {
2420         if (!strcasecmp(lang, "en") ) { /* English syntax */
2421                 return(ast_say_datetime_from_now_en(chan, t, ints, lang));
2422         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
2423                 return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
2424         }
2425
2426         /* Default to English */
2427         return(ast_say_datetime_from_now_en(chan, t, ints, lang));
2428 }
2429
2430 /* English syntax */
2431 int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
2432 {
2433         int res=0;
2434         time_t nowt;
2435         int daydiff;
2436         struct tm tm;
2437         struct tm now;
2438         char fn[256];
2439
2440         time(&nowt);
2441
2442         localtime_r(&t,&tm);
2443         localtime_r(&nowt,&now);
2444         daydiff = now.tm_yday - tm.tm_yday;
2445         if ((daydiff < 0) || (daydiff > 6)) {
2446                 /* Day of month and month */
2447                 if (!res) {
2448                         snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2449                         res = ast_streamfile(chan, fn, lang);
2450                         if (!res)
2451                                 res = ast_waitstream(chan, ints);
2452                 }
2453                 if (!res)
2454                         res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2455
2456         } else if (daydiff) {
2457                 /* Just what day of the week */
2458                 if (!res) {
2459                         snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2460                         res = ast_streamfile(chan, fn, lang);
2461                         if (!res)
2462                                 res = ast_waitstream(chan, ints);
2463                 }
2464         } /* Otherwise, it was today */
2465         if (!res)
2466                 res = ast_say_time(chan, t, ints, lang);
2467         return res;
2468 }
2469
2470 /* Portuguese syntax */
2471 int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
2472 {
2473         int res=0;
2474         time_t nowt;
2475         int daydiff;
2476         struct tm tm;
2477         struct tm now;
2478         char fn[256];
2479
2480         time(&nowt);
2481
2482         localtime_r(&t,&tm);
2483         localtime_r(&nowt,&now);
2484         daydiff = now.tm_yday - tm.tm_yday;
2485         if ((daydiff < 0) || (daydiff > 6)) {
2486                 /* Day of month and month */
2487                 if (!res)
2488                         res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2489                 if (!res)
2490                         res = wait_file(chan, ints, "digits/pt-de", lang);
2491                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
2492                 if (!res)
2493                         res = wait_file(chan, ints, fn, lang);
2494         
2495         } else if (daydiff) {
2496                 /* Just what day of the week */
2497                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
2498                 if (!res)
2499                         res = wait_file(chan, ints, fn, lang);
2500         }       /* Otherwise, it was today */
2501         snprintf(fn, sizeof(fn), "digits/pt-ah");
2502         if (!res)
2503                 res = wait_file(chan, ints, fn, lang);
2504         if (tm.tm_hour != 1)
2505         if (!res)
2506                 res = wait_file(chan, ints, "digits/pt-sss", lang);
2507         if (!res)
2508                 res = ast_say_time(chan, t, ints, lang);
2509         return res;
2510 }