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