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