2 * Asterisk -- A telephony toolkit for Linux.
4 * Say numbers and dates (maybe words one day too)
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <sys/types.h>
17 #include <netinet/in.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>
31 /* Forward declaration */
32 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang);
34 int ast_say_digit_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
36 /* XXX Merge with full version? XXX */
40 while(fn2[num] && !res) {
44 snprintf(fn, sizeof(fn), "digits/star");
47 snprintf(fn, sizeof(fn), "digits/pound");
50 if((fn2[num] >= '0') && (fn2[num] <= '9')){ /* Must be in {0-9} */
51 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
54 if(!ast_strlen_zero(fn)){ /* if length == 0, then skip this digit as it is invalid */
55 res = ast_streamfile(chan, fn, lang);
57 res = ast_waitstream(chan, ints);
65 int ast_say_character_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
67 /* XXX Merge with full version? XXX */
72 while(fn2[num] && !res) {
76 snprintf(fn, sizeof(fn), "digits/star");
79 snprintf(fn, sizeof(fn), "digits/pound");
91 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
94 strncpy(fn, "letters/exclaimation-point", sizeof(fn));
97 strncpy(fn, "letters/at", sizeof(fn));
100 strncpy(fn, "letters/dollar", sizeof(fn));
103 strncpy(fn, "letters/dash", sizeof(fn));
106 strncpy(fn, "letters/dot", sizeof(fn));
109 strncpy(fn, "letters/equals", sizeof(fn));
112 strncpy(fn, "letters/plus", sizeof(fn));
115 strncpy(fn, "letters/slash", sizeof(fn));
118 strncpy(fn, "letters/space", sizeof(fn));
122 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
123 snprintf(fn, sizeof(fn), "letters/%c", ltr);
125 if(!ast_strlen_zero(fn)) { /* if length == 0, then skip this digit as it is invalid */
126 res = ast_streamfile(chan, fn, lang);
128 res = ast_waitstream(chan, ints);
129 } ast_stopstream(chan);
135 int ast_say_phonetic_str(struct ast_channel *chan, char *fn2, char *ints, char *lang)
137 /* XXX Merge with full version? XXX */
145 /* while(fn2[num] && !res) { */
150 snprintf(fn, sizeof(fn), "digits/star");
153 snprintf(fn, sizeof(fn), "digits/pound");
164 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
167 strncpy(fn, "exclaimation-point", sizeof(fn));
170 strncpy(fn, "at", sizeof(fn));
173 strncpy(fn, "dollar", sizeof(fn));
176 strncpy(fn, "dash", sizeof(fn));
179 strncpy(fn, "dot", sizeof(fn));
182 strncpy(fn, "equals", sizeof(fn));
185 strncpy(fn, "plus", sizeof(fn));
188 strncpy(fn, "slash", sizeof(fn));
191 strncpy(fn, "space", sizeof(fn));
195 /* check if we have 2 chars after the % */
196 if (strlen(fn2) > num+2)
201 if (sscanf(hex,"%x", &temp))
202 { /* Hex to char convertion successfull */
206 { /* If it is a percent, play it now */
207 strncpy(fn, "percent", sizeof(fn));
211 /* check for invalid characters */
212 if ((temp<32) || (temp>126))
221 default: /* '9' falls through to here, too */
222 ltr = tolower(fn2[num]);
223 snprintf(fn, sizeof(fn), "phonetic/%c_p", ltr);
227 res = ast_streamfile(chan, fn, lang);
229 res = ast_waitstream(chan, ints);
230 ast_stopstream(chan);
237 int ast_say_digit_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
242 while(fn2[num] && !res) {
243 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
244 res = ast_streamfile(chan, fn, lang);
246 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
247 ast_stopstream(chan);
253 int ast_say_character_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
259 while(fn2[num] && !res) {
262 snprintf(fn, sizeof(fn), "digits/star");
265 snprintf(fn, sizeof(fn), "digits/pound");
277 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
280 strncpy(fn, "exclaimation-point", sizeof(fn));
283 strncpy(fn, "at", sizeof(fn));
286 strncpy(fn, "dollar", sizeof(fn));
289 strncpy(fn, "dash", sizeof(fn));
292 strncpy(fn, "dot", sizeof(fn));
295 strncpy(fn, "equals", sizeof(fn));
298 strncpy(fn, "plus", sizeof(fn));
301 strncpy(fn, "slash", sizeof(fn));
304 strncpy(fn, "space", sizeof(fn));
308 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
309 snprintf(fn, sizeof(fn), "letters/%c", ltr);
311 /* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
312 res = ast_streamfile(chan, fn, lang);
314 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
315 ast_stopstream(chan);
321 int ast_say_phonetic_str_full(struct ast_channel *chan, char *fn2, char *ints, char *lang, int audiofd, int ctrlfd)
327 while(fn2[num] && !res) {
330 snprintf(fn, sizeof(fn), "digits/star");
333 snprintf(fn, sizeof(fn), "digits/pound");
344 snprintf(fn, sizeof(fn), "digits/%c", fn2[num]);
347 strncpy(fn, "exclaimation-point", sizeof(fn));
350 strncpy(fn, "at", sizeof(fn));
353 strncpy(fn, "dollar", sizeof(fn));
356 strncpy(fn, "dash", sizeof(fn));
359 strncpy(fn, "dot", sizeof(fn));
362 strncpy(fn, "equals", sizeof(fn));
365 strncpy(fn, "plus", sizeof(fn));
368 strncpy(fn, "slash", sizeof(fn));
371 strncpy(fn, "space", sizeof(fn));
373 default: /* '9' falls here... */
375 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
376 snprintf(fn, sizeof(fn), "phonetic/%c", ltr);
378 /* snprintf(fn, sizeof(fn), "digits/%c", fn2[num]); */
379 res = ast_streamfile(chan, fn, lang);
381 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
382 ast_stopstream(chan);
388 int ast_say_digits(struct ast_channel *chan, int num, char *ints, char *lang)
390 /* XXX Should I be merged with say_digits_full XXX */
392 snprintf(fn2, sizeof(fn2), "%d", num);
393 return ast_say_digit_str(chan, fn2, ints, lang);
396 int ast_say_digits_full(struct ast_channel *chan, int num, char *ints, char *lang, int audiofd, int ctrlfd)
399 snprintf(fn2, sizeof(fn2), "%d", num);
400 return ast_say_digit_str_full(chan, fn2, ints, lang, audiofd, ctrlfd);
403 /* Forward declarations */
404 /* Syntaxes supported, not really language codes.
408 es - Spanish, Mexican
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).
422 Date/Time functions currently have less languages supported than saynumber().
424 Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
426 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
428 Portuguese sound files needed for Time/Date functions:
438 Spanish sound files needed for Time/Date functions:
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_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
453 static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
454 static int ast_say_number_full_tw(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd);
455 static int ast_say_number_full_pl(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd);
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);
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);
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);
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);
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);
482 static int wait_file(struct ast_channel *chan, char *ints, char *file, char *lang)
485 if ((res = ast_streamfile(chan, file, lang)))
486 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
488 res = ast_waitstream(chan, ints);
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, int audiofd, int ctrlfd)
496 char *options=(char *) NULL; /* While waiting for a general hack for agi */
498 if (!strcasecmp(language,"en") ) { /* English syntax */
499 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
500 } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
501 return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
502 } else if (!strcasecmp(language, "de") ) { /* German syntax */
503 return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
504 } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
505 return(ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd));
506 } else if (!strcasecmp(language, "fr") ) { /* French syntax */
507 return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
508 } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
509 return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
510 } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
511 return(ast_say_number_full_nl(chan, num, ints, language, 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, "pl") ) { /* Polish syntax */
517 return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
518 } else if (!strcasecmp(language, "tw")) { /* Taiwanese syntax */
519 return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
522 /* Default to english */
523 return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
526 /*--- ast_say_number: call language-specific functions without file descriptors */
527 int ast_say_number(struct ast_channel *chan, int num, char *ints, char *language, char *options)
529 if (!strcasecmp(language,"en") ) { /* English syntax */
530 return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
531 }else if (!strcasecmp(language, "da")) { /* Danish syntax */
532 return(ast_say_number_full_da(chan, num, ints, language, options, -1, -1));
533 } else if (!strcasecmp(language, "de")) { /* German syntax */
534 return(ast_say_number_full_de(chan, num, ints, language, options, -1, -1));
535 } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
536 return(ast_say_number_full_es(chan, num, ints, language, options, -1, -1));
537 } else if (!strcasecmp(language, "fr")) { /* French syntax */
538 return(ast_say_number_full_fr(chan, num, ints, language, options, -1, -1));
539 } else if (!strcasecmp(language, "it")) { /* Italian syntax */
540 return(ast_say_number_full_it(chan, num, ints, language, -1, -1));
541 } else if (!strcasecmp(language, "nl")) { /* Dutch syntax */
542 return(ast_say_number_full_nl(chan, num, ints, language, -1, -1));
543 } else if (!strcasecmp(language, "pt")) { /* Portuguese syntax */
544 return(ast_say_number_full_pt(chan, num, ints, language, options, -1, -1));
545 } else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
546 return(ast_say_number_full_pl(chan, num, ints, language, options, -1, -1));
547 } else if (!strcasecmp(language, "se")) { /* Swedish syntax */
548 return(ast_say_number_full_se(chan, num, ints, language, options, -1, -1));
551 /* Default to english */
552 return(ast_say_number_full_en(chan, num, ints, language, -1, -1));
555 /*--- ast_say_number_full_en: English syntax */
556 /* This is the default syntax, if no other syntax defined in this file is used */
557 static int ast_say_number_full_en(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
563 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
565 while(!res && (num || playh)) {
567 snprintf(fn, sizeof(fn), "digits/hundred");
569 } else if (num < 20) {
570 snprintf(fn, sizeof(fn), "digits/%d", num);
572 } else if (num < 100) {
573 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
574 num -= ((num / 10) * 10);
577 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
579 num -= ((num / 100) * 100);
581 if (num < 1000000) { /* 1,000,000 */
582 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
586 snprintf(fn, sizeof(fn), "digits/thousand");
588 if (num < 1000000000) { /* 1,000,000,000 */
589 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
593 snprintf(fn, sizeof(fn), "digits/million");
595 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
602 if(!ast_streamfile(chan, fn, language)) {
603 if (audiofd && ctrlfd)
604 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
606 res = ast_waitstream(chan, ints);
608 ast_stopstream(chan);
616 /*--- ast_say_number_full_da: Danish syntax */
618 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
620 static int ast_say_number_full_da(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
625 int cn = 1; /* +1 = Commune; -1 = Neutrum */
628 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
630 if (options && !strncasecmp(options, "n",1)) cn = -1;
632 while(!res && (num || playh || playa )) {
633 /* The grammar for Danish numbers is the same as for English except
635 * - 1 exists in both commune ("en", file "1N") and neutrum ("et", file "1")
636 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
637 * "one-and twenty" and 68 is "eight-and sixty".
638 * - "million" is different in singular and plural form
639 * - numbers > 1000 with zero as the third digit from last have an
640 * "and" before the last two digits, i.e. 2034 is "two thousand and
641 * four-and thirty" and 1000012 is "one million and twelve".
644 snprintf(fn, sizeof(fn), "digits/hundred");
647 snprintf(fn, sizeof(fn), "digits/and");
649 } else if (num == 1 && cn == -1) {
650 snprintf(fn, sizeof(fn), "digits/1N");
652 } else if (num < 20) {
653 snprintf(fn, sizeof(fn), "digits/%d", num);
655 } else if (num < 100) {
658 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
661 snprintf(fn, sizeof(fn), "digits/%d", num);
666 int hundreds = num / 100;
668 snprintf(fn, sizeof(fn), "digits/1N");
670 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
673 num -= 100 * hundreds;
679 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
683 snprintf(fn, sizeof(fn), "digits/thousand");
685 if (num < 1000000000) {
686 int millions = num / 1000000;
687 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
691 snprintf(fn, sizeof(fn), "digits/million");
693 snprintf(fn, sizeof(fn), "digits/millions");
696 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
700 if (num && num < 100)
705 if(!ast_streamfile(chan, fn, language)) {
706 if (audiofd && ctrlfd)
707 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
709 res = ast_waitstream(chan, ints);
711 ast_stopstream(chan);
717 /*--- ast_say_number_full_de: German syntax */
719 In addition to English, the following sounds are required:
721 "1-and" through "9-and"
724 NB "1" is recorded as 'eins'
726 static int ast_say_number_full_de(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
731 int mf = 1; /* +1 = Male, Neutrum; -1 = Female */
735 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
737 if (options && (!strncasecmp(options, "f",1)))
740 while(!res && (num || playh)) {
741 /* The grammar for German numbers is the same as for English except
743 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
744 * "one-and twenty" and 68 is "eight-and sixty".
745 * - "one" varies according to gender
746 * - 100 is 'hundert', however all other instances are 'ein hundert'
747 * - 1000 is 'tausend', however all other instances are 'ein tausend'
748 * - 1000000 is always 'ein million'
749 * - "million" is different in singular and plural form
752 snprintf(fn, sizeof(fn), "digits/hundred");
754 } else if (num == 1 && mf == -1) {
755 snprintf(fn, sizeof(fn), "digits/%dF", num);
757 } else if (num < 20) {
758 snprintf(fn, sizeof(fn), "digits/%d", num);
760 } else if (num < 100) {
763 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
766 snprintf(fn, sizeof(fn), "digits/%d", num);
769 } else if (num == 100) {
770 snprintf(fn, sizeof(fn), "digits/hundred");
772 } else if (num < 1000) {
773 int hundreds = num / 100;
775 snprintf(fn, sizeof(fn), "digits/1N");
777 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
779 num -= 100 * hundreds;
780 } else if (num == 1000 && t == 0) {
781 snprintf(fn, sizeof(fn), "digits/thousand");
783 } else if (num < 1000000) {
784 int thousands = num / 1000;
786 if (thousands == 1) {
787 snprintf(fn, sizeof(fn), "digits/1N");
788 snprintf(fna, sizeof(fna), "digits/thousand");
790 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
793 snprintf(fn, sizeof(fn), "digits/thousand");
796 } else if (num < 1000000000) {
797 int millions = num / 1000000;
800 snprintf(fn, sizeof(fn), "digits/1N");
801 snprintf(fna, sizeof(fna), "digits/million");
803 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
806 snprintf(fn, sizeof(fn), "digits/millions");
810 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
814 if(!ast_streamfile(chan, fn, language)) {
815 if (audiofd && ctrlfd)
816 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
818 res = ast_waitstream(chan, ints);
820 ast_stopstream(chan);
821 if(!ast_streamfile(chan, fna, language)) {
822 if (audiofd && ctrlfd)
823 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
825 res = ast_waitstream(chan, ints);
827 ast_stopstream(chan);
834 /*--- ast_say_number_full_es: Spanish syntax */
836 Requires a few new audios:
837 1F.gsm: feminine 'una'
838 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
840 static int ast_say_number_full_es(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
844 int mf = 1; /* +1 = Masculin; -1 = Feminin */
847 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
849 if (options && !strncasecmp(options, "f",1))
852 while (!res && num) {
854 snprintf(fn, sizeof(fn), "digits/y");
856 } else if (num == 1) {
858 snprintf(fn, sizeof(fn), "digits/%dF", num);
860 snprintf(fn, sizeof(fn), "digits/%d", num);
862 } else if (num < 31) {
863 snprintf(fn, sizeof(fn), "digits/%d", num);
865 } else if (num < 100) {
866 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
867 num -= ((num/10)*10);
870 } else if (num == 100) {
871 snprintf(fn, sizeof(fn), "digits/cien");
875 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
876 num -= ((num/100)*100);
879 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
883 snprintf(fn, sizeof(fn), "digits/mil");
885 if (num < 2147483640) {
886 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
889 if ((num/1000000) == 1) {
890 snprintf(fn, sizeof(fn), "digits/millon");
892 snprintf(fn, sizeof(fn), "digits/millones");
896 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
904 if(!ast_streamfile(chan, fn, language)) {
905 if (audiofd && ctrlfd)
906 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
908 res = ast_waitstream(chan, ints);
910 ast_stopstream(chan);
919 /*--- ast_say_number_full_fr: French syntax */
920 /* Extra sounds needed:
923 static int ast_say_number_full_fr(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
928 int mf = 1; /* +1 = Masculin; -1 = Feminin */
931 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
933 if (options && !strncasecmp(options, "f",1))
936 while(!res && (num || playh || playa)) {
938 snprintf(fn, sizeof(fn), "digits/hundred");
941 snprintf(fn, sizeof(fn), "digits/et");
943 } else if (num == 1) {
945 snprintf(fn, sizeof(fn), "digits/%dF", num);
947 snprintf(fn, sizeof(fn), "digits/%d", num);
949 } else if (num < 21) {
950 snprintf(fn, sizeof(fn), "digits/%d", num);
952 } else if (num < 70) {
953 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
954 if ((num % 10) == 1) playa++;
956 } else if (num < 80) {
957 snprintf(fn, sizeof(fn), "digits/60");
958 if ((num % 10) == 1) playa++;
960 } else if (num < 100) {
961 snprintf(fn, sizeof(fn), "digits/80");
963 } else if (num < 200) {
964 snprintf(fn, sizeof(fn), "digits/hundred");
966 } else if (num < 1000) {
967 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
970 } else if (num < 2000) {
971 snprintf(fn, sizeof(fn), "digits/thousand");
973 } else if (num < 1000000) {
974 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
977 snprintf(fn, sizeof(fn), "digits/thousand");
979 } else if (num < 1000000000) {
980 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
983 snprintf(fn, sizeof(fn), "digits/million");
986 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
990 if(!ast_streamfile(chan, fn, language)) {
991 if (audiofd && ctrlfd)
992 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
994 res = ast_waitstream(chan, ints);
996 ast_stopstream(chan);
1002 /*--- ast_say_number_full_it: Italian */
1003 static int ast_say_number_full_it(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
1011 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1016 Like english, numbers up to 20 are a single 'word', and others
1017 compound, but with exceptions.
1018 For example 21 is not twenty-one, but there is a single word in 'it'.
1019 Idem for 28 (ie when a the 2nd part of a compund number
1020 starts with a vowel)
1022 There are exceptions also for hundred, thousand and million.
1023 In english 100 = one hundred, 200 is two hundred.
1024 In italian 100 = cento , like to say hundred (without one),
1025 200 and more are like english.
1027 Same applies for thousand:
1028 1000 is one thousand in en, 2000 is two thousand.
1029 In it we have 1000 = mille , 2000 = 2 mila
1031 For million(s) we use the plural, if more than one
1032 Also, one million is abbreviated in it, like on-million,
1033 or 'un milione', not 'uno milione'.
1034 So the right file is provided.
1037 while(!res && (num || playh)) {
1039 snprintf(fn, sizeof(fn), "digits/hundred");
1041 } else if (num < 20) {
1042 snprintf(fn, sizeof(fn), "digits/%d", num);
1044 } else if (num == 21) {
1045 snprintf(fn, sizeof(fn), "digits/%d", num);
1047 } else if (num == 28) {
1048 snprintf(fn, sizeof(fn), "digits/%d", num);
1050 } else if (num == 31) {
1051 snprintf(fn, sizeof(fn), "digits/%d", num);
1053 } else if (num == 38) {
1054 snprintf(fn, sizeof(fn), "digits/%d", num);
1056 } else if (num == 41) {
1057 snprintf(fn, sizeof(fn), "digits/%d", num);
1059 } else if (num == 48) {
1060 snprintf(fn, sizeof(fn), "digits/%d", num);
1062 } else if (num == 51) {
1063 snprintf(fn, sizeof(fn), "digits/%d", num);
1065 } else if (num == 58) {
1066 snprintf(fn, sizeof(fn), "digits/%d", num);
1068 } else if (num == 61) {
1069 snprintf(fn, sizeof(fn), "digits/%d", num);
1071 } else if (num == 68) {
1072 snprintf(fn, sizeof(fn), "digits/%d", num);
1074 } else if (num == 71) {
1075 snprintf(fn, sizeof(fn), "digits/%d", num);
1077 } else if (num == 78) {
1078 snprintf(fn, sizeof(fn), "digits/%d", num);
1080 } else if (num == 81) {
1081 snprintf(fn, sizeof(fn), "digits/%d", num);
1083 } else if (num == 88) {
1084 snprintf(fn, sizeof(fn), "digits/%d", num);
1086 } else if (num == 91) {
1087 snprintf(fn, sizeof(fn), "digits/%d", num);
1089 } else if (num == 98) {
1090 snprintf(fn, sizeof(fn), "digits/%d", num);
1092 } else if (num < 100) {
1093 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1094 num -= ((num / 10) * 10);
1097 if ((num / 100) > 1) {
1098 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1101 snprintf(fn, sizeof(fn), "digits/hundred");
1103 num -= ((num / 100) * 100);
1105 if (num < 1000000) { /* 1,000,000 */
1107 res = ast_say_number_full(chan, num / 1000, ints, language, audiofd, ctrlfd);
1112 if ((tempnum / 1000) < 2)
1113 snprintf(fn, sizeof(fn), "digits/thousand");
1114 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1115 snprintf(fn, sizeof(fn), "digits/thousands");
1117 if (num < 1000000000) { /* 1,000,000,000 */
1118 if ((num / 1000000) > 1)
1119 res = ast_say_number_full(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1123 num = num % 1000000;
1124 if ((tempnum / 1000000) < 2)
1125 snprintf(fn, sizeof(fn), "digits/million");
1127 snprintf(fn, sizeof(fn), "digits/millions");
1129 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1136 if(!ast_streamfile(chan, fn, language)) {
1137 if (audiofd && ctrlfd)
1138 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1140 res = ast_waitstream(chan, ints);
1142 ast_stopstream(chan);
1148 /*--- ast_say_number_full_nl: dutch syntax */
1149 /* New files: digits/nl-en
1151 static int ast_say_number_full_nl(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
1158 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1159 while (!res && (num || playh )) {
1161 snprintf(fn, sizeof(fn), "digits/hundred");
1163 } else if (num < 20) {
1164 snprintf(fn, sizeof(fn), "digits/%d", num);
1166 } else if (num < 100) {
1169 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1173 snprintf(fn, sizeof(fn), "digits/nl-en");
1175 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1180 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1182 num -= ((num / 100) * 100);
1184 if (num < 1000000) { /* 1,000,000 */
1185 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1189 snprintf(fn, sizeof(fn), "digits/thousand");
1191 if (num < 1000000000) { /* 1,000,000,000 */
1192 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1195 num = num % 1000000;
1196 snprintf(fn, sizeof(fn), "digits/million");
1198 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1206 if(!ast_streamfile(chan, fn, language)) {
1207 if (audiofd && ctrlfd)
1208 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1210 res = ast_waitstream(chan, ints);
1212 ast_stopstream(chan);
1218 /* ast_say_number_full_pt: Portuguese syntax */
1219 /* Extra sounds needed: */
1220 /* For feminin all sound files end with F */
1221 /* 100E for 100+ something */
1222 /* 1000000S for plural */
1223 /* pt-e for 'and' */
1224 static int ast_say_number_full_pt(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1228 int mf = 1; /* +1 = Masculin; -1 = Feminin */
1232 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1234 if (options && !strncasecmp(options, "f",1))
1237 while(!res && num ) {
1239 if ((num == 1 || num == 2) && (mf < 0))
1240 snprintf(fn, sizeof(fn), "digits/%dF", num);
1242 snprintf(fn, sizeof(fn), "digits/%d", num);
1244 } else if (num < 100) {
1245 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1249 } else if (num < 1000) {
1251 snprintf(fn, sizeof(fn), "digits/100");
1253 snprintf(fn, sizeof(fn), "digits/100E");
1255 if (mf < 0 && num > 199)
1256 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
1258 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
1263 } else if (num < 1000000) {
1265 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
1269 snprintf(fn, sizeof(fn), "digits/1000");
1270 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
1273 } else if (num < 1000000000) {
1274 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
1278 snprintf(fn, sizeof(fn), "digits/1000000");
1280 snprintf(fn, sizeof(fn), "digits/1000000S");
1282 if ((num % 1000000) &&
1284 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
1285 // no hundreds and below
1286 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
1288 num = num % 1000000;
1290 if (!res && playh) {
1291 res = wait_file(chan, ints, "digits/pt-e", language);
1292 ast_stopstream(chan);
1296 if(!ast_streamfile(chan, fn, language)) {
1297 if (audiofd && ctrlfd)
1298 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); else
1299 res = ast_waitstream(chan, ints);
1301 ast_stopstream(chan);
1307 /*--- ast_say_number_full_se: Swedish/Norwegian syntax */
1308 static int ast_say_number_full_se(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1313 int cn = 1; /* +1 = Commune; -1 = Neutrum */
1315 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1316 if (options && !strncasecmp(options, "n",1)) cn = -1;
1318 while(!res && (num || playh)) {
1320 snprintf(fn, sizeof(fn), "digits/hundred");
1324 snprintf(fn, sizeof(fn), "digits/%d", num);
1328 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1329 num -= ((num / 10) * 10);
1331 if (num == 1 && cn == -1) { /* En eller ett? */
1332 snprintf(fn, sizeof(fn), "digits/1N");
1336 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1338 num -= ((num / 100) * 100);
1340 if (num < 1000000) { /* 1,000,000 */
1341 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1345 snprintf(fn, sizeof(fn), "digits/thousand");
1347 if (num < 1000000000) { /* 1,000,000,000 */
1348 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1351 num = num % 1000000;
1352 snprintf(fn, sizeof(fn), "digits/million");
1354 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1361 if(!ast_streamfile(chan, fn, language)) {
1362 if (audiofd && ctrlfd)
1363 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1365 res = ast_waitstream(chan, ints);
1367 ast_stopstream(chan);
1376 /*--- ast_say_number_full_tw: Taiwanese syntax */
1377 static int ast_say_number_full_tw(struct ast_channel *chan, int num, char *ints, char *language, int audiofd, int ctrlfd)
1383 return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
1385 while(!res && (num || playh)) {
1387 snprintf(fn, sizeof(fn), "digits/hundred");
1389 } else if (num < 10) {
1390 snprintf(fn, sizeof(fn), "digits/%d", num);
1392 } else if (num < 100) {
1393 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1394 num -= ((num / 10) * 10);
1397 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1399 num -= ((num / 100) * 100);
1401 if (num < 1000000) { /* 1,000,000 */
1402 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
1406 snprintf(fn, sizeof(fn), "digits/thousand");
1408 if (num < 1000000000) { /* 1,000,000,000 */
1409 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1412 num = num % 1000000;
1413 snprintf(fn, sizeof(fn), "digits/million");
1415 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
1422 if(!ast_streamfile(chan, fn, language)) {
1423 if (audiofd && ctrlfd)
1424 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1426 res = ast_waitstream(chan, ints);
1428 ast_stopstream(chan);
1435 /* ast_say_number_full_pl: Polish syntax */
1436 static int ast_say_number_full_pl(struct ast_channel *chan, int num, char *ints, char *language, char *options, int audiofd, int ctrlfd)
1446 1000000000.2 miliardy
1447 1000000000.5 miliardow
1511 70m siedemdziesieciu
1523 90m dziewiedziesieciu
1525 and combinations of eg.: 20_1, 30m_3m, etc...
1530 char *separator_dziesiatek;
1534 char *dziesiatki[10];
1539 char *zenski_cyfry[] = {
1540 "0","1z", "2z", "3", "4", "5",
1541 "6", "7", "8", "9"};
1543 char *zenski_cyfry2[] = {
1544 "0","1", "2z", "3", "4", "5",
1545 "6", "7", "8", "9"};
1547 char *meski_cyfry[] = {
1548 "0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/
1549 "6m", "7m", "8m", "9m"};
1551 char *meski_cyfry2[] = {
1552 "0","1", "2-2m", "3-2m", "4-2m", "5m",
1553 "6m", "7m", "8m", "9m"};
1555 char *meski_setki[] = {
1556 "", "100m", "200m", "300m", "400m", "500m",
1557 "600m", "700m", "800m", "900m"};
1559 char *meski_dziesiatki[] = {
1560 "", "10m", "20m", "30m", "40m", "50m",
1561 "60m", "70m", "80m", "90m"};
1563 char *meski_nastki[] = {
1564 "", "11m", "12m", "13m", "14m", "15m",
1565 "16m", "17m", "18m", "19m"};
1567 char *nijaki_cyfry[] = {
1568 "0","1", "2", "3", "4", "5",
1569 "6", "7", "8", "9"};
1571 char *nijaki_cyfry2[] = {
1572 "0","1", "2", "3", "4", "5",
1573 "6", "7", "8", "9"};
1575 char *nijaki_setki[] = {
1576 "", "100", "200", "300", "400", "500",
1577 "600", "700", "800", "900"};
1579 char *nijaki_dziesiatki[] = {
1580 "", "10", "20", "30", "40", "50",
1581 "60", "70", "80", "90"};
1583 char *nijaki_nastki[] = {
1584 "", "11", "12", "13", "14", "15",
1585 "16", "17", "18", "19"};
1587 char *rzedy[][3] = {
1588 {"1000", "1000.2", "1000.5"},
1589 {"1000000", "1000000.2", "1000000.5"},
1590 {"1000000000", "1000000000.2", "1000000000.5"}};
1592 static char* rzad_na_tekst(odmiana *odm, int i, int rzad) {
1593 if(rzad==0) return "";
1596 return odm->rzedy[rzad - 1][0];
1598 if((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1599 return odm->rzedy[rzad - 1][1];
1601 return odm->rzedy[rzad - 1][2];
1604 static char* append(char* buffer, char* str) {
1605 strcpy(buffer, str);
1606 buffer += strlen(str);
1610 static void odtworz_plik(char *fn) {
1611 char file_name[255] = "digits/";
1612 strcat(file_name, fn);
1613 ast_log(LOG_DEBUG, "Trying to play: %s\n", file_name);
1614 if(!ast_streamfile(chan, file_name, language)) {
1615 if (audiofd && ctrlfd)
1616 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1618 ast_waitstream(chan, ints);
1620 ast_stopstream(chan);
1624 static void powiedz(odmiana *odm, int rzad, int i) {
1626 if(i == 0 && rzad > 0)
1630 odtworz_plik(odm->cyfry[0]);
1633 int m1000E6 = i % 1000000000;
1634 int i1000E6 = i / 1000000000;
1636 powiedz(odm, rzad+3, i1000E6);
1638 int m1000E3 = m1000E6 % 1000000;
1639 int i1000E3 = m1000E6 / 1000000;
1641 powiedz(odm, rzad+2, i1000E3);
1643 int m1000 = m1000E3 % 1000;
1644 int i1000 = m1000E3 / 1000;
1646 powiedz(odm, rzad+1, i1000);
1648 int m100 = m1000 % 100;
1649 int i100 = m1000 / 100;
1652 odtworz_plik(odm->setki[i100]);
1654 if( m100 > 0 && m100 <=9 ) {
1656 odtworz_plik(odm->cyfry2[m100]);
1658 odtworz_plik(odm->cyfry[m100]);
1659 } else if(m100 % 10 == 0) {
1660 odtworz_plik(odm->dziesiatki[m100 / 10]);
1661 } else if(m100 <= 19 ) {
1662 odtworz_plik(odm->nastki[m100 % 10]);
1663 } else if(m100 != 0){
1664 if(odm->separator_dziesiatek[0]==' ') {
1665 odtworz_plik(odm->dziesiatki[m100 / 10]);
1666 odtworz_plik(odm->cyfry2[m100 % 10]);
1670 b = append(b, odm->dziesiatki[m100 / 10]);
1671 b = append(b, odm->separator_dziesiatek);
1672 b = append(b, odm->cyfry2[m100 % 10]);
1678 odtworz_plik(rzad_na_tekst(odm, i, rzad));
1682 static odmiana *odmiana_nieosobowa = NULL;
1683 static odmiana *odmiana_meska = NULL;
1684 static odmiana *odmiana_zenska = NULL;
1686 if(odmiana_nieosobowa == NULL) {
1687 odmiana_nieosobowa = (odmiana *) malloc(sizeof(odmiana));
1689 odmiana_nieosobowa->separator_dziesiatek = "_";
1691 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
1692 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
1693 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
1694 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
1695 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
1696 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
1699 if(odmiana_zenska == NULL) {
1700 odmiana_zenska = (odmiana *) malloc(sizeof(odmiana));
1702 odmiana_zenska->separator_dziesiatek = "_";
1704 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
1705 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
1706 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
1707 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
1708 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
1709 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
1712 if(odmiana_meska == NULL) {
1713 odmiana_meska = (odmiana *) malloc(sizeof(odmiana));
1715 odmiana_meska->separator_dziesiatek = "_";
1717 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
1718 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
1719 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
1720 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
1721 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
1722 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
1728 if(strncasecmp(options, "f", 1) == 0)
1730 else if(strncasecmp(options, "m", 1) == 0)
1733 o = odmiana_nieosobowa;
1735 o = odmiana_nieosobowa;
1742 int ast_say_date(struct ast_channel *chan, time_t t, char *ints, char *lang)
1744 if (!strcasecmp(lang,"en") ) { /* English syntax */
1745 return(ast_say_date_en(chan, t, ints, lang));
1746 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
1747 return(ast_say_date_nl(chan, t, ints, lang));
1748 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
1749 return(ast_say_date_pt(chan, t, ints, lang));
1752 /* Default to English */
1753 return(ast_say_date_en(chan, t, ints, lang));
1757 /* English syntax */
1758 int ast_say_date_en(struct ast_channel *chan, time_t t, char *ints, char *lang)
1763 ast_localtime(&t,&tm,NULL);
1765 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1766 res = ast_streamfile(chan, fn, lang);
1768 res = ast_waitstream(chan, ints);
1771 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1772 res = ast_streamfile(chan, fn, lang);
1774 res = ast_waitstream(chan, ints);
1777 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1779 res = ast_waitstream(chan, ints);
1781 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1786 int ast_say_date_nl(struct ast_channel *chan, time_t t, char *ints, char *lang)
1791 ast_localtime(&t,&tm,NULL);
1793 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1794 res = ast_streamfile(chan, fn, lang);
1796 res = ast_waitstream(chan, ints);
1799 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
1801 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1802 res = ast_streamfile(chan, fn, lang);
1804 res = ast_waitstream(chan, ints);
1807 res = ast_waitstream(chan, ints);
1809 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1813 /* Portuguese syntax */
1814 int ast_say_date_pt(struct ast_channel *chan, time_t t, char *ints, char *lang)
1819 ast_localtime(&t,&tm,NULL);
1820 localtime_r(&t,&tm);
1821 snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
1823 res = wait_file(chan, ints, fn, lang);
1825 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
1827 res = wait_file(chan, ints, "digits/pt-de", lang);
1828 snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
1830 res = wait_file(chan, ints, fn, lang);
1832 res = wait_file(chan, ints, "digits/pt-de", lang);
1834 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
1839 int ast_say_date_with_format(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1841 if (!strcasecmp(lang, "en") ) { /* English syntax */
1842 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1843 } else if (!strcasecmp(lang, "de") ) { /* German syntax */
1844 return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
1845 } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) { /* Spanish syntax */
1846 return(ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
1847 } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
1848 return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
1849 } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
1850 return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
1851 } else if (!strcasecmp(lang, "tw") ) { /* Taiwanese syntax */
1852 return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
1855 /* Default to English */
1856 return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
1859 /* English syntax */
1860 int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
1863 int res=0, offset, sndoffset;
1864 char sndfile[256], nextmsg[256];
1866 ast_localtime(&time,&tm,timezone);
1868 for (offset=0 ; format[offset] != '\0' ; offset++) {
1869 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
1870 switch (format[offset]) {
1871 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
1873 /* Literal name of a sound file */
1875 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
1876 sndfile[sndoffset] = format[offset];
1877 sndfile[sndoffset] = '\0';
1878 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1879 res = wait_file(chan,ints,nextmsg,lang);
1883 /* Sunday - Saturday */
1884 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
1885 res = wait_file(chan,ints,nextmsg,lang);
1890 /* January - December */
1891 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
1892 res = wait_file(chan,ints,nextmsg,lang);
1896 /* First - Thirtyfirst */
1897 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1898 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
1899 res = wait_file(chan,ints,nextmsg,lang);
1900 } else if (tm.tm_mday == 31) {
1901 /* "Thirty" and "first" */
1902 res = wait_file(chan,ints, "digits/30",lang);
1904 res = wait_file(chan,ints, "digits/h-1",lang);
1907 /* Between 21 and 29 - two sounds */
1908 res = wait_file(chan,ints, "digits/20",lang);
1910 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
1911 res = wait_file(chan,ints,nextmsg,lang);
1917 if (tm.tm_year > 99) {
1918 res = wait_file(chan,ints, "digits/2",lang);
1920 res = wait_file(chan,ints, "digits/thousand",lang);
1922 if (tm.tm_year > 100) {
1924 /* This works until the end of 2020 */
1925 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
1926 res = wait_file(chan,ints,nextmsg,lang);
1930 if (tm.tm_year < 1) {
1931 /* I'm not going to handle 1900 and prior */
1932 /* We'll just be silent on the year, instead of bombing out. */
1934 res = wait_file(chan,ints, "digits/19",lang);
1936 if (tm.tm_year <= 9) {
1938 res = wait_file(chan,ints, "digits/oh",lang);
1940 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1941 res = wait_file(chan,ints,nextmsg,lang);
1943 } else if (tm.tm_year <= 20) {
1945 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
1946 res = wait_file(chan,ints,nextmsg,lang);
1950 ten = tm.tm_year / 10;
1951 one = tm.tm_year % 10;
1952 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
1953 res = wait_file(chan,ints,nextmsg,lang);
1956 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
1957 res = wait_file(chan,ints,nextmsg,lang);
1968 if (tm.tm_hour == 0)
1969 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
1970 else if (tm.tm_hour > 12)
1971 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
1973 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
1974 res = wait_file(chan,ints,nextmsg,lang);
1979 if (format[offset] == 'H') {
1981 if (tm.tm_hour < 10) {
1982 res = wait_file(chan,ints, "digits/oh",lang);
1986 if (tm.tm_hour == 0) {
1987 res = wait_file(chan,ints, "digits/oh",lang);
1991 if (tm.tm_hour != 0) {
1992 int remainder = tm.tm_hour;
1993 if (tm.tm_hour > 20) {
1994 res = wait_file(chan,ints, "digits/20",lang);
1998 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
1999 res = wait_file(chan,ints,nextmsg,lang);
2006 if (tm.tm_min == 0) {
2007 res = wait_file(chan,ints, "digits/oclock",lang);
2008 } else if (tm.tm_min < 10) {
2009 res = wait_file(chan,ints, "digits/oh",lang);
2011 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
2012 res = wait_file(chan,ints,nextmsg,lang);
2014 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
2015 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
2016 res = wait_file(chan,ints,nextmsg,lang);
2019 ten = (tm.tm_min / 10) * 10;
2020 one = (tm.tm_min % 10);
2021 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2022 res = wait_file(chan,ints,nextmsg,lang);
2024 /* Fifty, not fifty-zero */
2026 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2027 res = wait_file(chan,ints,nextmsg,lang);
2035 if (tm.tm_hour > 11)
2036 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
2038 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
2039 res = wait_file(chan,ints,nextmsg,lang);
2042 /* Shorthand for "Today", "Yesterday", or ABdY */
2048 gettimeofday(&now,NULL);
2049 ast_localtime(&now.tv_sec,&tmnow,timezone);
2050 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2051 /* In any case, it saves not having to do ast_mktime() */
2052 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2053 if (beg_today < time) {
2055 res = wait_file(chan,ints, "digits/today",lang);
2056 } else if (beg_today - 86400 < time) {
2058 res = wait_file(chan,ints, "digits/yesterday",lang);
2060 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2065 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2071 gettimeofday(&now,NULL);
2072 ast_localtime(&now.tv_sec,&tmnow,timezone);
2073 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2074 /* In any case, it saves not having to do ast_mktime() */
2075 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2076 if (beg_today < time) {
2078 } else if ((beg_today - 86400) < time) {
2080 res = wait_file(chan,ints, "digits/yesterday",lang);
2081 } else if (beg_today - 86400 * 6 < time) {
2082 /* Within the last week */
2083 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2085 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2090 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
2094 if (tm.tm_sec == 0) {
2095 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2096 res = wait_file(chan,ints,nextmsg,lang);
2097 } else if (tm.tm_sec < 10) {
2098 res = wait_file(chan,ints, "digits/oh",lang);
2100 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2101 res = wait_file(chan,ints,nextmsg,lang);
2103 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2104 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2105 res = wait_file(chan,ints,nextmsg,lang);
2108 ten = (tm.tm_sec / 10) * 10;
2109 one = (tm.tm_sec % 10);
2110 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2111 res = wait_file(chan,ints,nextmsg,lang);
2113 /* Fifty, not fifty-zero */
2115 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2116 res = wait_file(chan,ints,nextmsg,lang);
2122 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2126 /* Just ignore spaces and tabs */
2129 /* Unknown character */
2130 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2132 /* Jump out on DTMF */
2141 /* NB This currently is a 100% clone of the English syntax, just getting ready to make changes... */
2142 int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
2145 int res=0, offset, sndoffset;
2146 char sndfile[256], nextmsg[256];
2148 ast_localtime(&time,&tm,timezone);
2150 for (offset=0 ; format[offset] != '\0' ; offset++) {
2151 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2152 switch (format[offset]) {
2153 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2155 /* Literal name of a sound file */
2157 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2158 sndfile[sndoffset] = format[offset];
2159 sndfile[sndoffset] = '\0';
2160 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
2161 res = wait_file(chan,ints,nextmsg,lang);
2165 /* Sunday - Saturday */
2166 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2167 res = wait_file(chan,ints,nextmsg,lang);
2172 /* January - December */
2173 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2174 res = wait_file(chan,ints,nextmsg,lang);
2178 /* First - Thirtyfirst */
2179 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
2180 snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
2181 res = wait_file(chan,ints,nextmsg,lang);
2182 } else if (tm.tm_mday == 31) {
2183 /* "Thirty" and "first" */
2184 res = wait_file(chan,ints, "digits/30",lang);
2186 res = wait_file(chan,ints, "digits/h-1",lang);
2189 /* Between 21 and 29 - two sounds */
2190 res = wait_file(chan,ints, "digits/20",lang);
2192 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - 20);
2193 res = wait_file(chan,ints,nextmsg,lang);
2199 if (tm.tm_year > 99) {
2200 res = wait_file(chan,ints, "digits/2",lang);
2202 res = wait_file(chan,ints, "digits/thousand",lang);
2204 if (tm.tm_year > 100) {
2206 /* This works until the end of 2020 */
2207 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
2208 res = wait_file(chan,ints,nextmsg,lang);
2212 if (tm.tm_year < 1) {
2213 /* I'm not going to handle 1900 and prior */
2214 /* We'll just be silent on the year, instead of bombing out. */
2216 res = wait_file(chan,ints, "digits/19",lang);
2218 if (tm.tm_year <= 9) {
2220 res = wait_file(chan,ints, "digits/oh",lang);
2222 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
2223 res = wait_file(chan,ints,nextmsg,lang);
2225 } else if (tm.tm_year <= 20) {
2227 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
2228 res = wait_file(chan,ints,nextmsg,lang);
2232 ten = tm.tm_year / 10;
2233 one = tm.tm_year % 10;
2234 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
2235 res = wait_file(chan,ints,nextmsg,lang);
2238 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2239 res = wait_file(chan,ints,nextmsg,lang);
2250 if (tm.tm_hour == 0)
2251 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
2252 else if (tm.tm_hour > 12)
2253 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
2255 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
2256 res = wait_file(chan,ints,nextmsg,lang);
2261 if (format[offset] == 'H') {
2263 if (tm.tm_hour < 10) {
2264 res = wait_file(chan,ints, "digits/oh",lang);
2268 if (tm.tm_hour == 0) {
2269 res = wait_file(chan,ints, "digits/oh",lang);
2273 if (tm.tm_hour != 0) {
2274 int remainder = tm.tm_hour;
2275 if (tm.tm_hour > 20) {
2276 res = wait_file(chan,ints, "digits/20",lang);
2280 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
2281 res = wait_file(chan,ints,nextmsg,lang);
2288 if (tm.tm_min == 0) {
2289 res = wait_file(chan,ints, "digits/oclock",lang);
2290 } else if (tm.tm_min < 10) {
2291 res = wait_file(chan,ints, "digits/oh",lang);
2293 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
2294 res = wait_file(chan,ints,nextmsg,lang);
2296 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
2297 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
2298 res = wait_file(chan,ints,nextmsg,lang);
2301 ten = (tm.tm_min / 10) * 10;
2302 one = (tm.tm_min % 10);
2303 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2304 res = wait_file(chan,ints,nextmsg,lang);
2306 /* Fifty, not fifty-zero */
2308 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2309 res = wait_file(chan,ints,nextmsg,lang);
2317 if (tm.tm_hour > 11)
2318 snprintf(nextmsg,sizeof(nextmsg), "digits/p-m");
2320 snprintf(nextmsg,sizeof(nextmsg), "digits/a-m");
2321 res = wait_file(chan,ints,nextmsg,lang);
2324 /* Shorthand for "Today", "Yesterday", or ABdY */
2330 gettimeofday(&now,NULL);
2331 ast_localtime(&now.tv_sec,&tmnow,timezone);
2332 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2333 /* In any case, it saves not having to do ast_mktime() */
2334 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2335 if (beg_today < time) {
2337 res = wait_file(chan,ints, "digits/today",lang);
2338 } else if (beg_today - 86400 < time) {
2340 res = wait_file(chan,ints, "digits/yesterday",lang);
2342 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2347 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2353 gettimeofday(&now,NULL);
2354 ast_localtime(&now.tv_sec,&tmnow,timezone);
2355 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2356 /* In any case, it saves not having to do ast_mktime() */
2357 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2358 if (beg_today < time) {
2360 } else if ((beg_today - 86400) < time) {
2362 res = wait_file(chan,ints, "digits/yesterday",lang);
2363 } else if (beg_today - 86400 * 6 < time) {
2364 /* Within the last week */
2365 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2367 res = ast_say_date_with_format(chan, time, ints, lang, "ABdY", timezone);
2372 res = ast_say_date_with_format(chan, time, ints, lang, "HM", timezone);
2376 if (tm.tm_sec == 0) {
2377 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2378 res = wait_file(chan,ints,nextmsg,lang);
2379 } else if (tm.tm_sec < 10) {
2380 res = wait_file(chan,ints, "digits/oh",lang);
2382 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2383 res = wait_file(chan,ints,nextmsg,lang);
2385 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2386 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2387 res = wait_file(chan,ints,nextmsg,lang);
2390 ten = (tm.tm_sec / 10) * 10;
2391 one = (tm.tm_sec % 10);
2392 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2393 res = wait_file(chan,ints,nextmsg,lang);
2395 /* Fifty, not fifty-zero */
2397 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2398 res = wait_file(chan,ints,nextmsg,lang);
2404 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2408 /* Just ignore spaces and tabs */
2411 /* Unknown character */
2412 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2414 /* Jump out on DTMF */
2422 /* Spanish syntax */
2423 int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
2426 int res=0, offset, sndoffset;
2427 char sndfile[256], nextmsg[256];
2429 ast_localtime(&time,&tm,timezone);
2431 for (offset=0 ; format[offset] != '\0' ; offset++) {
2432 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2433 switch (format[offset]) {
2434 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2436 /* Literal name of a sound file */
2438 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2439 sndfile[sndoffset] = format[offset];
2440 sndfile[sndoffset] = '\0';
2441 snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
2442 res = wait_file(chan,ints,nextmsg,lang);
2446 /* Sunday - Saturday */
2447 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2448 res = wait_file(chan,ints,nextmsg,lang);
2453 /* January - December */
2454 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2455 res = wait_file(chan,ints,nextmsg,lang);
2459 /* First - Thirtyfirst */
2460 res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
2464 res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
2469 if (tm.tm_hour == 0)
2470 snprintf(nextmsg,sizeof(nextmsg), "digits/12");
2471 else if (tm.tm_hour > 12)
2472 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
2474 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
2475 res = wait_file(chan,ints,nextmsg,lang);
2480 res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
2482 if (tm.tm_hour != 0) {
2483 int remainder = tm.tm_hour;
2484 if (tm.tm_hour > 20) {
2485 res = wait_file(chan,ints, "digits/20",lang);
2489 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
2490 res = wait_file(chan,ints,nextmsg,lang);
2497 res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
2502 if (tm.tm_hour > 12)
2503 res = wait_file(chan, ints, "digits/p-m", lang);
2504 else if (tm.tm_hour && tm.tm_hour < 12)
2505 res = wait_file(chan, ints, "digits/a-m", lang);
2508 /* Shorthand for "Today", "Yesterday", or ABdY */
2514 gettimeofday(&now,NULL);
2515 ast_localtime(&now.tv_sec,&tmnow,timezone);
2516 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2517 /* In any case, it saves not having to do ast_mktime() */
2518 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2519 if (beg_today < time) {
2521 res = wait_file(chan,ints, "digits/today",lang);
2522 } else if (beg_today - 86400 < time) {
2524 res = wait_file(chan,ints, "digits/yesterday",lang);
2526 res = ast_say_date_with_format(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
2531 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2537 gettimeofday(&now,NULL);
2538 ast_localtime(&now.tv_sec,&tmnow,timezone);
2539 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
2540 /* In any case, it saves not having to do ast_mktime() */
2541 beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
2542 if (beg_today < time) {
2544 res = wait_file(chan,ints, "digits/today",lang);
2545 } else if ((beg_today - 86400) < time) {
2547 res = wait_file(chan,ints, "digits/yesterday",lang);
2548 } else if (beg_today - 86400 * 6 < time) {
2549 /* Within the last week */
2550 res = ast_say_date_with_format(chan, time, ints, lang, "A", timezone);
2552 res = ast_say_date_with_format(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
2557 res = ast_say_date_with_format(chan, time, ints, lang, "H 'digits/y' M", timezone);
2561 if (tm.tm_sec == 0) {
2562 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2563 res = wait_file(chan,ints,nextmsg,lang);
2564 } else if (tm.tm_sec < 10) {
2565 res = wait_file(chan,ints, "digits/oh",lang);
2567 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2568 res = wait_file(chan,ints,nextmsg,lang);
2570 } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
2571 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
2572 res = wait_file(chan,ints,nextmsg,lang);
2575 ten = (tm.tm_sec / 10) * 10;
2576 one = (tm.tm_sec % 10);
2577 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
2578 res = wait_file(chan,ints,nextmsg,lang);
2580 /* Fifty, not fifty-zero */
2582 snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
2583 res = wait_file(chan,ints,nextmsg,lang);
2589 res = ast_say_date_with_format(chan, time, ints, lang, "HMS", timezone);
2593 /* Just ignore spaces and tabs */
2596 /* Unknown character */
2597 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
2599 /* Jump out on DTMF */
2608 int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, char *ints, char *lang, char *format, char *timezone)
2611 int res=0, offset, sndoffset;
2612 char sndfile[256], nextmsg[256];
2614 ast_localtime(&time,&tm,timezone);
2616 for (offset=0 ; format[offset] != '\0' ; offset++) {
2617 ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
2618 switch (format[offset]) {
2619 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2621 /* Literal name of a sound file */
2623 for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
2624 sndfile[sndoffset] = format[offset];
2625 sndfile[sndoffset] = '\0';
2626 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
2627 res = wait_file(chan,ints,nextmsg,lang);
2631 /* Sunday - Saturday */
2632 snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
2633 res = wait_file(chan,ints,nextmsg,lang);
2638 /* January - December */
2639 snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
2640 res = wait_file(chan,ints,nextmsg,lang);
2644 /* First - Thirtyfirst */
2645 res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
2649 if (tm.tm_year > 99) {
2650 res = wait_file(chan,ints, "digits/2",lang);
2652 res = wait_file(chan,ints, "digits/thousand",lang);
2654 if (tm.tm_year > 100) {