Add taiwanese support for saynumber/datetime (bug #1615)
[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       pt - Portuguese
413       se - Swedish
414       tw - Taiwanese
415
416  Gender:
417  For Portuguese, French & Spanish, we're using m & f options to saynumber() to indicate if the gender is masculine or feminine.
418  For Danish, we're using c & n options to saynumber() to indicate if the gender is commune or neutrum.
419  This still needs to be implemented for German (although the option is passed to the function, it currently does nothing with it).
420  
421  Date/Time functions currently have less languages supported than saynumber().
422
423  Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
424
425  See contrib/i18n.testsuite.conf for some examples of the different syntaxes
426
427  Portuguese sound files needed for Time/Date functions:
428  pt-ah
429  pt-ao
430  pt-de
431  pt-e
432  pt-ora
433  pt-meianoite
434  pt-meiodia
435  pt-sss
436
437  Spanish sound files needed for Time/Date functions:
438  es-de
439  es-el
440
441 */
442
443 /* Forward declarations of language specific variants of ast_say_number_full */
444 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
445 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
446 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
447 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
448 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
449 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
450 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
451 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
452 static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
453 static int ast_say_number_full_tw(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
454
455 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
456 static int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
457 static int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
458 static int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
459
460 static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
461 static int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
462 static int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
463 static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
464 static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
465 static int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone);
466
467 static int ast_say_time_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
468 static int ast_say_time_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
469 static int ast_say_time_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
470 static int ast_say_time_tw(struct ast_channel *chan, time_t t, char *ints, char *lang);
471
472 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
473 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, char *ints, char *lang);
474 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
475 static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, char *ints, char *lang);
476
477 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, char *ints, char *lang);
478 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, char *ints, char *lang);
479
480 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang) 
481 {
482         int res;
483         if ((res = ast_streamfile(chan, file, lang)))
484                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
485         if (!res)
486                 res = ast_waitstream(chan, ints);
487         return res;
488 }
489
490 /*--- ast_say_number_full: call language-specific functions */
491 /* Called from AGI */
492 int ast_say_number_full(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
493 {
494         char *options=(char *) NULL;    /* While waiting for a general hack for agi */
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, "pt") ) {      /* Portuguese syntax */
511            return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
512         } else if (!strcasecmp(language, "se") ) {      /* Swedish syntax */
513            return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
514         } else if (!strcasecmp(language, "tw")) {       /* Taiwanese syntax */
515                  return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
516         }
517
518         /* Default to english */
519         return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
520 }
521
522 /*--- ast_say_number: call language-specific functions without file descriptors */
523 int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language, char *options)
524 {
525         if (!strcasecmp(language,"en") ) {      /* English syntax */
526            return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
527         }else if (!strcasecmp(language, "da")) {        /* Danish syntax */
528            return(ast_say_number_full_da(chan, num, ints, language, options, -1, -1));
529         } else if (!strcasecmp(language, "de")) {       /* German syntax */
530            return(ast_say_number_full_de(chan, num, ints, language, options, -1, -1));
531         } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) {        /* Spanish syntax */
532            return(ast_say_number_full_es(chan, num, ints, language, options, -1, -1));
533         } else if (!strcasecmp(language, "fr")) {       /* French syntax */
534            return(ast_say_number_full_fr(chan, num, ints, language, options, -1, -1));
535         } else if (!strcasecmp(language, "it")) {       /* Italian syntax */
536            return(ast_say_number_full_it(chan, num, ints, language, -1, -1));
537         } else if (!strcasecmp(language, "nl")) {       /* Dutch syntax */
538            return(ast_say_number_full_nl(chan, num, ints, language, -1, -1));
539         } else if (!strcasecmp(language, "pt")) {       /* Portuguese syntax */
540            return(ast_say_number_full_pt(chan, num, ints, language, options, -1, -1));
541         } else if (!strcasecmp(language, "se")) {       /* Swedish syntax */
542            return(ast_say_number_full_se(chan, num, ints, language, options, -1, -1));
543         }
544
545         /* Default to english */
546         return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
547 }
548
549 /*--- ast_say_number_full_en: English syntax */
550 /* This is the default syntax, if no other syntax defined in this file is used */
551 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
552 {
553         int res = 0;
554         int playh = 0;
555         char fn[256] = "";
556         if (!num) 
557                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
558
559         while(!res && (num || playh)) {
560                         if (playh) {
561                                 snprintf(fn, sizeof(fn), "digits/hundred");
562                                 playh = 0;
563                         } else  if (num < 20) {
564                                 snprintf(fn, sizeof(fn), "digits/%d", num);
565                                 num = 0;
566                         } else  if (num < 100) {
567                                 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
568                                 num -= ((num / 10) * 10);
569                         } else {
570                                 if (num < 1000){
571                                         snprintf(fn, sizeof(fn), "digits/%d", (num/100));
572                                         playh++;
573                                         num -= ((num / 100) * 100);
574                                 } else {
575                                         if (num < 1000000) { /* 1,000,000 */
576                                                 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
577                                                 if (res)
578                                                         return res;
579                                                 num = num % 1000;
580                                                 snprintf(fn, sizeof(fn), "digits/thousand");
581                                         } else {
582                                                 if (num < 1000000000) { /* 1,000,000,000 */
583                                                         res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
584                                                         if (res)
585                                                                 return res;
586                                                         num = num % 1000000;
587                                                         snprintf(fn, sizeof(fn), "digits/million");
588                                                 } else {
589                                                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
590                                                         res = -1;
591                                                 }
592                                         }
593                                 }
594                         }
595                         if (!res) {
596                                 if(!ast_streamfile(chan, fn, language)) {
597                                         if (audiofd && ctrlfd)
598                                                 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
599                                         else
600                                                 res = ast_waitstream(chan, ints);
601                                 }
602                                 ast_stopstream(chan);
603
604                         }
605                         
606         }
607         return res;
608 }
609
610 /*--- ast_say_number_full_da: Danish syntax */
611 /* New files:
612  In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and" 
613  */
614 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
615 {
616         int res = 0;
617         int playh = 0;
618         int playa = 0;
619         int cn = 1;             /* +1 = Commune; -1 = Neutrum */
620         char fn[256] = "";
621         if (!num) 
622                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
623
624         if (options && !strncasecmp(options, "n",1)) cn = -1;
625
626         while(!res && (num || playh || playa )) {
627                 /* The grammar for Danish numbers is the same as for English except
628                 * for the following:
629                 * - 1 exists in both commune ("en", file "1N") and neutrum ("et", file "1")
630                 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
631                 *   "one-and twenty" and 68 is "eight-and sixty".
632                 * - "million" is different in singular and plural form
633                 * - numbers > 1000 with zero as the third digit from last have an
634                 *   "and" before the last two digits, i.e. 2034 is "two thousand and
635                 *   four-and thirty" and 1000012 is "one million and twelve".
636                 */
637                 if (playh) {
638                         snprintf(fn, sizeof(fn), "digits/hundred");
639                         playh = 0;
640                 } else if (playa) {
641                         snprintf(fn, sizeof(fn), "digits/and");
642                         playa = 0;
643                 } else if (num == 1 && cn == -1) {
644                         snprintf(fn, sizeof(fn), "digits/1N");
645                         num = 0;
646                 } else if (num < 20) {
647                         snprintf(fn, sizeof(fn), "digits/%d", num);
648                         num = 0;
649                 } else if (num < 100) {
650                         int ones = num % 10;
651                         if (ones) {
652                                 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
653                                 num -= ones;
654                         } else {
655                                 snprintf(fn, sizeof(fn), "digits/%d", num);
656                                 num = 0;
657                         }
658                 } else {
659                         if (num < 1000) {
660                                 int hundreds = num / 100;
661                                 if (hundreds == 1)
662                                         snprintf(fn, sizeof(fn), "digits/1N");
663                                 else
664                                         snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
665
666                                 playh++;
667                                 num -= 100 * hundreds;
668                                 if (num)
669                                         playa++;
670
671                         } else {
672                                 if (num < 1000000) {
673                                         res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
674                                         if (res)
675                                                 return res;
676                                         num = num % 1000;
677                                         snprintf(fn, sizeof(fn), "digits/thousand");
678                                 } else {
679                                         if (num < 1000000000) {
680                                                 int millions = num / 1000000;
681                                                 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
682                                                 if (res)
683                                                         return res;
684                                                 if (millions == 1)
685                                                         snprintf(fn, sizeof(fn), "digits/million");
686                                                 else
687                                                         snprintf(fn, sizeof(fn), "digits/millions");
688                                                 num = num % 1000000;
689                                         } else {
690                                                 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
691                                                 res = -1;
692                                         }
693                                 }
694                                 if (num && num < 100)
695                                         playa++;
696                         }
697                 }
698                 if (!res) {
699                         if(!ast_streamfile(chan, fn, language)) {
700                                 if (audiofd && ctrlfd) 
701                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
702                                 else  
703                                         res = ast_waitstream(chan, ints);
704                         }
705                         ast_stopstream(chan);
706                 }
707         }
708         return res;
709 }
710
711 /*--- ast_say_number_full_de: German syntax */
712 /* New files:
713  In addition to English, the following sounds are required:
714  "millions"
715  "1-and" through "9-and" 
716  "1F" (eine)
717  "1N" (ein)
718  NB "1" is recorded as 'eins'
719  */
720 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
721 {
722         int res = 0;
723         int playh = 0;
724         int t = 0;
725         int mf = 1;                            /* +1 = Male, Neutrum; -1 = Female */
726         char fn[256] = "";
727         char fna[256] = "";
728         if (!num) 
729                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
730
731         if (options && (!strncasecmp(options, "f",1)))
732                 mf = -1;
733
734         while(!res && (num || playh)) {
735                 /* The grammar for German numbers is the same as for English except
736                 * for the following:
737                 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
738                 *   "one-and twenty" and 68 is "eight-and sixty".
739                 * - "one" varies according to gender
740                 * - 100 is 'hundert', however all other instances are 'ein hundert'
741                 * - 1000 is 'tausend', however all other instances are 'ein tausend'
742                 * - 1000000 is always 'ein million'
743                 * - "million" is different in singular and plural form
744                 */
745                 if (playh) {
746                         snprintf(fn, sizeof(fn), "digits/hundred");
747                         playh = 0;
748                 } else if (num == 1 && mf == -1) {
749                         snprintf(fn, sizeof(fn), "digits/%dF", num);
750                         num = 0;
751                 } else if (num < 20) {
752                         snprintf(fn, sizeof(fn), "digits/%d", num);
753                         num = 0;
754                 } else if (num < 100) {
755                         int ones = num % 10;
756                         if (ones) {
757                                 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
758                                 num -= ones;
759                         } else {
760                                 snprintf(fn, sizeof(fn), "digits/%d", num);
761                                 num = 0;
762                         }
763                 } else if (num == 100) {
764                         snprintf(fn, sizeof(fn), "digits/hundred");
765                         num = num - 100;
766                 } else if (num < 1000) {
767                         int hundreds = num / 100;
768                         if (hundreds == 1)
769                                 snprintf(fn, sizeof(fn), "digits/1N");
770                         else
771                                 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
772                         playh++;
773                         num -= 100 * hundreds;
774                 } else if (num == 1000 && t == 0) {
775                         snprintf(fn, sizeof(fn), "digits/thousand");
776                         num = 0;
777                 } else  if (num < 1000000) {
778                         int thousands = num / 1000;
779                         t = 1;
780                         if (thousands == 1) {
781                                 snprintf(fn, sizeof(fn), "digits/1N");
782                                 snprintf(fna, sizeof(fna), "digits/thousand");
783                         } else {
784                                 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
785                                 if (res)
786                                         return res;
787                                 snprintf(fn, sizeof(fn), "digits/thousand");
788                         }
789                         num = num % 1000;
790                 } else if (num < 1000000000) {
791                         int millions = num / 1000000;
792                         t = 1;
793                         if (millions == 1) {
794                                 snprintf(fn, sizeof(fn), "digits/1N");
795                                 snprintf(fna, sizeof(fna), "digits/million");
796                         } else {
797                                 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
798                                 if (res)
799                                         return res;
800                                 snprintf(fn, sizeof(fn), "digits/millions");
801                         }
802                         num = num % 1000000;
803                 } else {
804                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
805                         res = -1;
806                 }
807                 if (!res) {
808                         if(!ast_streamfile(chan, fn, language)) {
809                                 if (audiofd && ctrlfd) 
810                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
811                                 else  
812                                         res = ast_waitstream(chan, ints);
813                         }
814                         ast_stopstream(chan);
815                         if(!ast_streamfile(chan, fna, language)) {
816                                 if (audiofd && ctrlfd) 
817                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
818                                 else  
819                                         res = ast_waitstream(chan, ints);
820                         }
821                         ast_stopstream(chan);
822                         strcpy(fna, "");
823                 }
824         }
825         return res;
826 }
827
828 /*--- ast_say_number_full_es: Spanish syntax */
829 /* New files:
830  Requires a few new audios:
831    1F.gsm: feminine 'una'
832    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 
833  */
834 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
835 {
836         int res = 0;
837         int playa = 0;
838         int mf = 1;                            /* +1 = Masculin; -1 = Feminin */
839         char fn[256] = "";
840         if (!num) 
841                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
842
843         if (options && !strncasecmp(options, "f",1))
844                 mf = -1;
845
846         while (!res && num) {
847                 if (playa) {
848                         snprintf(fn, sizeof(fn), "digits/y");
849                         playa = 0;
850                 } else if (num == 1) {
851                         if (mf < 0)
852                                 snprintf(fn, sizeof(fn), "digits/%dF", num);
853                         else
854                                 snprintf(fn, sizeof(fn), "digits/%d", num);
855                         num = 0;
856                 } else if (num < 31) {
857                         snprintf(fn, sizeof(fn), "digits/%d", num);
858                         num = 0;
859                 } else if (num < 100) {
860                         snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
861                         num -= ((num/10)*10);
862                         if (num)
863                                 playa++;
864                 } else if (num == 100) {
865                         snprintf(fn, sizeof(fn), "digits/cien");
866                         num = 0;
867                 } else {
868                         if (num < 1000) {
869                                 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
870                                 num -= ((num/100)*100);
871                         } else {
872                                 if (num < 1000000) {
873                                         res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
874                                         if (res)
875                                                 return res;
876                                         num = num % 1000;
877                                         snprintf(fn, sizeof(fn), "digits/mil");
878                                 } else {
879                                         if (num < 2147483640) {
880                                                 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
881                                                 if (res)
882                                                         return res;
883                                                 if ((num/1000000) == 1) {
884                                                         snprintf(fn, sizeof(fn), "digits/millon");
885                                                 } else {
886                                                         snprintf(fn, sizeof(fn), "digits/millones");
887                                                 }
888                                                 num = num % 1000000;
889                                         } else {
890                                                 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
891                                                 res = -1;
892                                         }
893                                 }
894                         }
895                 }
896
897                 if (!res) {
898                         if(!ast_streamfile(chan, fn, language)) {
899                                 if (audiofd && ctrlfd)
900                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
901                                 else
902                                         res = ast_waitstream(chan, ints);
903                         }
904                         ast_stopstream(chan);
905
906                 }
907                         
908         }
909         return res;
910 }
911
912
913 /*--- ast_say_number_full_fr: French syntax */
914 /*      Extra sounds needed:
915         1F: feminin 'une'
916         et: 'and' */
917 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
918 {
919         int res = 0;
920         int playh = 0;
921         int playa = 0;
922         int mf = 1;                            /* +1 = Masculin; -1 = Feminin */
923         char fn[256] = "";
924         if (!num) 
925                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
926         
927         if (options && !strncasecmp(options, "f",1))
928                 mf = -1;
929
930         while(!res && (num || playh || playa)) {
931                 if (playh) {
932                         snprintf(fn, sizeof(fn), "digits/hundred");
933                         playh = 0;
934                 } else if (playa) {
935                         snprintf(fn, sizeof(fn), "digits/et");
936                         playa = 0;
937                 } else if (num == 1) {
938                         if (mf < 0)
939                                 snprintf(fn, sizeof(fn), "digits/%dF", num);
940                         else
941                                 snprintf(fn, sizeof(fn), "digits/%d", num);
942                         num = 0;
943                 } else if (num < 21) {
944                         snprintf(fn, sizeof(fn), "digits/%d", num);
945                         num = 0;
946                 } else if (num < 70) {
947                         snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
948                         if ((num % 10) == 1) playa++;
949                         num = num % 10;
950                 } else if (num < 80) {
951                         snprintf(fn, sizeof(fn), "digits/60");
952                         if ((num % 10) == 1) playa++;
953                         num = num - 60;
954                 } else if (num < 100) {
955                         snprintf(fn, sizeof(fn), "digits/80");
956                         num = num - 80;
957                 } else if (num < 200) {
958                         snprintf(fn, sizeof(fn), "digits/hundred");
959                         num = num - 100;
960                 } else if (num < 1000) {
961                         snprintf(fn, sizeof(fn), "digits/%d", (num/100));
962                         playh++;
963                         num = num % 100;
964                 } else if (num < 2000) {
965                         snprintf(fn, sizeof(fn), "digits/thousand");
966                         num = num - 1000;
967                 } else if (num < 1000000) {
968                         res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
969                         if (res)
970                                 return res;
971                         snprintf(fn, sizeof(fn), "digits/thousand");
972                         num = num % 1000;
973                 } else  if (num < 1000000000) {
974                         res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
975                         if (res)
976                                 return res;
977                         snprintf(fn, sizeof(fn), "digits/million");
978                         num = num % 1000000;
979                 } else {
980                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
981                         res = -1;
982                 }
983                 if (!res) {
984                         if(!ast_streamfile(chan, fn, language)) {
985                                 if (audiofd && ctrlfd)
986                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
987                                 else
988                                         res = ast_waitstream(chan, ints);
989                         }
990                         ast_stopstream(chan);
991                 }
992         }
993         return res;
994 }
995
996 /*--- ast_say_number_full_it:  Italian */
997 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
998 {
999         int res = 0;
1000         int playh = 0;
1001         int tempnum = 0;
1002         char fn[256] = "";
1003
1004         if (!num)
1005                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1006
1007                 /*
1008                 Italian support
1009
1010                 Like english, numbers up to 20 are a single 'word', and others
1011                 compound, but with exceptions.
1012                 For example 21 is not twenty-one, but there is a single word in 'it'.
1013                 Idem for 28 (ie when a the 2nd part of a compund number
1014                 starts with a vowel)
1015
1016                 There are exceptions also for hundred, thousand and million.
1017                 In english 100 = one hundred, 200 is two hundred.
1018                 In italian 100 = cento , like to say hundred (without one),
1019                 200 and more are like english.
1020                 
1021                 Same applies for thousand:
1022                 1000 is one thousand in en, 2000 is two thousand.
1023                 In it we have 1000 = mille , 2000 = 2 mila 
1024
1025                 For million(s) we use the plural, if more than one
1026                 Also, one million is abbreviated in it, like on-million,
1027                 or 'un milione', not 'uno milione'.
1028                 So the right file is provided.
1029                 */
1030
1031                 while(!res && (num || playh)) {
1032                         if (playh) {
1033                                 snprintf(fn, sizeof(fn), "digits/hundred");
1034                                 playh = 0;
1035                         } else if (num < 20) {
1036                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1037                                 num = 0;
1038                         } else if (num == 21) {
1039                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1040                                 num = 0;
1041                         } else if (num == 28) {
1042                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1043                                 num = 0;
1044                         } else if (num == 31) {
1045                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1046                                 num = 0;
1047                         } else if (num == 38) {
1048                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1049                                 num = 0;
1050                         } else if (num == 41) {
1051                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1052                                 num = 0;
1053                         } else if (num == 48) {
1054                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1055                                 num = 0;
1056                         } else if (num == 51) {
1057                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1058                                 num = 0;
1059                         } else if (num == 58) {
1060                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1061                                 num = 0;
1062                         } else if (num == 61) {
1063                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1064                                 num = 0;
1065                         } else if (num == 68) {
1066                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1067                                 num = 0;
1068                         } else if (num == 71) {
1069                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1070                                 num = 0;
1071                         } else if (num == 78) {
1072                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1073                                 num = 0;
1074                         } else if (num == 81) {
1075                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1076                                 num = 0;
1077                         } else if (num == 88) {
1078                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1079                                 num = 0;
1080                         } else if (num == 91) {
1081                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1082                                 num = 0;
1083                         } else if (num == 98) {
1084                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1085                                 num = 0;
1086                         } else if (num < 100) {
1087                                 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1088                                 num -= ((num / 10) * 10);
1089                         } else {
1090                                 if (num < 1000) {
1091                                         if ((num / 100) > 1) {
1092                                                 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1093                                                 playh++;
1094                                         } else {
1095                                                 snprintf(fn, sizeof(fn), "digits/hundred");
1096                                         }
1097                                         num -= ((num / 100) * 100);
1098                                 } else {
1099                                         if (num < 1000000) { /* 1,000,000 */
1100                                                 if ((num/1000) > 1)
1101                                                         res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd);
1102                                                 if (res)
1103                                                         return res;
1104                                                 tempnum = num;
1105                                                 num = num % 1000;
1106                                                 if ((tempnum / 1000) < 2)
1107                                                         snprintf(fn, sizeof(fn), "digits/thousand");
1108                                                 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1109                                                         snprintf(fn, sizeof(fn), "digits/thousands");
1110                                         } else {
1111                                                 if (num < 1000000000) { /* 1,000,000,000 */
1112                                                         if ((num / 1000000) > 1)
1113                                                                 res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1114                                                         if (res)
1115                                                                 return res;
1116                                                         tempnum = num;
1117                                                         num = num % 1000000;
1118                                                         if ((tempnum / 1000000) < 2)
1119                                                                 snprintf(fn, sizeof(fn), "digits/million");
1120                                                         else
1121                                                                 snprintf(fn, sizeof(fn), "digits/millions");
1122                                                 } else {
1123                                                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1124                                                         res = -1;
1125                                                 }
1126                                         }
1127                                 }
1128                         }
1129                         if (!res) {
1130                                 if(!ast_streamfile(chan, fn, language)) {
1131                                         if (audiofd && ctrlfd)
1132                                                 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1133                                         else
1134                                                 res = ast_waitstream(chan, ints);
1135                                 }
1136                                 ast_stopstream(chan);
1137                         }
1138                 }
1139         return res;
1140 }
1141
1142 /*--- ast_say_number_full_nl: dutch syntax */
1143 /* New files: digits/nl-en
1144  */
1145 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
1146 {
1147         int res = 0;
1148         int playh = 0;
1149         int units = 0;
1150         char fn[256] = "";
1151         if (!num) 
1152                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1153         while (!res && (num || playh )) {
1154                 if (playh) {
1155                         snprintf(fn, sizeof(fn), "digits/hundred");
1156                         playh = 0;
1157                 } else if (num < 20) {
1158                         snprintf(fn, sizeof(fn), "digits/%d", num);
1159                         num = 0;
1160                 } else if (num < 100) {
1161                         units = num % 10;
1162                         if (units > 0) {
1163                                 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1164                                 if (res)
1165                                         return res;
1166                                 num = num - units;
1167                                 snprintf(fn, sizeof(fn), "digits/nl-en");
1168                         } else {
1169                                 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1170                                 num = 0;
1171                         }
1172                 } else {
1173                         if (num < 1000) {
1174                                 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1175                                 playh++;
1176                                 num -= ((num / 100) * 100);
1177                         } else {
1178                                 if (num < 1000000) { /* 1,000,000 */
1179                                         res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1180                                         if (res)
1181                                                 return res;
1182                                         num = num % 1000;
1183                                         snprintf(fn, sizeof(fn), "digits/thousand");
1184                                 } else {
1185                                         if (num < 1000000000) { /* 1,000,000,000 */
1186                                                 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1187                                                 if (res)
1188                                                         return res;
1189                                                 num = num % 1000000;
1190                                                 snprintf(fn, sizeof(fn), "digits/million");
1191                                         } else {
1192                                                 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1193                                                 res = -1;
1194                                         }
1195                                 }
1196                         }
1197                 }
1198
1199                 if (!res) {
1200                         if(!ast_streamfile(chan, fn, language)) {
1201                                 if (audiofd && ctrlfd)
1202                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1203                                 else
1204                                         res = ast_waitstream(chan, ints);
1205                         }
1206                         ast_stopstream(chan);
1207                 }
1208         }
1209         return res;
1210 }
1211
1212 /* ast_say_number_full_pt: Portuguese syntax */
1213 /*      Extra sounds needed: */
1214 /*      For feminin all sound files end with F */
1215 /*      100E for 100+ something */
1216 /*      1000000S for plural */
1217 /*      pt-e for 'and' */
1218 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1219 {
1220         int res = 0;
1221         int playh = 0;
1222         int mf = 1;                            /* +1 = Masculin; -1 = Feminin */
1223         char fn[256] = "";
1224
1225         if (!num) 
1226                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1227
1228         if (options && !strncasecmp(options, "f",1))
1229                 mf = -1;
1230
1231         while(!res && num ) {
1232                 if (num < 20) {
1233                         if ((num == 1 || num == 2) && (mf < 0))
1234                                 snprintf(fn, sizeof(fn), "digits/%dF", num);
1235                         else
1236                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1237                         num = 0;
1238                 } else if (num < 100) {
1239                         snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1240                         if (num % 10)
1241                                 playh = 1;
1242                         num = num % 10;
1243                 } else if (num < 1000) {
1244                         if (num == 100)
1245                                 snprintf(fn, sizeof(fn), "digits/100");
1246                         else if (num < 200)
1247                                 snprintf(fn, sizeof(fn), "digits/100E");
1248                         else {
1249                                 if (mf < 0 && num > 199)
1250                                         snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1251                                 else
1252                                         snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1253                                 if (num % 100)
1254                                         playh = 1;
1255                         }
1256                         num = num % 100;
1257                 } else if (num < 1000000) {
1258                         if (num > 1999) {
1259                                 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1260                                 if (res)
1261                                         return res;
1262                         }
1263                         snprintf(fn, sizeof(fn), "digits/1000");
1264                         if ((num % 1000) && ((num % 1000) < 100  || !(num % 100)))
1265                                 playh = 1;
1266                         num = num % 1000;
1267                 } else if (num < 1000000000) {
1268                         res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
1269                         if (res)
1270                                 return res;
1271                         if (num < 2000000)
1272                                 snprintf(fn, sizeof(fn), "digits/1000000");
1273                         else
1274                                 snprintf(fn, sizeof(fn), "digits/1000000S");
1275  
1276                         if ((num % 1000000) &&
1277                                 // no thousands
1278                                 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
1279                                 // no hundreds and below
1280                                 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
1281                                 playh = 1;
1282                         num = num % 1000000;
1283                 }
1284                 if (!res && playh) {
1285                         res = wait_file(chan, ints, "digits/pt-e", language);
1286                         ast_stopstream(chan);
1287                         playh = 0;
1288                 }
1289                 if (!res) {
1290                         if(!ast_streamfile(chan, fn, language)) {
1291                                 if (audiofd && ctrlfd)
1292                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);                 else
1293                                         res = ast_waitstream(chan, ints);
1294                         }
1295                         ast_stopstream(chan);
1296                 }
1297         }
1298         return res;
1299 }
1300
1301 /*--- ast_say_number_full_se: Swedish/Norwegian syntax */
1302 static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1303 {
1304         int res = 0;
1305         int playh = 0;
1306         char fn[256] = "";
1307         int cn = 1;             /* +1 = Commune; -1 = Neutrum */
1308         if (!num) 
1309                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1310         if (options && !strncasecmp(options, "n",1)) cn = -1;
1311
1312         while(!res && (num || playh)) {
1313                         if (playh) {
1314                                 snprintf(fn, sizeof(fn), "digits/hundred");
1315                                 playh = 0;
1316                         } else
1317                         if (num < 20) {
1318                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1319                                 num = 0;
1320                         } else
1321                         if (num < 100) {
1322                                 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1323                                 num -= ((num / 10) * 10);
1324                         } else 
1325                         if (num == 1 && cn == -1) {     /* En eller ett? */
1326                                 snprintf(fn, sizeof(fn), "digits/1N");
1327                                 num = 0;
1328                         } else {
1329                                 if (num < 1000){
1330                                         snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1331                                         playh++;
1332                                         num -= ((num / 100) * 100);
1333                                 } else {
1334                                         if (num < 1000000) { /* 1,000,000 */
1335                                                 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1336                                                 if (res)
1337                                                         return res;
1338                                                 num = num % 1000;
1339                                                 snprintf(fn, sizeof(fn), "digits/thousand");
1340                                         } else {
1341                                                 if (num < 1000000000) { /* 1,000,000,000 */
1342                                                         res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1343                                                         if (res)
1344                                                                 return res;
1345                                                         num = num % 1000000;
1346                                                         snprintf(fn, sizeof(fn), "digits/million");
1347                                                 } else {
1348                                                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1349                                                         res = -1;
1350                                                 }
1351                                         }
1352                                 }
1353                         }
1354                          if (!res) {
1355                                 if(!ast_streamfile(chan, fn, language)) {
1356                                     if (audiofd && ctrlfd)
1357                                         res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1358                                     else
1359                                          res = ast_waitstream(chan, ints);
1360                                 }
1361                                 ast_stopstream(chan);
1362
1363                         }
1364                         
1365         }
1366         return res;
1367 }
1368
1369
1370 /*--- ast_say_number_full_tw: Taiwanese syntax */
1371 static int ast_say_number_full_tw(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
1372 {
1373         int res = 0;
1374         int playh = 0;
1375         char fn[256] = "";
1376         if (!num)
1377                 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1378
1379         while(!res && (num || playh)) {
1380                 if (playh) {
1381                                 snprintf(fn, sizeof(fn), "digits/hundred");
1382                                 playh = 0;
1383                         } else  if (num < 10) {
1384                                 snprintf(fn, sizeof(fn), "digits/%d", num);
1385                                 num = 0;
1386                         } else  if (num < 100) {
1387                                 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1388                                 num -= ((num / 10) * 10);
1389                         } else {
1390                                 if (num < 1000){
1391                                         snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1392                                         playh++;
1393                                         num -= ((num / 100) * 100);
1394                                 } else {
1395                                         if (num < 1000000) { /* 1,000,000 */
1396                                                 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1397                                                 if (res)
1398                                                         return res;
1399                                                 num = num % 1000;
1400                                                 snprintf(fn, sizeof(fn), "digits/thousand");
1401                                         } else {
1402                                                 if (num < 1000000000) { /* 1,000,000,000 */
1403                                                         res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1404                                                         if (res)
1405                                                                 return res;
1406                                                         num = num % 1000000;
1407                                                         snprintf(fn, sizeof(fn), "digits/million");
1408                                                 } else {
1409                                                         ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1410                                                         res = -1;
1411                                                 }
1412                                         }
1413                                 }
1414                         }
1415                         if (!res) {
1416                                 if(!ast_streamfile(chan, fn, language)) {
1417                                         if (audiofd && ctrlfd)
1418                                                 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1419                                         else
1420                                                 res = ast_waitstream(chan, ints);
1421                                 }
1422                                 ast_stopstream(chan);
1423
1424                         }
1425         }
1426         return res;
1427 }
1428
1429
1430 int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang)
1431 {
1432         if (!strcasecmp(lang,"en") ) {  /* English syntax */
1433                 return(ast_say_date_en(chan, t, ints, lang));
1434         } else if (!strcasecmp(lang, "nl") ) {  /* Dutch syntax */
1435                 return(ast_say_date_nl(chan, t, ints, lang));
1436         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
1437                 return(ast_say_date_pt(chan, t, ints, lang));
1438         }
1439
1440         /* Default to English */
1441         return(ast_say_date_en(chan, t, ints, lang));
1442 }
1443
1444
1445 /* English syntax */
1446 int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
1447 {
1448         struct tm tm;
1449         char fn[256];
1450         int res = 0;
1451         ast_localtime(&t,&tm,NULL);
1452         if (!res) {
1453                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1454                 res = ast_streamfile(chan, fn, lang);
1455                 if (!res)
1456                         res = ast_waitstream(chan, ints);
1457         }
1458         if (!res) {
1459                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1460                 res = ast_streamfile(chan, fn, lang);
1461                 if (!res)
1462                         res = ast_waitstream(chan, ints);
1463         }
1464         if (!res)
1465                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1466         if (!res)
1467                 res = ast_waitstream(chan, ints);
1468         if (!res)
1469                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1470         return res;
1471 }
1472
1473 /* Dutch syntax */
1474 int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
1475 {
1476         struct tm tm;
1477         char fn[256];
1478         int res = 0;
1479         ast_localtime(&t,&tm,NULL);
1480         if (!res) {
1481                 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1482                 res = ast_streamfile(chan, fn, lang);
1483                 if (!res)
1484                         res = ast_waitstream(chan, ints);
1485         }
1486         if (!res)
1487                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1488         if (!res) {
1489                 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1490                 res = ast_streamfile(chan, fn, lang);
1491                 if (!res)
1492                         res = ast_waitstream(chan, ints);
1493         }
1494         if (!res)
1495                 res = ast_waitstream(chan, ints);
1496         if (!res)
1497                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1498         return res;
1499 }
1500
1501 /* Portuguese syntax */
1502 int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
1503 {
1504         struct tm tm;
1505         char fn[256];
1506         int res = 0;
1507         ast_localtime(&t,&tm,NULL);
1508         localtime_r(&t,&tm);
1509         snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1510         if (!res)
1511                 res = wait_file(chan, ints, fn, lang);
1512         if (!res)
1513                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1514         if (!res)
1515                 res = wait_file(chan, ints, "digits/pt-de", lang);
1516         snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1517         if (!res)
1518                 res = wait_file(chan, ints, fn, lang);
1519         if (!res)
1520                 res = wait_file(chan, ints, "digits/pt-de", lang);
1521         if (!res)
1522                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1523
1524         return res;
1525 }
1526
1527 int ast_say_date_with_format(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1528 {
1529         if (!strcasecmp(lang, "en") ) { /* English syntax */
1530                 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1531         } else if (!strcasecmp(lang, "de") ) {  /* German syntax */
1532                 return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
1533         } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) {        /* Spanish syntax */
1534                 return(ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
1535         } else if (!strcasecmp(lang, "nl") ) {  /* Dutch syntax */
1536                 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
1537         } else if (!strcasecmp(lang, "pt") ) {  /* Portuguese syntax */
1538                 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
1539         } else if (!strcasecmp(lang, "tw") ) {  /* Taiwanese syntax */
1540                 return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
1541         }
1542
1543         /* Default to English */
1544         return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1545 }
1546
1547 /* English syntax */
1548 int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1549 {
1550         struct tm tm;
1551         int res=0, offset, sndoffset;
1552         char sndfile[256], nextmsg[256];
1553
1554         ast_localtime(&time,&tm,timezone);
1555
1556         for (offset=0 ; format[offset] != '\0' ; offset++) {
1557                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1558                 switch (format[offset]) {
1559                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
1560                         case '\'':
1561                                 /* Literal name of a sound file */
1562                                 sndoffset=0;
1563                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1564                                         sndfile[sndoffset] = format[offset];
1565                                 sndfile[sndoffset] = '\0';
1566                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1567                                 res = wait_file(chan,ints,nextmsg,lang);
1568                                 break;
1569                         case 'A':
1570                         case 'a':
1571                                 /* Sunday - Saturday */
1572                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1573                                 res = wait_file(chan,ints,nextmsg,lang);
1574                                 break;
1575                         case 'B':
1576                         case 'b':
1577                         case 'h':
1578                                 /* January - December */
1579                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1580                                 res = wait_file(chan,ints,nextmsg,lang);
1581                                 break;
1582                         case 'd':
1583                         case 'e':
1584                                 /* First - Thirtyfirst */
1585                                 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1586                                         snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
1587                                         res = wait_file(chan,ints,nextmsg,lang);
1588                                 } else if (tm.tm_mday == 31) {
1589                                         /* "Thirty" and "first" */
1590                                         res = wait_file(chan,ints, "digits/30",lang);
1591                                         if (!res) {
1592                                                 res = wait_file(chan,ints, "digits/h-1",lang);
1593                                         }
1594                                 } else {
1595                                         /* Between 21 and 29 - two sounds */
1596                                         res = wait_file(chan,ints, "digits/20",lang);
1597                                         if (!res) {
1598                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
1599                                                 res = wait_file(chan,ints,nextmsg,lang);
1600                                         }
1601                                 }
1602                                 break;
1603                         case 'Y':
1604                                 /* Year */
1605                                 if (tm.tm_year > 99) {
1606                                         res = wait_file(chan,ints, "digits/2",lang);
1607                                         if (!res) {
1608                                                 res = wait_file(chan,ints, "digits/thousand",lang);
1609                                         }
1610                                         if (tm.tm_year > 100) {
1611                                                 if (!res) {
1612                                                         /* This works until the end of 2020 */
1613                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1614                                                         res = wait_file(chan,ints,nextmsg,lang);
1615                                                 }
1616                                         }
1617                                 } else {
1618                                         if (tm.tm_year < 1) {
1619                                                 /* I'm not going to handle 1900 and prior */
1620                                                 /* We'll just be silent on the year, instead of bombing out. */
1621                                         } else {
1622                                                 res = wait_file(chan,ints, "digits/19",lang);
1623                                                 if (!res) {
1624                                                         if (tm.tm_year <= 9) {
1625                                                                 /* 1901 - 1909 */
1626                                                                 res = wait_file(chan,ints, "digits/oh",lang);
1627                                                                 if (!res) {
1628                                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1629                                                                         res = wait_file(chan,ints,nextmsg,lang);
1630                                                                 }
1631                                                         } else if (tm.tm_year <= 20) {
1632                                                                 /* 1910 - 1920 */
1633                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1634                                                                 res = wait_file(chan,ints,nextmsg,lang);
1635                                                         } else {
1636                                                                 /* 1921 - 1999 */
1637                                                                 int ten, one;
1638                                                                 ten = tm.tm_year / 10;
1639                                                                 one = tm.tm_year % 10;
1640                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1641                                                                 res = wait_file(chan,ints,nextmsg,lang);
1642                                                                 if (!res) {
1643                                                                         if (one != 0) {
1644                                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1645                                                                                 res = wait_file(chan,ints,nextmsg,lang);
1646                                                                         }
1647                                                                 }
1648                                                         }
1649                                                 }
1650                                         }
1651                                 }
1652                                 break;
1653                         case 'I':
1654                         case 'l':
1655                                 /* 12-Hour */
1656                                 if (tm.tm_hour == 0)
1657                                         snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1658                                 else if (tm.tm_hour > 12)
1659                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1660                                 else
1661                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1662                                 res = wait_file(chan,ints,nextmsg,lang);
1663                                 break;
1664                         case 'H':
1665                         case 'k':
1666                                 /* 24-Hour */
1667                                 if (format[offset] == 'H') {
1668                                         /* e.g. oh-eight */
1669                                         if (tm.tm_hour < 10) {
1670                                                 res = wait_file(chan,ints, "digits/oh",lang);
1671                                         }
1672                                 } else {
1673                                         /* e.g. eight */
1674                                         if (tm.tm_hour == 0) {
1675                                                 res = wait_file(chan,ints, "digits/oh",lang);
1676                                         }
1677                                 }
1678                                 if (!res) {
1679                                         if (tm.tm_hour != 0) {
1680                                                 int remainder = tm.tm_hour;
1681                                                 if (tm.tm_hour > 20) {
1682                                                         res = wait_file(chan,ints, "digits/20",lang);
1683                                                         remainder -= 20;
1684                                                 }
1685                                                 if (!res) {
1686                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1687                                                         res = wait_file(chan,ints,nextmsg,lang);
1688                                                 }
1689                                         }
1690                                 }
1691                                 break;
1692                         case 'M':
1693                                 /* Minute */
1694                                 if (tm.tm_min == 0) {
1695                                         res = wait_file(chan,ints, "digits/oclock",lang);
1696                                 } else if (tm.tm_min < 10) {
1697                                         res = wait_file(chan,ints, "digits/oh",lang);
1698                                         if (!res) {
1699                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1700                                                 res = wait_file(chan,ints,nextmsg,lang);
1701                                         }
1702                                 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
1703                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1704                                         res = wait_file(chan,ints,nextmsg,lang);
1705                                 } else {
1706                                         int ten, one;
1707                                         ten = (tm.tm_min / 10) * 10;
1708                                         one = (tm.tm_min % 10);
1709                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1710                                         res = wait_file(chan,ints,nextmsg,lang);
1711                                         if (!res) {
1712                                                 /* Fifty, not fifty-zero */
1713                                                 if (one != 0) {
1714                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1715                                                         res = wait_file(chan,ints,nextmsg,lang);
1716                                                 }
1717                                         }
1718                                 }
1719                                 break;
1720                         case 'P':
1721                         case 'p':
1722                                 /* AM/PM */
1723                                 if (tm.tm_hour > 11)
1724                                         snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
1725                                 else
1726                                         snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
1727                                 res = wait_file(chan,ints,nextmsg,lang);
1728                                 break;
1729                         case 'Q':
1730                                 /* Shorthand for "Today", "Yesterday", or ABdY */
1731                                 {
1732                                         struct timeval now;
1733                                         struct tm tmnow;
1734                                         time_t beg_today;
1735
1736                                         gettimeofday(&now,NULL);
1737                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1738                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1739                                         /* In any case, it saves not having to do ast_mktime() */
1740                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1741                                         if (beg_today < time) {
1742                                                 /* Today */
1743                                                 res = wait_file(chan,ints, "digits/today",lang);
1744                                         } else if (beg_today - 86400 < time) {
1745                                                 /* Yesterday */
1746                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1747                                         } else {
1748                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1749                                         }
1750                                 }
1751                                 break;
1752                         case 'q':
1753                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
1754                                 {
1755                                         struct timeval now;
1756                                         struct tm tmnow;
1757                                         time_t beg_today;
1758
1759                                         gettimeofday(&now,NULL);
1760                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
1761                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
1762                                         /* In any case, it saves not having to do ast_mktime() */
1763                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
1764                                         if (beg_today < time) {
1765                                                 /* Today */
1766                                         } else if ((beg_today - 86400) < time) {
1767                                                 /* Yesterday */
1768                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
1769                                         } else if (beg_today - 86400 * 6 < time) {
1770                                                 /* Within the last week */
1771                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
1772                                         } else {
1773                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
1774                                         }
1775                                 }
1776                                 break;
1777                         case 'R':
1778                                 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
1779                                 break;
1780                         case 'S':
1781                                 /* Seconds */
1782                                 if (tm.tm_sec == 0) {
1783                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1784                                         res = wait_file(chan,ints,nextmsg,lang);
1785                                 } else if (tm.tm_sec < 10) {
1786                                         res = wait_file(chan,ints, "digits/oh",lang);
1787                                         if (!res) {
1788                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1789                                                 res = wait_file(chan,ints,nextmsg,lang);
1790                                         }
1791                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
1792                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
1793                                         res = wait_file(chan,ints,nextmsg,lang);
1794                                 } else {
1795                                         int ten, one;
1796                                         ten = (tm.tm_sec / 10) * 10;
1797                                         one = (tm.tm_sec % 10);
1798                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1799                                         res = wait_file(chan,ints,nextmsg,lang);
1800                                         if (!res) {
1801                                                 /* Fifty, not fifty-zero */
1802                                                 if (one != 0) {
1803                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1804                                                         res = wait_file(chan,ints,nextmsg,lang);
1805                                                 }
1806                                         }
1807                                 }
1808                                 break;
1809                         case 'T':
1810                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
1811                                 break;
1812                         case ' ':
1813                         case '  ':
1814                                 /* Just ignore spaces and tabs */
1815                                 break;
1816                         default:
1817                                 /* Unknown character */
1818                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
1819                 }
1820                 /* Jump out on DTMF */
1821                 if (res) {
1822                         break;
1823                 }
1824         }
1825         return res;
1826 }
1827
1828 /* German syntax */
1829 /* NB This currently is a 100% clone of the English syntax, just getting ready to make changes... */
1830 int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1831 {
1832         struct tm tm;
1833         int res=0, offset, sndoffset;
1834         char sndfile[256], nextmsg[256];
1835
1836         ast_localtime(&time,&tm,timezone);
1837
1838         for (offset=0 ; format[offset] != '\0' ; offset++) {
1839                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1840                 switch (format[offset]) {
1841                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
1842                         case '\'':
1843                                 /* Literal name of a sound file */
1844                                 sndoffset=0;
1845                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1846                                         sndfile[sndoffset] = format[offset];
1847                                 sndfile[sndoffset] = '\0';
1848                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1849                                 res = wait_file(chan,ints,nextmsg,lang);
1850                                 break;
1851                         case 'A':
1852                         case 'a':
1853                                 /* Sunday - Saturday */
1854                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1855                                 res = wait_file(chan,ints,nextmsg,lang);
1856                                 break;
1857                         case 'B':
1858                         case 'b':
1859                         case 'h':
1860                                 /* January - December */
1861                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1862                                 res = wait_file(chan,ints,nextmsg,lang);
1863                                 break;
1864                         case 'd':
1865                         case 'e':
1866                                 /* First - Thirtyfirst */
1867                                 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1868                                         snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
1869                                         res = wait_file(chan,ints,nextmsg,lang);
1870                                 } else if (tm.tm_mday == 31) {
1871                                         /* "Thirty" and "first" */
1872                                         res = wait_file(chan,ints, "digits/30",lang);
1873                                         if (!res) {
1874                                                 res = wait_file(chan,ints, "digits/h-1",lang);
1875                                         }
1876                                 } else {
1877                                         /* Between 21 and 29 - two sounds */
1878                                         res = wait_file(chan,ints, "digits/20",lang);
1879                                         if (!res) {
1880                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
1881                                                 res = wait_file(chan,ints,nextmsg,lang);
1882                                         }
1883                                 }
1884                                 break;
1885                         case 'Y':
1886                                 /* Year */
1887                                 if (tm.tm_year > 99) {
1888                                         res = wait_file(chan,ints, "digits/2",lang);
1889                                         if (!res) {
1890                                                 res = wait_file(chan,ints, "digits/thousand",lang);
1891                                         }
1892                                         if (tm.tm_year > 100) {
1893                                                 if (!res) {
1894                                                         /* This works until the end of 2020 */
1895                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1896                                                         res = wait_file(chan,ints,nextmsg,lang);
1897                                                 }
1898                                         }
1899                                 } else {
1900                                         if (tm.tm_year < 1) {
1901                                                 /* I'm not going to handle 1900 and prior */
1902                                                 /* We'll just be silent on the year, instead of bombing out. */
1903                                         } else {
1904                                                 res = wait_file(chan,ints, "digits/19",lang);
1905                                                 if (!res) {
1906                                                         if (tm.tm_year <= 9) {
1907                                                                 /* 1901 - 1909 */
1908                                                                 res = wait_file(chan,ints, "digits/oh",lang);
1909                                                                 if (!res) {
1910                                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1911                                                                         res = wait_file(chan,ints,nextmsg,lang);
1912                                                                 }
1913                                                         } else if (tm.tm_year <= 20) {
1914                                                                 /* 1910 - 1920 */
1915                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1916                                                                 res = wait_file(chan,ints,nextmsg,lang);
1917                                                         } else {
1918                                                                 /* 1921 - 1999 */
1919                                                                 int ten, one;
1920                                                                 ten = tm.tm_year / 10;
1921                                                                 one = tm.tm_year % 10;
1922                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1923                                                                 res = wait_file(chan,ints,nextmsg,lang);
1924                                                                 if (!res) {
1925                                                                         if (one != 0) {
1926                                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1927                                                                                 res = wait_file(chan,ints,nextmsg,lang);
1928                                                                         }
1929                                                                 }
1930                                                         }
1931                                                 }
1932                                         }
1933                                 }
1934                                 break;
1935                         case 'I':
1936                         case 'l':
1937                                 /* 12-Hour */
1938                                 if (tm.tm_hour == 0)
1939                                         snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1940                                 else if (tm.tm_hour > 12)
1941                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1942                                 else
1943                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1944                                 res = wait_file(chan,ints,nextmsg,lang);
1945                                 break;
1946                         case 'H':
1947                         case 'k':
1948                                 /* 24-Hour */
1949                                 if (format[offset] == 'H') {
1950                                         /* e.g. oh-eight */
1951                                         if (tm.tm_hour < 10) {
1952                                                 res = wait_file(chan,ints, "digits/oh",lang);
1953                                         }
1954                                 } else {
1955                                         /* e.g. eight */
1956                                         if (tm.tm_hour == 0) {
1957                                                 res = wait_file(chan,ints, "digits/oh",lang);
1958                                         }
1959                                 }
1960                                 if (!res) {
1961                                         if (tm.tm_hour != 0) {
1962                                                 int remainder = tm.tm_hour;
1963                                                 if (tm.tm_hour > 20) {
1964                                                         res = wait_file(chan,ints, "digits/20",lang);
1965                                                         remainder -= 20;
1966                                                 }
1967                                                 if (!res) {
1968                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1969                                                         res = wait_file(chan,ints,nextmsg,lang);
1970                                                 }
1971                                         }
1972                                 }
1973                                 break;
1974                         case 'M':
1975                                 /* Minute */
1976                                 if (tm.tm_min == 0) {
1977                                         res = wait_file(chan,ints, "digits/oclock",lang);
1978                                 } else if (tm.tm_min < 10) {
1979                                         res = wait_file(chan,ints, "digits/oh",lang);
1980                                         if (!res) {
1981                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1982                                                 res = wait_file(chan,ints,nextmsg,lang);
1983                                         }
1984                                 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
1985                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
1986                                         res = wait_file(chan,ints,nextmsg,lang);
1987                                 } else {
1988                                         int ten, one;
1989                                         ten = (tm.tm_min / 10) * 10;
1990                                         one = (tm.tm_min % 10);
1991                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
1992                                         res = wait_file(chan,ints,nextmsg,lang);
1993                                         if (!res) {
1994                                                 /* Fifty, not fifty-zero */
1995                                                 if (one != 0) {
1996                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1997                                                         res = wait_file(chan,ints,nextmsg,lang);
1998                                                 }
1999                                         }
2000                                 }
2001                                 break;
2002                         case 'P':
2003                         case 'p':
2004                                 /* AM/PM */
2005                                 if (tm.tm_hour > 11)
2006                                         snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
2007                                 else
2008                                         snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
2009                                 res = wait_file(chan,ints,nextmsg,lang);
2010                                 break;
2011                         case 'Q':
2012                                 /* Shorthand for "Today", "Yesterday", or ABdY */
2013                                 {
2014                                         struct timeval now;
2015                                         struct tm tmnow;
2016                                         time_t beg_today;
2017
2018                                         gettimeofday(&now,NULL);
2019                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2020                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2021                                         /* In any case, it saves not having to do ast_mktime() */
2022                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2023                                         if (beg_today < time) {
2024                                                 /* Today */
2025                                                 res = wait_file(chan,ints, "digits/today",lang);
2026                                         } else if (beg_today - 86400 < time) {
2027                                                 /* Yesterday */
2028                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2029                                         } else {
2030                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2031                                         }
2032                                 }
2033                                 break;
2034                         case 'q':
2035                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2036                                 {
2037                                         struct timeval now;
2038                                         struct tm tmnow;
2039                                         time_t beg_today;
2040
2041                                         gettimeofday(&now,NULL);
2042                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2043                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2044                                         /* In any case, it saves not having to do ast_mktime() */
2045                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2046                                         if (beg_today < time) {
2047                                                 /* Today */
2048                                         } else if ((beg_today - 86400) < time) {
2049                                                 /* Yesterday */
2050                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2051                                         } else if (beg_today - 86400 * 6 < time) {
2052                                                 /* Within the last week */
2053                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2054                                         } else {
2055                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2056                                         }
2057                                 }
2058                                 break;
2059                         case 'R':
2060                                 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
2061                                 break;
2062                         case 'S':
2063                                 /* Seconds */
2064                                 if (tm.tm_sec == 0) {
2065                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2066                                         res = wait_file(chan,ints,nextmsg,lang);
2067                                 } else if (tm.tm_sec < 10) {
2068                                         res = wait_file(chan,ints, "digits/oh",lang);
2069                                         if (!res) {
2070                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2071                                                 res = wait_file(chan,ints,nextmsg,lang);
2072                                         }
2073                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2074                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2075                                         res = wait_file(chan,ints,nextmsg,lang);
2076                                 } else {
2077                                         int ten, one;
2078                                         ten = (tm.tm_sec / 10) * 10;
2079                                         one = (tm.tm_sec % 10);
2080                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2081                                         res = wait_file(chan,ints,nextmsg,lang);
2082                                         if (!res) {
2083                                                 /* Fifty, not fifty-zero */
2084                                                 if (one != 0) {
2085                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2086                                                         res = wait_file(chan,ints,nextmsg,lang);
2087                                                 }
2088                                         }
2089                                 }
2090                                 break;
2091                         case 'T':
2092                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2093                                 break;
2094                         case ' ':
2095                         case '  ':
2096                                 /* Just ignore spaces and tabs */
2097                                 break;
2098                         default:
2099                                 /* Unknown character */
2100                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2101                 }
2102                 /* Jump out on DTMF */
2103                 if (res) {
2104                         break;
2105                 }
2106         }
2107         return res;
2108 }
2109
2110 /* Spanish syntax */
2111 int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
2112 {
2113         struct tm tm;
2114         int res=0, offset, sndoffset;
2115         char sndfile[256], nextmsg[256];
2116
2117         ast_localtime(&time,&tm,timezone);
2118
2119         for (offset=0 ; format[offset] != '\0' ; offset++) {
2120                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2121                 switch (format[offset]) {
2122                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
2123                         case '\'':
2124                                 /* Literal name of a sound file */
2125                                 sndoffset=0;
2126                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2127                                         sndfile[sndoffset] = format[offset];
2128                                 sndfile[sndoffset] = '\0';
2129                                 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
2130                                 res = wait_file(chan,ints,nextmsg,lang);
2131                                 break;
2132                         case 'A':
2133                         case 'a':
2134                                 /* Sunday - Saturday */
2135                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2136                                 res = wait_file(chan,ints,nextmsg,lang);
2137                                 break;
2138                         case 'B':
2139                         case 'b':
2140                         case 'h':
2141                                 /* January - December */
2142                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2143                                 res = wait_file(chan,ints,nextmsg,lang);
2144                                 break;
2145                         case 'd':
2146                         case 'e':
2147                                 /* First - Thirtyfirst */
2148                                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2149                                 break;
2150                         case 'Y':
2151                                 /* Year */
2152                                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2153                                 break;
2154                         case 'I':
2155                         case 'l':
2156                                 /* 12-Hour */
2157                                 if (tm.tm_hour == 0)
2158                                         snprintf(nextmsg,sizeof(nextmsg), "digits/12");
2159                                 else if (tm.tm_hour > 12)
2160                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
2161                                 else
2162                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
2163                                 res = wait_file(chan,ints,nextmsg,lang);
2164                                 break;
2165                         case 'H':
2166                         case 'k':
2167                                 /* 24-Hour */
2168                                 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
2169                                 if (!res) {
2170                                         if (tm.tm_hour != 0) {
2171                                                 int remainder = tm.tm_hour;
2172                                                 if (tm.tm_hour > 20) {
2173                                                         res = wait_file(chan,ints, "digits/20",lang);
2174                                                         remainder -= 20;
2175                                                 }
2176                                                 if (!res) {
2177                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
2178                                                         res = wait_file(chan,ints,nextmsg,lang);
2179                                                 }
2180                                         }
2181                                 }
2182                                 break;
2183                         case 'M':
2184                                 /* Minute */
2185                                 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);       
2186                                 break;
2187                         case 'P':
2188                         case 'p':
2189                                 /* AM/PM */
2190                                 if (tm.tm_hour > 12)
2191                                         res = wait_file(chan, ints, "digits/p-m", lang);
2192                                 else if (tm.tm_hour  && tm.tm_hour < 12)
2193                                         res = wait_file(chan, ints, "digits/a-m", lang);
2194                                 break;
2195                         case 'Q':
2196                                 /* Shorthand for "Today", "Yesterday", or ABdY */
2197                                 {
2198                                         struct timeval now;
2199                                         struct tm tmnow;
2200                                         time_t beg_today;
2201
2202                                         gettimeofday(&now,NULL);
2203                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2204                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2205                                         /* In any case, it saves not having to do ast_mktime() */
2206                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2207                                         if (beg_today < time) {
2208                                                 /* Today */
2209                                                 res = wait_file(chan,ints, "digits/today",lang);
2210                                         } else if (beg_today - 86400 < time) {
2211                                                 /* Yesterday */
2212                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2213                                         } else {
2214                                                 res = ast_say_date_with_format(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
2215                                         }
2216                                 }
2217                                 break;
2218                         case 'q':
2219                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2220                                 {
2221                                         struct timeval now;
2222                                         struct tm tmnow;
2223                                         time_t beg_today;
2224
2225                                         gettimeofday(&now,NULL);
2226                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2227                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2228                                         /* In any case, it saves not having to do ast_mktime() */
2229                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2230                                         if (beg_today < time) {
2231                                                 /* Today */
2232                                                 res = wait_file(chan,ints, "digits/today",lang);
2233                                         } else if ((beg_today - 86400) < time) {
2234                                                 /* Yesterday */
2235                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2236                                         } else if (beg_today - 86400 * 6 < time) {
2237                                                 /* Within the last week */
2238                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2239                                         } else {
2240                                                 res = ast_say_date_with_format(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
2241                                         }
2242                                 }
2243                                 break;
2244                         case 'R':
2245                                 res = ast_say_date_with_format(chan, time, ints, lang, "H 'digits/y' M", timezone);
2246                                 break;
2247                         case 'S':
2248                                 /* Seconds */
2249                                 if (tm.tm_sec == 0) {
2250                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2251                                         res = wait_file(chan,ints,nextmsg,lang);
2252                                 } else if (tm.tm_sec < 10) {
2253                                         res = wait_file(chan,ints, "digits/oh",lang);
2254                                         if (!res) {
2255                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2256                                                 res = wait_file(chan,ints,nextmsg,lang);
2257                                         }
2258                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2259                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2260                                         res = wait_file(chan,ints,nextmsg,lang);
2261                                 } else {
2262                                         int ten, one;
2263                                         ten = (tm.tm_sec / 10) * 10;
2264                                         one = (tm.tm_sec % 10);
2265                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2266                                         res = wait_file(chan,ints,nextmsg,lang);
2267                                         if (!res) {
2268                                                 /* Fifty, not fifty-zero */
2269                                                 if (one != 0) {
2270                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2271                                                         res = wait_file(chan,ints,nextmsg,lang);
2272                                                 }
2273                                         }
2274                                 }
2275                                 break;
2276                         case 'T':
2277                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2278                                 break;
2279                         case ' ':
2280                         case '  ':
2281                                 /* Just ignore spaces and tabs */
2282                                 break;
2283                         default:
2284                                 /* Unknown character */
2285                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2286                 }
2287                 /* Jump out on DTMF */
2288                 if (res) {
2289                         break;
2290                 }
2291         }
2292         return res;
2293 }
2294
2295 /* Dutch syntax */
2296 int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
2297 {
2298         struct tm tm;
2299         int res=0, offset, sndoffset;
2300         char sndfile[256], nextmsg[256];
2301
2302         ast_localtime(&time,&tm,timezone);
2303
2304         for (offset=0 ; format[offset] != '\0' ; offset++) {
2305                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2306                 switch (format[offset]) {
2307                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
2308                         case '\'':
2309                                 /* Literal name of a sound file */
2310                                 sndoffset=0;
2311                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2312                                         sndfile[sndoffset] = format[offset];
2313                                 sndfile[sndoffset] = '\0';
2314                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
2315                                 res = wait_file(chan,ints,nextmsg,lang);
2316                                 break;
2317                         case 'A':
2318                         case 'a':
2319                                 /* Sunday - Saturday */
2320                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2321                                 res = wait_file(chan,ints,nextmsg,lang);
2322                                 break;
2323                         case 'B':
2324                         case 'b':
2325                         case 'h':
2326                                 /* January - December */
2327                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2328                                 res = wait_file(chan,ints,nextmsg,lang);
2329                                 break;
2330                         case 'd':
2331                         case 'e':
2332                                 /* First - Thirtyfirst */
2333                                 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
2334                                 break;
2335                         case 'Y':
2336                                 /* Year */
2337                                 if (tm.tm_year > 99) {
2338                                         res = wait_file(chan,ints, "digits/2",lang);
2339                                         if (!res) {
2340                                                 res = wait_file(chan,ints, "digits/thousand",lang);
2341                                         }
2342                                         if (tm.tm_year > 100) {
2343                                                 if (!res) {
2344                                                         /* This works until the end of 2020 */
2345                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
2346                                                         res = wait_file(chan,ints,nextmsg,lang);
2347                                                 }
2348                                         }
2349                                 } else {
2350                                         if (tm.tm_year < 1) {
2351                                                 /* I'm not going to handle 1900 and prior */
2352                                                 /* We'll just be silent on the year, instead of bombing out. */
2353                                         } else {
2354                                                 res = wait_file(chan,ints, "digits/19",lang);
2355                                                 if (!res) {
2356                                                         if (tm.tm_year <= 9) {
2357                                                                 /* 1901 - 1909 */
2358                                                                 res = wait_file(chan,ints, "digits/oh",lang);
2359                                                                 if (!res) {
2360                                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
2361                                                                         res = wait_file(chan,ints,nextmsg,lang);
2362                                                                 }
2363                                                         } else if (tm.tm_year <= 20) {
2364                                                                 /* 1910 - 1920 */
2365                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
2366                                                                 res = wait_file(chan,ints,nextmsg,lang);
2367                                                         } else {
2368                                                                 /* 1921 - 1999 */
2369                                                                 int ten, one;
2370                                                                 ten = tm.tm_year / 10;
2371                                                                 one = tm.tm_year % 10;
2372                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
2373                                                                 res = wait_file(chan,ints,nextmsg,lang);
2374                                                                 if (!res) {
2375                                                                         if (one != 0) {
2376                                                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2377                                                                                 res = wait_file(chan,ints,nextmsg,lang);
2378                                                                         }
2379                                                                 }
2380                                                         }
2381                                                 }
2382                                         }
2383                                 }
2384                                 break;
2385                         case 'I':
2386                         case 'l':
2387                                 /* 12-Hour */
2388                                 if (tm.tm_hour == 0)
2389                                         snprintf(nextmsg,sizeof(nextmsg), "digits/12");
2390                                 else if (tm.tm_hour > 12)
2391                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
2392                                 else
2393                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
2394                                 res = wait_file(chan,ints,nextmsg,lang);
2395                                 break;
2396                         case 'H':
2397                         case 'k':
2398                                 /* 24-Hour */
2399                                 res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
2400                                 if (!res) {
2401                                         res = wait_file(chan,ints, "digits/nl-uur",lang);
2402                                 }
2403                                 break;
2404                         case 'M':
2405                                 /* Minute */
2406                                 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2407                                 break;
2408                         case 'P':
2409                         case 'p':
2410                                 /* AM/PM */
2411                                 if (tm.tm_hour > 11)
2412                                         snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
2413                                 else
2414                                         snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
2415                                 res = wait_file(chan,ints,nextmsg,lang);
2416                                 break;
2417                         case 'Q':
2418                                 /* Shorthand for "Today", "Yesterday", or ABdY */
2419                                 {
2420                                         struct timeval now;
2421                                         struct tm tmnow;
2422                                         time_t beg_today;
2423
2424                                         gettimeofday(&now,NULL);
2425                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2426                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2427                                         /* In any case, it saves not having to do ast_mktime() */
2428                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2429                                         if (beg_today < time) {
2430                                                 /* Today */
2431                                                 res = wait_file(chan,ints, "digits/today",lang);
2432                                         } else if (beg_today - 86400 < time) {
2433                                                 /* Yesterday */
2434                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2435                                         } else {
2436                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2437                                         }
2438                                 }
2439                                 break;
2440                         case 'q':
2441                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2442                                 {
2443                                         struct timeval now;
2444                                         struct tm tmnow;
2445                                         time_t beg_today;
2446
2447                                         gettimeofday(&now,NULL);
2448                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2449                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2450                                         /* In any case, it saves not having to do ast_mktime() */
2451                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2452                                         if (beg_today < time) {
2453                                                 /* Today */
2454                                         } else if ((beg_today - 86400) < time) {
2455                                                 /* Yesterday */
2456                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2457                                         } else if (beg_today - 86400 * 6 < time) {
2458                                                 /* Within the last week */
2459                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2460                                         } else {
2461                                                 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2462                                         }
2463                                 }
2464                                 break;
2465                         case 'R':
2466                                 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
2467                                 break;
2468                         case 'S':
2469                                 /* Seconds */
2470                                 if (tm.tm_sec == 0) {
2471                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2472                                         res = wait_file(chan,ints,nextmsg,lang);
2473                                 } else if (tm.tm_sec < 10) {
2474                                         res = wait_file(chan,ints, "digits/oh",lang);
2475                                         if (!res) {
2476                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2477                                                 res = wait_file(chan,ints,nextmsg,lang);
2478                                         }
2479                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2480                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2481                                         res = wait_file(chan,ints,nextmsg,lang);
2482                                 } else {
2483                                         int ten, one;
2484                                         ten = (tm.tm_sec / 10) * 10;
2485                                         one = (tm.tm_sec % 10);
2486                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2487                                         res = wait_file(chan,ints,nextmsg,lang);
2488                                         if (!res) {
2489                                                 /* Fifty, not fifty-zero */
2490                                                 if (one != 0) {
2491                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2492                                                         res = wait_file(chan,ints,nextmsg,lang);
2493                                                 }
2494                                         }
2495                                 }
2496                                 break;
2497                         case 'T':
2498                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2499                                 break;
2500                         case ' ':
2501                         case '  ':
2502                                 /* Just ignore spaces and tabs */
2503                                 break;
2504                         default:
2505                                 /* Unknown character */
2506                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2507                 }
2508                 /* Jump out on DTMF */
2509                 if (res) {
2510                         break;
2511                 }
2512         }
2513         return res;
2514 }
2515
2516 /* Portuguese syntax */
2517 int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
2518 {
2519         struct tm tm;
2520         int res=0, offset, sndoffset;
2521         char sndfile[256], nextmsg[256];
2522
2523         ast_localtime(&time,&tm,timezone);
2524
2525         for (offset=0 ; format[offset] != '\0' ; offset++) {
2526                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2527                 switch (format[offset]) {
2528                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
2529                         case '\'':
2530                                 /* Literal name of a sound file */
2531                                 sndoffset=0;
2532                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2533                                         sndfile[sndoffset] = format[offset];
2534                                 sndfile[sndoffset] = '\0';
2535                                 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
2536                                 res = wait_file(chan,ints,nextmsg,lang);
2537                                 break;
2538                         case 'A':
2539                         case 'a':
2540                                 /* Sunday - Saturday */
2541                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2542                                 res = wait_file(chan,ints,nextmsg,lang);
2543                                 break;
2544                         case 'B':
2545                         case 'b':
2546                         case 'h':
2547                                 /* January - December */
2548                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2549                                 res = wait_file(chan,ints,nextmsg,lang);
2550                                 break;
2551                         case 'd':
2552                         case 'e':
2553                                 /* First - Thirtyfirst */
2554                                 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2555                                 break;
2556                         case 'Y':
2557                                 /* Year */
2558                                 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2559                                 break;
2560                         case 'I':
2561                         case 'l':
2562                                 /* 12-Hour */
2563                                 if (tm.tm_hour == 0) {
2564                                         if (format[offset] == 'I')
2565                                                 res = wait_file(chan, ints, "digits/pt-ah", lang);
2566                                         if (!res)
2567                                                 res = wait_file(chan, ints, "digits/pt-meianoite", lang);
2568                                 }
2569                                 else if (tm.tm_hour == 12) {
2570                                         if (format[offset] == 'I')
2571                                                 res = wait_file(chan, ints, "digits/pt-ao", lang);
2572                                         if (!res)
2573                                                 res = wait_file(chan, ints, "digits/pt-meiodia", lang);
2574                                 }
2575                                 else {
2576                                         if (format[offset] == 'I') {
2577                                                 res = wait_file(chan, ints, "digits/pt-ah", lang);
2578                                                 if ((tm.tm_hour % 12) != 1)
2579                                                         if (!res)
2580                                                                 res = wait_file(chan, ints, "digits/pt-sss", lang);
2581                                         }
2582                                         if (!res)
2583                                                 res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
2584                                 }
2585                                 break;
2586                         case 'H':
2587                         case 'k':
2588                                 /* 24-Hour */
2589                                 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
2590                                 if (!res) {
2591                                         if (tm.tm_hour != 0) {
2592                                                 int remainder = tm.tm_hour;
2593                                                 if (tm.tm_hour > 20) {
2594                                                         res = wait_file(chan,ints, "digits/20",lang);
2595                                                         remainder -= 20;
2596                                                 }
2597                                                 if (!res) {
2598                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
2599                                                         res = wait_file(chan,ints,nextmsg,lang);
2600                                                 }
2601                                         }
2602                                 }
2603                                 break;
2604                         case 'M':
2605                                 /* Minute */
2606                                 if (tm.tm_min == 0) {
2607                                         res = wait_file(chan, ints, "digits/pt-hora", lang);
2608                                         if (tm.tm_hour != 1)
2609                                                 if (!res)
2610                                                         res = wait_file(chan, ints, "digits/pt-sss", lang);                     } else {
2611                                         res = wait_file(chan,ints,"digits/pt-e",lang);
2612                                         if (!res)
2613                                                 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);       
2614                                 }
2615                                 break;
2616                         case 'P':
2617                         case 'p':
2618                                 /* AM/PM */
2619                                 if (tm.tm_hour > 12)
2620                                         res = wait_file(chan, ints, "digits/p-m", lang);
2621                                 else if (tm.tm_hour  && tm.tm_hour < 12)
2622                                         res = wait_file(chan, ints, "digits/a-m", lang);
2623                                 break;
2624                         case 'Q':
2625                                 /* Shorthand for "Today", "Yesterday", or ABdY */
2626                                 {
2627                                         struct timeval now;
2628                                         struct tm tmnow;
2629                                         time_t beg_today;
2630
2631                                         gettimeofday(&now,NULL);
2632                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2633                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2634                                         /* In any case, it saves not having to do ast_mktime() */
2635                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2636                                         if (beg_today < time) {
2637                                                 /* Today */
2638                                                 res = wait_file(chan,ints, "digits/today",lang);
2639                                         } else if (beg_today - 86400 < time) {
2640                                                 /* Yesterday */
2641                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2642                                         } else {
2643                                                 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
2644                                         }
2645                                 }
2646                                 break;
2647                         case 'q':
2648                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2649                                 {
2650                                         struct timeval now;
2651                                         struct tm tmnow;
2652                                         time_t beg_today;
2653
2654                                         gettimeofday(&now,NULL);
2655                                         ast_localtime(&now.tv_sec,&tmnow,timezone);
2656                                         /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2657                                         /* In any case, it saves not having to do ast_mktime() */
2658                                         beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2659                                         if (beg_today < time) {
2660                                                 /* Today */
2661                                         } else if ((beg_today - 86400) < time) {
2662                                                 /* Yesterday */
2663                                                 res = wait_file(chan,ints, "digits/yesterday",lang);
2664                                         } else if (beg_today - 86400 * 6 < time) {
2665                                                 /* Within the last week */
2666                                                 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2667                                         } else {
2668                                                 res = ast_say_date_with_format(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
2669                                         }
2670                                 }
2671                                 break;
2672                         case 'R':
2673                                 res = ast_say_date_with_format(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
2674                                 break;
2675                         case 'S':
2676                                 /* Seconds */
2677                                 if (tm.tm_sec == 0) {
2678                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2679                                         res = wait_file(chan,ints,nextmsg,lang);
2680                                 } else if (tm.tm_sec < 10) {
2681                                         res = wait_file(chan,ints, "digits/oh",lang);
2682                                         if (!res) {
2683                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2684                                                 res = wait_file(chan,ints,nextmsg,lang);
2685                                         }
2686                                 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2687                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2688                                         res = wait_file(chan,ints,nextmsg,lang);
2689                                 } else {
2690                                         int ten, one;
2691                                         ten = (tm.tm_sec / 10) * 10;
2692                                         one = (tm.tm_sec % 10);
2693                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2694                                         res = wait_file(chan,ints,nextmsg,lang);
2695                                         if (!res) {
2696                                                 /* Fifty, not fifty-zero */
2697                                                 if (one != 0) {
2698                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2699                                                         res = wait_file(chan,ints,nextmsg,lang);
2700                                                 }
2701                                         }
2702                                 }
2703                                 break;
2704                         case 'T':
2705                                 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2706                                 break;
2707                         case ' ':
2708                         case '  ':
2709                                 /* Just ignore spaces and tabs */
2710                                 break;
2711                         default:
2712                                 /* Unknown character */
2713                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2714                 }
2715                 /* Jump out on DTMF */
2716                 if (res) {
2717                         break;
2718                 }
2719         }
2720         return res;
2721 }
2722
2723 /* Taiwanese syntax */
2724 int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
2725 {
2726         struct tm tm;
2727         int res=0, offset, sndoffset;
2728         char sndfile[256], nextmsg[256];
2729
2730         ast_localtime(&time,&tm,timezone);
2731
2732         for (offset=0 ; format[offset] != '\0' ; offset++) {
2733                 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2734                 switch (format[offset]) {
2735                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
2736                         case '\'':
2737                                 /* Literal name of a sound file */
2738                                 sndoffset=0;
2739                                 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2740                                         sndfile[sndoffset] = format[offset];
2741                                 sndfile[sndoffset] = '\0';
2742                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
2743                                 res = wait_file(chan,ints,nextmsg,lang);
2744                                 break;
2745                         case 'A':
2746                         case 'a':
2747                                 /* Sunday - Saturday */
2748                                 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2749                                 res = wait_file(chan,ints,nextmsg,lang);
2750                                 break;
2751                         case 'B':
2752                         case 'b':
2753                         case 'h':
2754                                 /* January - December */
2755                                 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2756                                 res = wait_file(chan,ints,nextmsg,lang);
2757                                 break;
2758                         case 'd':
2759                         case 'e':
2760                                 /* First - Thirtyfirst */
2761                                 if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
2762                                         snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
2763                                         res = wait_file(chan,ints,nextmsg,lang);
2764                                 } else {
2765                                         snprintf(nextmsg,sizeof(nextmsg), "digits/h-%dh", tm.tm_mday - (tm.tm_mday % 10));
2766                                         res = wait_file(chan,ints,nextmsg,lang);
2767                                         if(!res) {
2768                                                 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday % 10);
2769                                                 res = wait_file(chan,ints,nextmsg,lang);
2770                                         }
2771                                 }
2772                                 break;
2773                         case 'Y':
2774                                 /* Year */
2775                                 if (tm.tm_year > 99) {
2776                                         res = wait_file(chan,ints, "digits/2",lang);
2777                                         if (!res) {
2778                                                 res = wait_file(chan,ints, "digits/thousand",lang);
2779                                         }
2780                                         if (tm.tm_year > 100) {
2781                                                 if (!res) {
2782                                                         snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
2783                                                         res = wait_file(chan,ints,nextmsg,lang);
2784                                                         if (!res) {
2785              &nb