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