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