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