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