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