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