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