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