First pass at properly handling account codes in forwarding
[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                                 res = wait_file(chan,ints,sndfile,lang);
1840                                 break;
1841                         case 'A':
1842                         case 'a':
1843                                 /* Sunday - Saturday */
1844                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1845                                 res = wait_file(chan,ints,nextmsg,lang);
1846                                 break;
1847                         case 'B':
1848                         case 'b':
1849                         case 'h':
1850                                 /* January - December */
1851                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1852                                 res = wait_file(chan,ints,nextmsg,lang);
1853                                 break;
1854                         case 'd':
1855                         case 'e':
1856                                 /* First - Thirtyfirst */
1857                                 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1858                                         snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
1859                                         res = wait_file(chan,ints,nextmsg,lang);
1860                                 } else if (tm.tm_mday == 31) {
1861                                         /* "Thirty" and "first" */
1862                                         res = wait_file(chan,ints, "digits/30",lang);
1863                                         if (!res) {
1864                                                 res = wait_file(chan,ints, "digits/h-1",lang);
1865                                         }
1866                                 } else {
1867                                         /* Between 21 and 29 - two sounds */
1868                                         res = wait_file(chan,ints, "digits/20",lang);
1869                                         if (!res) {
1870                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
1871                                                 res = wait_file(chan,ints,nextmsg,lang);
1872                                         }
1873                                 }
1874                                 break;
1875                         case 'Y':
1876                                 /* Year */
1877                                 if (tm.tm_year > 99) {
1878                                         res = wait_file(chan,ints, "digits/2",lang);
1879                                         if (!res) {
1880                                                 res = wait_file(chan,ints, "digits/thousand",lang);
1881                                         }
1882                                         if (tm.tm_year > 100) {
1883                                                 if (!res) {
1884                                                         /* This works until the end of 2020 */
1885                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1886                                                         res = wait_file(chan,ints,nextmsg,lang);
1887                                                 }
1888                                         }
1889                                 } else {
1890                                         if (tm.tm_year < 1) {
1891                                                 /* I'm not going to handle 1900 and prior */
1892                                                 /* We'll just be silent on the year, instead of bombing out. */
1893                                         } else {
1894                                                 res = wait_file(chan,ints, "digits/19",lang);
1895                                                 if (!res) {
1896                                                         if (tm.tm_year <= 9) {
1897                                                                 /* 1901 - 1909 */
1898                                                                 res = wait_file(chan,ints, "digits/oh",lang);
1899                                                                 if (!res) {
1900                                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1901                                                                         res = wait_file(chan,ints,nextmsg,lang);
1902                                                                 }
1903                                                         } else if (tm.tm_year <= 20) {
1904                                                                 /* 1910 - 1920 */
1905                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1906                                                                 res = wait_file(chan,ints,nextmsg,lang);
1907                                                         } else {
1908                                                                 /* 1921 - 1999 */
1909                                                                 int ten, one;
1910                                                                 ten = tm.tm_year / 10;
1911                                                                 one = tm.tm_year % 10;
1912                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1913                                                                 res = wait_file(chan,ints,nextmsg,lang);
1914                                                                 if (!res) {
1915                                                                         if (one != 0) {
1916                                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1917                                                                                 res = wait_file(chan,ints,nextmsg,lang);
1918                                                                         }
1919                                                                 }
1920                                                         }
1921                                                 }
1922                                         }
1923                                 }
1924                                 break;
1925                         case 'I':
1926                         case 'l':
1927                                 /* 12-Hour */
1928                                 if (tm.tm_hour == 0)
1929                                         snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1930                                 else if (tm.tm_hour > 12)
1931                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1932                                 else
1933                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1934                                 res = wait_file(chan,ints,nextmsg,lang);
1935                                 break;
1936                         case 'H':
1937                         case 'k':
1938                                 /* 24-Hour */
1939                                 if (format[offset] == 'H') {
1940                                         /* e.g. oh-eight */
1941                                         if (tm.tm_hour < 10) {
1942                                                 res = wait_file(chan,ints, "digits/oh",lang);
1943                                         }
1944                                 } else {
1945                                         /* e.g. eight */
1946                                         if (tm.tm_hour == 0) {
1947                                                 res = wait_file(chan,ints, "digits/oh",lang);
1948                                         }
1949                                 }
1950                                 if (!res) {
1951                                         if (tm.tm_hour != 0) {
1952                                                 int remainder = tm.tm_hour;
1953                                                 if (tm.tm_hour > 20) {
1954                                                         res = wait_file(chan,ints, "digits/20",lang);
1955                                                         remainder -= 20;
1956                                                 }
1957                                                 if (!res) {
1958                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1959                                                         res = wait_file(chan,ints,nextmsg,lang);
1960                                                 }
1961                                         }
1962                                 }
1963                                 break;
1964                         case 'M':
1965                                 /* Minute */
1966                                 if (tm.tm_min == 0) {
1967                                         res = wait_file(chan,ints, "digits/oclock",lang);
1968                                 } else if (tm.tm_min < 10) {
1969                                         res = wait_file(chan,ints, "digits/oh",lang);
1970                                         if (!res) {
1971                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1972                                                 res = wait_file(chan,ints,nextmsg,lang);
1973                                         }
1974                                 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
1975                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1976                                         res = wait_file(chan,ints,nextmsg,lang);
1977                                 } else {
1978                                         int ten, one;
1979                                         ten = (tm.tm_min / 10) * 10;
1980                                         one = (tm.tm_min % 10);
1981                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1982                                         res = wait_file(chan,ints,nextmsg,lang);
1983                                         if (!res) {
1984                                                 /* Fifty, not fifty-zero */
1985                                                 if (one != 0) {
1986                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1987                                                         res = wait_file(chan,ints,nextmsg,lang);
1988                                                 }
1989                                         }
1990                                 }
1991                                 break;
1992                         case 'P':
1993                         case 'p':
1994                                 /* AM/PM */
1995                                 if (tm.tm_hour > 11)
1996                                         snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1997                                 else
1998                                         snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1999                                 res = wait_file(chan,ints,nextmsg,lang);
2000                                 break;
2001                         case 'Q':
2002                                 /* Shorthand for "Today", "Yesterday", or ABdY */
2003                                 {
2004                                         struct timeval now;
2005                                         struct tm tmnow;
2006                                         time_t beg_today;
2007
2008                                         gettimeofday(&now,NULL);
2009                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2010                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2011                                         /* In any case, it saves not having to do ast_mktime() */
2012                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2013                                         if (beg_today < time) {
2014                                                 /* Today */
2015                                                 res = wait_file(chan,ints, "digits/today",lang);
2016                                         } else if (beg_today - 86400 < time) {
2017                                                 /* Yesterday */
2018                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2019                                         } else {
2020                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2021                                         }
2022                                 }
2023                                 break;
2024                         case 'q':
2025                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2026                                 {
2027                                         struct timeval now;
2028                                         struct tm tmnow;
2029                                         time_t beg_today;
2030
2031                                         gettimeofday(&now,NULL);
2032                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2033                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2034                                         /* In any case, it saves not having to do ast_mktime() */
2035                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2036                                         if (beg_today < time) {
2037                                                 /* Today */
2038                                         } else if ((beg_today - 86400) < time) {
2039                                                 /* Yesterday */
2040                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2041                                         } else if (beg_today - 86400 * 6 < time) {
2042                                                 /* Within the last week */
2043                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2044                                         } else {
2045                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2046                                         }
2047                                 }
2048                                 break;
2049                         case 'R':
2050                                 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
2051                                 break;
2052                         case 'S':
2053                                 /* Seconds */
2054                                 if (tm.tm_sec == 0) {
2055                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2056                                         res = wait_file(chan,ints,nextmsg,lang);
2057                                 } else if (tm.tm_sec < 10) {
2058                                         res = wait_file(chan,ints, "digits/oh",lang);
2059                                         if (!res) {
2060                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2061                                                 res = wait_file(chan,ints,nextmsg,lang);
2062                                         }
2063                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2064                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2065                                         res = wait_file(chan,ints,nextmsg,lang);
2066                                 } else {
2067                                         int ten, one;
2068                                         ten = (tm.tm_sec / 10) * 10;
2069                                         one = (tm.tm_sec % 10);
2070                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2071                                         res = wait_file(chan,ints,nextmsg,lang);
2072                                         if (!res) {
2073                                                 /* Fifty, not fifty-zero */
2074                                                 if (one != 0) {
2075                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2076                                                         res = wait_file(chan,ints,nextmsg,lang);
2077                                                 }
2078                                         }
2079                                 }
2080                                 break;
2081                         case 'T':
2082                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2083                                 break;
2084                         case ' ':
2085                         case '  ':
2086                                 /* Just ignore spaces and tabs */
2087                                 break;
2088                         default:
2089                                 /* Unknown character */
2090                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2091                 }
2092                 /* Jump out on DTMF */
2093                 if (res) {
2094                         break;
2095                 }
2096         }
2097         return res;
2098 }
2099
2100 /* German syntax */
2101 /* NB This currently is a 100% clone of the English syntax, just getting ready to make changes... */
2102 int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
2103 {
2104         struct tm tm;
2105         int res=0, offset, sndoffset;
2106         char sndfile[256], nextmsg[256];
2107
2108         ast_localtime(&time,&tm,timezone);
2109
2110         for (offset=0 ; format[offset] != '\0' ; offset++) {
2111                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2112                 switch (format[offset]) {
2113                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
2114                         case '\'':
2115                                 /* Literal name of a sound file */
2116                                 sndoffset=0;
2117                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2118                                         sndfile[sndoffset] = format[offset];
2119                                 sndfile[sndoffset] = '\0';
2120                                 res = wait_file(chan,ints,sndfile,lang);
2121                                 break;
2122                         case 'A':
2123                         case 'a':
2124                                 /* Sunday - Saturday */
2125                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2126                                 res = wait_file(chan,ints,nextmsg,lang);
2127                                 break;
2128                         case 'B':
2129                         case 'b':
2130                         case 'h':
2131                                 /* January - December */
2132                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2133                                 res = wait_file(chan,ints,nextmsg,lang);
2134                                 break;
2135                         case 'd':
2136                         case 'e':
2137                                 /* First - Thirtyfirst */
2138                                 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
2139                                         snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
2140                                         res = wait_file(chan,ints,nextmsg,lang);
2141                                 } else if (tm.tm_mday == 31) {
2142                                         /* "Thirty" and "first" */
2143                                         res = wait_file(chan,ints, "digits/30",lang);
2144                                         if (!res) {
2145                                                 res = wait_file(chan,ints, "digits/h-1",lang);
2146                                         }
2147                                 } else {
2148                                         /* Between 21 and 29 - two sounds */
2149                                         res = wait_file(chan,ints, "digits/20",lang);
2150                                         if (!res) {
2151                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
2152                                                 res = wait_file(chan,ints,nextmsg,lang);
2153                                         }
2154                                 }
2155                                 break;
2156                         case 'Y':
2157                                 /* Year */
2158                                 if (tm.tm_year > 99) {
2159                                         res = wait_file(chan,ints, "digits/2",lang);
2160                                         if (!res) {
2161                                                 res = wait_file(chan,ints, "digits/thousand",lang);
2162                                         }
2163                                         if (tm.tm_year > 100) {
2164                                                 if (!res) {
2165                                                         /* This works until the end of 2020 */
2166                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
2167                                                         res = wait_file(chan,ints,nextmsg,lang);
2168                                                 }
2169                                         }
2170                                 } else {
2171                                         if (tm.tm_year < 1) {
2172                                                 /* I'm not going to handle 1900 and prior */
2173                                                 /* We'll just be silent on the year, instead of bombing out. */
2174                                         } else {
2175                                                 res = wait_file(chan,ints, "digits/19",lang);
2176                                                 if (!res) {
2177                                                         if (tm.tm_year <= 9) {
2178                                                                 /* 1901 - 1909 */
2179                                                                 res = wait_file(chan,ints, "digits/oh",lang);
2180                                                                 if (!res) {
2181                                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
2182                                                                         res = wait_file(chan,ints,nextmsg,lang);
2183                                                                 }
2184                                                         } else if (tm.tm_year <= 20) {
2185                                                                 /* 1910 - 1920 */
2186                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
2187                                                                 res = wait_file(chan,ints,nextmsg,lang);
2188                                                         } else {
2189                                                                 /* 1921 - 1999 */
2190                                                                 int ten, one;
2191                                                                 ten = tm.tm_year / 10;
2192                                                                 one = tm.tm_year % 10;
2193                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
2194                                                                 res = wait_file(chan,ints,nextmsg,lang);
2195                                                                 if (!res) {
2196                                                                         if (one != 0) {
2197                                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2198                                                                                 res = wait_file(chan,ints,nextmsg,lang);
2199                                                                         }
2200                                                                 }
2201                                                         }
2202                                                 }
2203                                         }
2204                                 }
2205                                 break;
2206                         case 'I':
2207                         case 'l':
2208                                 /* 12-Hour */
2209                                 if (tm.tm_hour == 0)
2210                                         snprintf(nextmsg,sizeof(nextmsg), "digits/12");
2211                                 else if (tm.tm_hour > 12)
2212                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
2213                                 else
2214                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
2215                                 res = wait_file(chan,ints,nextmsg,lang);
2216                                 break;
2217                         case 'H':
2218                         case 'k':
2219                                 /* 24-Hour */
2220                                 if (format[offset] == 'H') {
2221                                         /* e.g. oh-eight */
2222                                         if (tm.tm_hour < 10) {
2223                                                 res = wait_file(chan,ints, "digits/oh",lang);
2224                                         }
2225                                 } else {
2226                                         /* e.g. eight */
2227                                         if (tm.tm_hour == 0) {
2228                                                 res = wait_file(chan,ints, "digits/oh",lang);
2229                                         }
2230                                 }
2231                                 if (!res) {
2232                                         if (tm.tm_hour != 0) {
2233                                                 int remainder = tm.tm_hour;
2234                                                 if (tm.tm_hour > 20) {
2235                                                         res = wait_file(chan,ints, "digits/20",lang);
2236                                                         remainder -= 20;
2237                                                 }
2238                                                 if (!res) {
2239                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
2240                                                         res = wait_file(chan,ints,nextmsg,lang);
2241                                                 }
2242                                         }
2243                                 }
2244                                 break;
2245                         case 'M':
2246                                 /* Minute */
2247                                 if (tm.tm_min == 0) {
2248                                         res = wait_file(chan,ints, "digits/oclock",lang);
2249                                 } else if (tm.tm_min < 10) {
2250                                         res = wait_file(chan,ints, "digits/oh",lang);
2251                                         if (!res) {
2252                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
2253                                                 res = wait_file(chan,ints,nextmsg,lang);
2254                                         }
2255                                 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
2256                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
2257                                         res = wait_file(chan,ints,nextmsg,lang);
2258                                 } else {
2259                                         int ten, one;
2260                                         ten = (tm.tm_min / 10) * 10;
2261                                         one = (tm.tm_min % 10);
2262                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2263                                         res = wait_file(chan,ints,nextmsg,lang);
2264                                         if (!res) {
2265                                                 /* Fifty, not fifty-zero */
2266                                                 if (one != 0) {
2267                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2268                                                         res = wait_file(chan,ints,nextmsg,lang);
2269                                                 }
2270                                         }
2271                                 }
2272                                 break;
2273                         case 'P':
2274                         case 'p':
2275                                 /* AM/PM */
2276                                 if (tm.tm_hour > 11)
2277                                         snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
2278                                 else
2279                                         snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
2280                                 res = wait_file(chan,ints,nextmsg,lang);
2281                                 break;
2282                         case 'Q':
2283                                 /* Shorthand for "Today", "Yesterday", or ABdY */
2284                                 {
2285                                         struct timeval now;
2286                                         struct tm tmnow;
2287                                         time_t beg_today;
2288
2289                                         gettimeofday(&now,NULL);
2290                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2291                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2292                                         /* In any case, it saves not having to do ast_mktime() */
2293                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2294                                         if (beg_today < time) {
2295                                                 /* Today */
2296                                                 res = wait_file(chan,ints, "digits/today",lang);
2297                                         } else if (beg_today - 86400 < time) {
2298                                                 /* Yesterday */
2299                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2300                                         } else {
2301                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2302                                         }
2303                                 }
2304                                 break;
2305                         case 'q':
2306                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2307                                 {
2308                                         struct timeval now;
2309                                         struct tm tmnow;
2310                                         time_t beg_today;
2311
2312                                         gettimeofday(&now,NULL);
2313                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2314                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2315                                         /* In any case, it saves not having to do ast_mktime() */
2316                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2317                                         if (beg_today < time) {
2318                                                 /* Today */
2319                                         } else if ((beg_today - 86400) < time) {
2320                                                 /* Yesterday */
2321                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2322                                         } else if (beg_today - 86400 * 6 < time) {
2323                                                 /* Within the last week */
2324                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2325                                         } else {
2326                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2327                                         }
2328                                 }
2329                                 break;
2330                         case 'R':
2331                                 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
2332                                 break;
2333                         case 'S':
2334                                 /* Seconds */
2335                                 if (tm.tm_sec == 0) {
2336                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2337                                         res = wait_file(chan,ints,nextmsg,lang);
2338                                 } else if (tm.tm_sec < 10) {
2339                                         res = wait_file(chan,ints, "digits/oh",lang);
2340                                         if (!res) {
2341                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2342                                                 res = wait_file(chan,ints,nextmsg,lang);
2343                                         }
2344                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2345                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2346                                         res = wait_file(chan,ints,nextmsg,lang);
2347                                 } else {
2348                                         int ten, one;
2349                                         ten = (tm.tm_sec / 10) * 10;
2350                                         one = (tm.tm_sec % 10);
2351                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2352                                         res = wait_file(chan,ints,nextmsg,lang);
2353                                         if (!res) {
2354                                                 /* Fifty, not fifty-zero */
2355                                                 if (one != 0) {
2356                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2357                                                         res = wait_file(chan,ints,nextmsg,lang);
2358                                                 }
2359                                         }
2360                                 }
2361                                 break;
2362                         case 'T':
2363                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2364                                 break;
2365                         case ' ':
2366                         case '  ':
2367                                 /* Just ignore spaces and tabs */
2368                                 break;
2369                         default:
2370                                 /* Unknown character */
2371                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2372                 }
2373                 /* Jump out on DTMF */
2374                 if (res) {
2375                         break;
2376                 }
2377         }
2378         return res;
2379 }
2380
2381 /* Spanish syntax */
2382 int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
2383 {
2384         struct tm tm;
2385         int res=0, offset, sndoffset;
2386         char sndfile[256], nextmsg[256];
2387
2388         ast_localtime(&time,&tm,timezone);
2389
2390         for (offset=0 ; format[offset] != '\0' ; offset++) {
2391                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2392                 switch (format[offset]) {
2393                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
2394                         case '\'':
2395                                 /* Literal name of a sound file */
2396                                 sndoffset=0;
2397                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2398                                         sndfile[sndoffset] = format[offset];
2399                                 sndfile[sndoffset] = '\0';
2400                                 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
2401                                 res = wait_file(chan,ints,nextmsg,lang);
2402                                 break;
2403                         case 'A':
2404                         case 'a':
2405                                 /* Sunday - Saturday */
2406                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2407                                 res = wait_file(chan,ints,nextmsg,lang);
2408                                 break;
2409                         case 'B':
2410                         case 'b':
2411                         case 'h':
2412                                 /* January - December */
2413                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2414                                 res = wait_file(chan,ints,nextmsg,lang);
2415                                 break;
2416                         case 'd':
2417                         case 'e':
2418                                 /* First - Thirtyfirst */
2419                                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2420                                 break;
2421                         case 'Y':
2422                                 /* Year */
2423                                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2424                                 break;
2425                         case 'I':
2426                         case 'l':
2427                                 /* 12-Hour */
2428                                 if (tm.tm_hour == 0)
2429                                         snprintf(nextmsg,sizeof(nextmsg), "digits/12");
2430                                 else if (tm.tm_hour > 12)
2431                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
2432                                 else
2433                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
2434                                 res = wait_file(chan,ints,nextmsg,lang);
2435                                 break;
2436                         case 'H':
2437                         case 'k':
2438                                 /* 24-Hour */
2439                                 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
2440                                 if (!res) {
2441                                         if (tm.tm_hour != 0) {
2442                                                 int remainder = tm.tm_hour;
2443                                                 if (tm.tm_hour > 20) {
2444                                                         res = wait_file(chan,ints, "digits/20",lang);
2445                                                         remainder -= 20;
2446                                                 }
2447                                                 if (!res) {
2448                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
2449                                                         res = wait_file(chan,ints,nextmsg,lang);
2450                                                 }
2451                                         }
2452                                 }
2453                                 break;
2454                         case 'M':
2455                                 /* Minute */
2456                                 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);       
2457                                 break;
2458                         case 'P':
2459                         case 'p':
2460                                 /* AM/PM */
2461                                 if (tm.tm_hour > 12)
2462                                         res = wait_file(chan, ints, "digits/p-m", lang);
2463                                 else if (tm.tm_hour  && tm.tm_hour < 12)
2464                                         res = wait_file(chan, ints, "digits/a-m", lang);
2465                                 break;
2466                         case 'Q':
2467                                 /* Shorthand for "Today", "Yesterday", or ABdY */
2468                                 {
2469                                         struct timeval now;
2470                                         struct tm tmnow;
2471                                         time_t beg_today;
2472
2473                                         gettimeofday(&now,NULL);
2474                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2475                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2476                                         /* In any case, it saves not having to do ast_mktime() */
2477                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2478                                         if (beg_today < time) {
2479                                                 /* Today */
2480                                                 res = wait_file(chan,ints, "digits/today",lang);
2481                                         } else if (beg_today - 86400 < time) {
2482                                                 /* Yesterday */
2483                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2484                                         } else {
2485                                                 res = ast_say_date_with_format(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
2486                                         }
2487                                 }
2488                                 break;
2489                         case 'q':
2490                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2491                                 {
2492                                         struct timeval now;
2493                                         struct tm tmnow;
2494                                         time_t beg_today;
2495
2496                                         gettimeofday(&now,NULL);
2497                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2498                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2499                                         /* In any case, it saves not having to do ast_mktime() */
2500                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2501                                         if (beg_today < time) {
2502                                                 /* Today */
2503                                                 res = wait_file(chan,ints, "digits/today",lang);
2504                                         } else if ((beg_today - 86400) < time) {
2505                                                 /* Yesterday */
2506                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2507                                         } else if (beg_today - 86400 * 6 < time) {
2508                                                 /* Within the last week */
2509                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2510                                         } else {
2511                                                 res = ast_say_date_with_format(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
2512                                         }
2513                                 }
2514                                 break;
2515                         case 'R':
2516                                 res = ast_say_date_with_format(chan, time, ints, lang, "H 'digits/y' M", timezone);
2517                                 break;
2518                         case 'S':
2519                                 /* Seconds */
2520                                 if (tm.tm_sec == 0) {
2521                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2522                                         res = wait_file(chan,ints,nextmsg,lang);
2523                                 } else if (tm.tm_sec < 10) {
2524                                         res = wait_file(chan,ints, "digits/oh",lang);
2525                                         if (!res) {
2526                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2527                                                 res = wait_file(chan,ints,nextmsg,lang);
2528                                         }
2529                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2530                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2531                                         res = wait_file(chan,ints,nextmsg,lang);
2532                                 } else {
2533                                         int ten, one;
2534                                         ten = (tm.tm_sec / 10) * 10;
2535                                         one = (tm.tm_sec % 10);
2536                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2537                                         res = wait_file(chan,ints,nextmsg,lang);
2538                                         if (!res) {
2539                                                 /* Fifty, not fifty-zero */
2540                                                 if (one != 0) {
2541                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2542                                                         res = wait_file(chan,ints,nextmsg,lang);
2543                                                 }
2544                                         }
2545                                 }
2546                                 break;
2547                         case 'T':
2548                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2549                                 break;
2550                         case ' ':
2551                         case '  ':
2552                                 /* Just ignore spaces and tabs */
2553                                 break;
2554                         default:
2555                                 /* Unknown character */
2556                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2557                 }
2558                 /* Jump out on DTMF */
2559                 if (res) {
2560                         break;
2561                 }
2562         }
2563         return res;
2564 }
2565
2566 /* Dutch syntax */
2567 int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
2568 {
2569         struct tm tm;
2570         int res=0, offset, sndoffset;
2571         char sndfile[256], nextmsg[256];
2572
2573         ast_localtime(&time,&tm,timezone);
2574
2575         for (offset=0 ; format[offset] != '\0' ; offset++) {
2576                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2577                 switch (format[offset]) {
2578                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
2579                         case '\'':
2580                                 /* Literal name of a sound file */
2581                                 sndoffset=0;
2582                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2583                                         sndfile[sndoffset] = format[offset];
2584                                 sndfile[sndoffset] = '\0';
2585                                 res = wait_file(chan,ints,sndfile,lang);
2586                                 break;
2587                         case 'A':
2588                         case 'a':
2589                                 /* Sunday - Saturday */
2590                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2591                                 res = wait_file(chan,ints,nextmsg,lang);
2592                                 break;
2593                         case 'B':
2594                         case 'b':
2595                         case 'h':
2596                                 /* January - December */
2597                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2598                                 res = wait_file(chan,ints,nextmsg,lang);
2599                                 break;
2600                         case 'd':
2601                         case 'e':
2602                                 /* First - Thirtyfirst */
2603                                 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
2604                                 break;
2605                         case 'Y':
2606                                 /* Year */
2607                                 if (tm.tm_year > 99) {
2608                                         res = wait_file(chan,ints, "digits/2",lang);
2609                                         if (!res) {
2610                                                 res = wait_file(chan,ints, "digits/thousand",lang);
2611                                         }
2612                                         if (tm.tm_year > 100) {
2613                                                 if (!res) {
2614                                                         /* This works until the end of 2020 */
2615                                                         snprintf(nextmsg,sizeof(nextmsg), &qu