2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
7 * George Konstantoulakis <gkon@inaccessnetworks.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
22 * \brief Say numbers and dates (maybe words one day too)
24 * \author Mark Spencer <markster@digium.com>
26 * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <gkon@inaccessnetworks.com>
30 #include <sys/types.h>
33 #include <netinet/in.h>
40 #include <iso/limits_iso.h>
45 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
47 #include "asterisk/file.h"
48 #include "asterisk/channel.h"
49 #include "asterisk/logger.h"
50 #include "asterisk/options.h"
51 #include "asterisk/say.h"
52 #include "asterisk/lock.h"
53 #include "asterisk/localtime.h"
54 #include "asterisk/utils.h"
56 /* Forward declaration */
57 static int wait_file(struct ast_channel
*chan
, const char *ints
, const char *file
, const char *lang
);
60 static int say_character_str_full(struct ast_channel
*chan
, const char *str
, const char *ints
, const char *lang
, int audiofd
, int ctrlfd
)
78 fn
= "letters/exclaimation-point";
84 fn
= "letters/dollar";
93 fn
= "letters/equals";
102 fn
= "letters/space";
114 strcpy(fnbuf
, "digits/X");
120 if ('A' <= ltr
&& ltr
<= 'Z') ltr
+= 'a' - 'A'; /* file names are all lower-case */
121 strcpy(fnbuf
, "letters/X");
125 res
= ast_streamfile(chan
, fn
, lang
);
127 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
128 ast_stopstream(chan
);
135 static int say_phonetic_str_full(struct ast_channel
*chan
, const char *str
, const char *ints
, const char *lang
, int audiofd
, int ctrlfd
)
153 fn
= "letters/exclaimation-point";
159 fn
= "letters/dollar";
168 fn
= "letters/equals";
174 fn
= "letters/slash";
177 fn
= "letters/space";
188 strcpy(fnbuf
, "digits/X");
192 default: /* '9' falls here... */
194 if ('A' <= ltr
&& ltr
<= 'Z') ltr
+= 'a' - 'A'; /* file names are all lower-case */
195 strcpy(fnbuf
, "phonetic/X_p");
199 res
= ast_streamfile(chan
, fn
, lang
);
201 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
202 ast_stopstream(chan
);
209 static int say_digit_str_full(struct ast_channel
*chan
, const char *str
, const char *ints
, const char *lang
, int audiofd
, int ctrlfd
)
216 while (str
[num
] && !res
) {
238 strcpy(fnbuf
, "digits/X");
244 res
= ast_streamfile(chan
, fn
, lang
);
246 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
247 ast_stopstream(chan
);
255 /* Forward declarations */
256 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
257 \note Not really language codes.
258 For these language codes, Asterisk will change the syntax when
259 saying numbers (and in some cases dates and voicemail messages
263 \arg \b en - English (US)
264 \arg \b en_GB - English (British)
265 \arg \b es - Spanish, Mexican
270 \arg \b no - Norwegian
272 \arg \b pt - Portuguese
274 \arg \b tw - Taiwanese / Chinese
278 For Some languages the numbers differ for gender and plural.
279 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
280 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
281 use the option argument 'p' for plural enumerations like in German
283 Date/Time functions currently have less languages supported than saynumber().
285 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
287 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
290 Portuguese sound files needed for Time/Date functions:
301 Spanish sound files needed for Time/Date functions:
306 Italian sound files needed for Time/Date functions:
312 /* Forward declarations of language specific variants of ast_say_number_full */
313 static int ast_say_number_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
314 static int ast_say_number_full_cz(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
315 static int ast_say_number_full_da(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
316 static int ast_say_number_full_de(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
317 static int ast_say_number_full_en_GB(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
318 static int ast_say_number_full_es(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
319 static int ast_say_number_full_fr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
320 static int ast_say_number_full_he(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
321 static int ast_say_number_full_it(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
322 static int ast_say_number_full_nl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
323 static int ast_say_number_full_no(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
324 static int ast_say_number_full_pl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
325 static int ast_say_number_full_pt(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
326 static int ast_say_number_full_se(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
327 static int ast_say_number_full_tw(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
328 static int ast_say_number_full_gr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
329 static int ast_say_number_full_ru(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
331 /* Forward declarations of language specific variants of ast_say_enumeration_full */
332 static int ast_say_enumeration_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
333 static int ast_say_enumeration_full_da(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
334 static int ast_say_enumeration_full_de(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
336 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
337 static int ast_say_date_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
338 static int ast_say_date_da(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
339 static int ast_say_date_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
340 static int ast_say_date_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
341 static int ast_say_date_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
342 static int ast_say_date_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
343 static int ast_say_date_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
345 static int ast_say_date_with_format_en(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
346 static int ast_say_date_with_format_da(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
347 static int ast_say_date_with_format_de(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
348 static int ast_say_date_with_format_es(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
349 static int ast_say_date_with_format_he(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
350 static int ast_say_date_with_format_fr(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
351 static int ast_say_date_with_format_it(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
352 static int ast_say_date_with_format_nl(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
353 static int ast_say_date_with_format_pl(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
354 static int ast_say_date_with_format_pt(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
355 static int ast_say_date_with_format_tw(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
356 static int ast_say_date_with_format_gr(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
);
358 static int ast_say_time_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
359 static int ast_say_time_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
360 static int ast_say_time_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
361 static int ast_say_time_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
362 static int ast_say_time_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
363 static int ast_say_time_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
364 static int ast_say_time_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
366 static int ast_say_datetime_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
367 static int ast_say_datetime_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
368 static int ast_say_datetime_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
369 static int ast_say_datetime_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
370 static int ast_say_datetime_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
371 static int ast_say_datetime_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
372 static int ast_say_datetime_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
374 static int ast_say_datetime_from_now_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
375 static int ast_say_datetime_from_now_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
376 static int ast_say_datetime_from_now_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
378 static int wait_file(struct ast_channel
*chan
, const char *ints
, const char *file
, const char *lang
)
381 if ((res
= ast_streamfile(chan
, file
, lang
)))
382 ast_log(LOG_WARNING
, "Unable to play message %s\n", file
);
384 res
= ast_waitstream(chan
, ints
);
388 /*! \brief ast_say_number_full: call language-specific functions */
389 /* Called from AGI */
390 static int say_number_full(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
392 if (!strcasecmp(language
,"en") ) { /* English syntax */
393 return(ast_say_number_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
394 } else if (!strcasecmp(language
, "cz") ) { /* Czech syntax */
395 return(ast_say_number_full_cz(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
396 } else if (!strcasecmp(language
, "da") ) { /* Danish syntax */
397 return(ast_say_number_full_da(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
398 } else if (!strcasecmp(language
, "de") ) { /* German syntax */
399 return(ast_say_number_full_de(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
400 } else if (!strcasecmp(language
, "en_GB") ) { /* British syntax */
401 return(ast_say_number_full_en_GB(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
402 } else if (!strcasecmp(language
, "no") ) { /* Norwegian syntax */
403 return(ast_say_number_full_no(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
404 } else if (!strcasecmp(language
, "es") || !strcasecmp(language
, "mx")) { /* Spanish syntax */
405 return(ast_say_number_full_es(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
406 } else if (!strcasecmp(language
, "fr") ) { /* French syntax */
407 return(ast_say_number_full_fr(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
408 } else if (!strcasecmp(language
, "he") ) { /* Hebrew syntax */
409 return(ast_say_number_full_he(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
410 } else if (!strcasecmp(language
, "it") ) { /* Italian syntax */
411 return(ast_say_number_full_it(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
412 } else if (!strcasecmp(language
, "nl") ) { /* Dutch syntax */
413 return(ast_say_number_full_nl(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
414 } else if (!strcasecmp(language
, "pl") ) { /* Polish syntax */
415 return(ast_say_number_full_pl(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
416 } else if (!strcasecmp(language
, "pt") ) { /* Portuguese syntax */
417 return(ast_say_number_full_pt(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
418 } else if (!strcasecmp(language
, "se") ) { /* Swedish syntax */
419 return(ast_say_number_full_se(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
420 } else if (!strcasecmp(language
, "tw") || !strcasecmp(language
, "zh") ) { /* Taiwanese / Chinese syntax */
421 return(ast_say_number_full_tw(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
422 } else if (!strcasecmp(language
, "gr") ) { /* Greek syntax */
423 return(ast_say_number_full_gr(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
424 } else if (!strcasecmp(language
, "ru") ) { /* Russian syntax */
425 return(ast_say_number_full_ru(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
428 /* Default to english */
429 return(ast_say_number_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
432 /*! \brief ast_say_number_full_en: English syntax */
433 /* This is the default syntax, if no other syntax defined in this file is used */
434 static int ast_say_number_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
440 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
442 while(!res
&& (num
|| playh
)) {
444 snprintf(fn
, sizeof(fn
), "digits/minus");
445 if ( num
> INT_MIN
) {
451 snprintf(fn
, sizeof(fn
), "digits/hundred");
453 } else if (num
< 20) {
454 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
456 } else if (num
< 100) {
457 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
458 num
-= ((num
/ 10) * 10);
461 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
463 num
-= ((num
/ 100) * 100);
465 if (num
< 1000000) { /* 1,000,000 */
466 res
= ast_say_number_full_en(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
470 snprintf(fn
, sizeof(fn
), "digits/thousand");
472 if (num
< 1000000000) { /* 1,000,000,000 */
473 res
= ast_say_number_full_en(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
477 snprintf(fn
, sizeof(fn
), "digits/million");
479 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
486 if(!ast_streamfile(chan
, fn
, language
)) {
487 if ((audiofd
> -1) && (ctrlfd
> -1))
488 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
490 res
= ast_waitstream(chan
, ints
);
492 ast_stopstream(chan
);
498 static int exp10_int(int power
)
501 for (x
=0;x
<power
;x
++)
506 /*! \brief ast_say_number_full_cz: Czech syntax */
508 * 1m,2m - gender male
509 * 1w,2w - gender female
513 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
515 * for each number 10^(3n + 3) exist 3 files represented as:
516 * 1 tousand = jeden tisic = 1_E3
517 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
518 * 5,6,... tousands = pet,sest,... tisic = 5_E3
524 * tousand, milion are gender male, so 1 and 2 is 1m 2m
525 * miliard is gender female, so 1 and 2 is 1w 2w
527 static int ast_say_number_full_cz(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
537 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
542 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
544 while(!res
&& (num
|| playh
)) {
546 snprintf(fn
, sizeof(fn
), "digits/minus");
547 if ( num
> INT_MIN
) {
552 } else if (num
< 3 ) {
553 snprintf(fn
, sizeof(fn
), "digits/%d%c",num
,options
[0]);
556 } else if (num
< 20) {
557 snprintf(fn
, sizeof(fn
), "digits/%d",num
);
560 } else if (num
< 100) {
561 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
562 num
-= ((num
/ 10) * 10);
563 } else if (num
< 1000) {
564 hundered
= num
/ 100;
565 if ( hundered
== 1 ) {
566 snprintf(fn
, sizeof(fn
), "digits/1sto");
567 } else if ( hundered
== 2 ) {
568 snprintf(fn
, sizeof(fn
), "digits/2ste");
570 res
= ast_say_number_full_cz(chan
,hundered
,ints
,language
,options
,audiofd
,ctrlfd
);
573 if (hundered
== 3 || hundered
== 4) {
574 snprintf(fn
, sizeof(fn
), "digits/sta");
575 } else if ( hundered
> 4 ) {
576 snprintf(fn
, sizeof(fn
), "digits/set");
579 num
-= (hundered
* 100);
580 } else { /* num > 1000 */
581 length
= (int)log10(num
)+1;
582 while ( (length
% 3 ) != 1 ) {
585 left
= num
/ (exp10_int(length
-1));
588 case 9: options
= "w"; /* 1,000,000,000 gender female */
590 default : options
= "m"; /* others are male */
593 if ( left
> 1 ) { /* we dont say "one thousand" but only thousand */
594 res
= ast_say_number_full_cz(chan
,left
,ints
,language
,options
,audiofd
,ctrlfd
);
598 if ( left
>= 5 ) { /* >= 5 have the same declesion */
599 snprintf(fn
, sizeof(fn
), "digits/5_E%d",length
-1);
600 } else if ( left
>= 2 && left
<= 4 ) {
601 snprintf(fn
, sizeof(fn
), "digits/2-4_E%d",length
-1);
602 } else { /* left == 1 */
603 snprintf(fn
, sizeof(fn
), "digits/1_E%d",length
-1);
605 num
-= left
* (exp10_int(length
-1));
608 if(!ast_streamfile(chan
, fn
, language
)) {
609 if ((audiofd
> -1) && (ctrlfd
> -1)) {
610 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
612 res
= ast_waitstream(chan
, ints
);
615 ast_stopstream(chan
);
621 /*! \brief ast_say_number_full_da: Danish syntax */
623 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
625 static int ast_say_number_full_da(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
630 int cn
= 1; /* +1 = commune; -1 = neuter */
633 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
635 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
637 while(!res
&& (num
|| playh
|| playa
)) {
638 /* The grammar for Danish numbers is the same as for English except
640 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
641 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
642 * "one-and twenty" and 68 is "eight-and sixty".
643 * - "million" is different in singular and plural form
644 * - numbers > 1000 with zero as the third digit from last have an
645 * "and" before the last two digits, i.e. 2034 is "two thousand and
646 * four-and thirty" and 1000012 is "one million and twelve".
649 snprintf(fn
, sizeof(fn
), "digits/minus");
650 if ( num
> INT_MIN
) {
656 snprintf(fn
, sizeof(fn
), "digits/hundred");
659 snprintf(fn
, sizeof(fn
), "digits/and");
661 } else if (num
== 1 && cn
== -1) {
662 snprintf(fn
, sizeof(fn
), "digits/1N");
664 } else if (num
< 20) {
665 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
667 } else if (num
< 100) {
670 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
673 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
678 int hundreds
= num
/ 100;
680 snprintf(fn
, sizeof(fn
), "digits/1N");
682 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
685 num
-= 100 * hundreds
;
691 res
= ast_say_number_full_da(chan
, num
/ 1000, ints
, language
, "n", audiofd
, ctrlfd
);
695 snprintf(fn
, sizeof(fn
), "digits/thousand");
697 if (num
< 1000000000) {
698 int millions
= num
/ 1000000;
699 res
= ast_say_number_full_da(chan
, millions
, ints
, language
, "c", audiofd
, ctrlfd
);
703 snprintf(fn
, sizeof(fn
), "digits/million");
705 snprintf(fn
, sizeof(fn
), "digits/millions");
708 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
712 if (num
&& num
< 100)
717 if(!ast_streamfile(chan
, fn
, language
)) {
718 if ((audiofd
> -1) && (ctrlfd
> -1))
719 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
721 res
= ast_waitstream(chan
, ints
);
723 ast_stopstream(chan
);
729 /*! \brief ast_say_number_full_de: German syntax */
731 In addition to English, the following sounds are required:
733 "1-and" through "9-and"
736 NB "1" is recorded as 'eins'
738 static int ast_say_number_full_de(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
741 int mf
= 1; /* +1 = male and neuter; -1 = female */
745 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
747 if (options
&& (!strncasecmp(options
, "f",1)))
751 /* The grammar for German numbers is the same as for English except
753 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
754 * "one-and twenty" and 68 is "eight-and sixty".
755 * - "one" varies according to gender
756 * - 100 is 'hundert', however all other instances are 'ein hundert'
757 * - 1000 is 'tausend', however all other instances are 'ein tausend'
758 * - 1000000 is always 'eine million'
759 * - "million" is different in singular and plural form
762 snprintf(fn
, sizeof(fn
), "digits/minus");
763 if ( num
> INT_MIN
) {
768 } else if (num
< 100 && t
) {
769 snprintf(fn
, sizeof(fn
), "digits/and");
771 } else if (num
== 1 && mf
== -1) {
772 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
774 } else if (num
< 20) {
775 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
777 } else if (num
< 100) {
780 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
783 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
786 } else if (num
== 100 && t
== 0) {
787 snprintf(fn
, sizeof(fn
), "digits/hundred");
789 } else if (num
< 1000) {
790 int hundreds
= num
/ 100;
793 snprintf(fn
, sizeof(fn
), "digits/1N");
795 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
797 snprintf(fna
, sizeof(fna
), "digits/hundred");
799 } else if (num
== 1000 && t
== 0) {
800 snprintf(fn
, sizeof(fn
), "digits/thousand");
802 } else if (num
< 1000000) {
803 int thousands
= num
/ 1000;
806 if (thousands
== 1) {
807 snprintf(fn
, sizeof(fn
), "digits/1N");
808 snprintf(fna
, sizeof(fna
), "digits/thousand");
810 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
813 snprintf(fn
, sizeof(fn
), "digits/thousand");
815 } else if (num
< 1000000000) {
816 int millions
= num
/ 1000000;
820 snprintf(fn
, sizeof(fn
), "digits/1F");
821 snprintf(fna
, sizeof(fna
), "digits/million");
823 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
826 snprintf(fn
, sizeof(fn
), "digits/millions");
828 } else if (num
<= INT_MAX
) {
829 int billions
= num
/ 1000000000;
830 num
= num
% 1000000000;
833 snprintf(fn
, sizeof(fn
), "digits/1F");
834 snprintf(fna
, sizeof(fna
), "digits/milliard");
836 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
840 snprintf(fn
, sizeof(fn
), "digits/milliards");
843 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
847 if(!ast_streamfile(chan
, fn
, language
)) {
848 if ((audiofd
> -1) && (ctrlfd
> -1))
849 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
851 res
= ast_waitstream(chan
, ints
);
853 ast_stopstream(chan
);
855 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
856 if ((audiofd
> -1) && (ctrlfd
> -1))
857 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
859 res
= ast_waitstream(chan
, ints
);
861 ast_stopstream(chan
);
869 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
871 In addition to American English, the following sounds are required: "and"
873 static int ast_say_number_full_en_GB(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
880 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
882 while(!res
&& (num
|| playh
|| playa
)) {
884 snprintf(fn
, sizeof(fn
), "digits/minus");
885 if ( num
> INT_MIN
) {
891 snprintf(fn
, sizeof(fn
), "digits/hundred");
894 snprintf(fn
, sizeof(fn
), "digits/and");
896 } else if (num
< 20) {
897 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
899 } else if (num
< 100) {
900 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
901 num
-= ((num
/ 10) * 10);
902 } else if (num
< 1000) {
903 int hundreds
= num
/ 100;
904 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
907 num
-= 100 * hundreds
;
910 } else if (num
< 1000000) {
911 res
= ast_say_number_full_en_GB(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
914 snprintf(fn
, sizeof(fn
), "digits/thousand");
916 if (num
&& num
< 100)
918 } else if (num
< 1000000000) {
919 int millions
= num
/ 1000000;
920 res
= ast_say_number_full_en_GB(chan
, millions
, ints
, language
, audiofd
, ctrlfd
);
923 snprintf(fn
, sizeof(fn
), "digits/million");
925 if (num
&& num
< 100)
928 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
933 if(!ast_streamfile(chan
, fn
, language
)) {
934 if ((audiofd
> -1) && (ctrlfd
> -1))
935 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
937 res
= ast_waitstream(chan
, ints
);
939 ast_stopstream(chan
);
945 /*! \brief ast_say_number_full_es: Spanish syntax */
947 Requires a few new audios:
948 1F.gsm: feminine 'una'
949 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
951 static int ast_say_number_full_es(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
955 int mf
= 0; /* +1 = male; -1 = female */
958 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
961 if (!strncasecmp(options
, "f",1))
963 else if (!strncasecmp(options
, "m", 1))
967 while (!res
&& num
) {
969 snprintf(fn
, sizeof(fn
), "digits/minus");
970 if ( num
> INT_MIN
) {
976 snprintf(fn
, sizeof(fn
), "digits/and");
978 } else if (num
== 1) {
980 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
982 snprintf(fn
, sizeof(fn
), "digits/%dM", num
);
984 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
986 } else if (num
< 31) {
987 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
989 } else if (num
< 100) {
990 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
991 num
-= ((num
/10)*10);
994 } else if (num
== 100) {
995 snprintf(fn
, sizeof(fn
), "digits/100");
997 } else if (num
< 200) {
998 snprintf(fn
, sizeof(fn
), "digits/100-and");
1002 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100)*100);
1003 num
-= ((num
/100)*100);
1004 } else if (num
< 2000) {
1006 snprintf(fn
, sizeof(fn
), "digits/thousand");
1008 if (num
< 1000000) {
1009 res
= ast_say_number_full_es(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1013 snprintf(fn
, sizeof(fn
), "digits/thousand");
1015 if (num
< 2147483640) {
1016 if ((num
/1000000) == 1) {
1017 res
= ast_say_number_full_es(chan
, num
/ 1000000, ints
, language
, "M", audiofd
, ctrlfd
);
1020 snprintf(fn
, sizeof(fn
), "digits/million");
1022 res
= ast_say_number_full_es(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1025 snprintf(fn
, sizeof(fn
), "digits/millions");
1027 num
= num
% 1000000;
1029 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1037 if(!ast_streamfile(chan
, fn
, language
)) {
1038 if ((audiofd
> -1) && (ctrlfd
> -1))
1039 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1041 res
= ast_waitstream(chan
, ints
);
1043 ast_stopstream(chan
);
1051 /*! \brief ast_say_number_full_fr: French syntax */
1052 /* Extra sounds needed:
1055 static int ast_say_number_full_fr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
1060 int mf
= 1; /* +1 = male; -1 = female */
1063 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1065 if (options
&& !strncasecmp(options
, "f",1))
1068 while(!res
&& (num
|| playh
|| playa
)) {
1070 snprintf(fn
, sizeof(fn
), "digits/minus");
1071 if ( num
> INT_MIN
) {
1077 snprintf(fn
, sizeof(fn
), "digits/hundred");
1080 snprintf(fn
, sizeof(fn
), "digits/et");
1082 } else if (num
== 1) {
1084 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1086 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1088 } else if (num
< 21) {
1089 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1091 } else if (num
< 70) {
1092 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1093 if ((num
% 10) == 1) playa
++;
1095 } else if (num
< 80) {
1096 snprintf(fn
, sizeof(fn
), "digits/60");
1097 if ((num
% 10) == 1) playa
++;
1099 } else if (num
< 100) {
1100 snprintf(fn
, sizeof(fn
), "digits/80");
1102 } else if (num
< 200) {
1103 snprintf(fn
, sizeof(fn
), "digits/hundred");
1105 } else if (num
< 1000) {
1106 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1109 } else if (num
< 2000) {
1110 snprintf(fn
, sizeof(fn
), "digits/thousand");
1112 } else if (num
< 1000000) {
1113 res
= ast_say_number_full_fr(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1116 snprintf(fn
, sizeof(fn
), "digits/thousand");
1118 } else if (num
< 1000000000) {
1119 res
= ast_say_number_full_fr(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1122 snprintf(fn
, sizeof(fn
), "digits/million");
1123 num
= num
% 1000000;
1125 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1129 if(!ast_streamfile(chan
, fn
, language
)) {
1130 if ((audiofd
> -1) && (ctrlfd
> -1))
1131 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1133 res
= ast_waitstream(chan
, ints
);
1135 ast_stopstream(chan
);
1143 /*! \brief ast_say_number_full_he: Hebrew syntax */
1144 /* Extra sounds needed:
1148 2thousands: 2 thousand
1149 thousands: plural of 'thousand'
1150 3sF 'Smichut forms (female)
1157 3s 'Smichut' forms (male)
1174 TODO: 've' should sometimed be 'hu':
1175 * before 'shtaym' (2, F)
1176 * before 'shnaym' (2, M)
1177 * before 'shlosha' (3, M)
1178 * before 'shmone' (8, M)
1179 * before 'shlosim' (30)
1180 * before 'shmonim' (80)
1186 #define SAY_NUM_BUF_SIZE 256
1187 static int ast_say_number_full_he(struct ast_channel
*chan
, int num
,
1188 const char *ints
, const char *language
, const char *options
,
1189 int audiofd
, int ctrlfd
)
1192 int state
= 0; /* no need to save anything */
1193 int mf
= 1; /* +1 = Masculin; -1 = Feminin */
1194 char fn
[SAY_NUM_BUF_SIZE
] = "";
1195 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: started. "
1196 "num: %d, options=\"%s\"\n",
1200 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1202 if (options
&& !strncasecmp(options
, "f",1))
1205 /* Do we have work to do? */
1206 while(!res
&& (num
|| (state
>0) )) {
1207 /* first type of work: play a second sound. In this loop
1208 * we can only play one sound file at a time. Thus playing
1209 * a second one requires repeating the loop just for the
1210 * second file. The variable 'state' remembers where we were.
1211 * state==0 is the normal mode and it means that we continue
1212 * to check if the number num has yet anything left.
1214 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: num: %d, "
1215 "state=%d, options=\"%s\", mf=%d\n",
1216 num
, state
, options
, mf
1219 snprintf(fn
, sizeof(fn
), "digits/hundred");
1221 } else if (state
==2) {
1222 snprintf(fn
, sizeof(fn
), "digits/ve");
1224 } else if (state
==3) {
1225 snprintf(fn
, sizeof(fn
), "digits/thousands");
1227 } else if (num
<21) {
1229 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1231 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1233 } else if (num
< 100) {
1234 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1237 } else if (num
< 200) {
1238 snprintf(fn
, sizeof(fn
), "digits/hundred");
1240 } else if (num
< 300) {
1241 snprintf(fn
, sizeof(fn
), "digits/hundred");
1243 } else if (num
< 1000) {
1244 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1247 } else if (num
< 2000) {
1248 snprintf(fn
, sizeof(fn
), "digits/thousand");
1250 } else if (num
< 3000) {
1251 snprintf(fn
, sizeof(fn
), "digits/2thousand");
1254 } else if (num
< 20000) {
1255 snprintf(fn
, sizeof(fn
), "digits/%ds",(num
/1000));
1258 } else if (num
< 1000000) {
1259 res
= ast_say_number_full_he(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1262 snprintf(fn
, sizeof(fn
), "digits/thousand");
1264 } else if (num
< 1000000000) {
1265 res
= ast_say_number_full_he(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1268 snprintf(fn
, sizeof(fn
), "digits/million");
1269 num
= num
% 1000000;
1271 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1275 if(!ast_streamfile(chan
, fn
, language
)) {
1276 if ((audiofd
> -1) && (ctrlfd
> -1))
1277 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1279 res
= ast_waitstream(chan
, ints
);
1281 ast_stopstream(chan
);
1287 /*! \brief ast_say_number_full_it: Italian */
1288 static int ast_say_number_full_it(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1296 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1301 Like english, numbers up to 20 are a single 'word', and others
1302 compound, but with exceptions.
1303 For example 21 is not twenty-one, but there is a single word in 'it'.
1304 Idem for 28 (ie when a the 2nd part of a compund number
1305 starts with a vowel)
1307 There are exceptions also for hundred, thousand and million.
1308 In english 100 = one hundred, 200 is two hundred.
1309 In italian 100 = cento , like to say hundred (without one),
1310 200 and more are like english.
1312 Same applies for thousand:
1313 1000 is one thousand in en, 2000 is two thousand.
1314 In it we have 1000 = mille , 2000 = 2 mila
1316 For million(s) we use the plural, if more than one
1317 Also, one million is abbreviated in it, like on-million,
1318 or 'un milione', not 'uno milione'.
1319 So the right file is provided.
1322 while(!res
&& (num
|| playh
)) {
1324 snprintf(fn
, sizeof(fn
), "digits/minus");
1325 if ( num
> INT_MIN
) {
1331 snprintf(fn
, sizeof(fn
), "digits/hundred");
1333 } else if (num
< 20) {
1334 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1336 } else if (num
== 21) {
1337 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1339 } else if (num
== 28) {
1340 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1342 } else if (num
== 31) {
1343 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1345 } else if (num
== 38) {
1346 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1348 } else if (num
== 41) {
1349 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1351 } else if (num
== 48) {
1352 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1354 } else if (num
== 51) {
1355 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1357 } else if (num
== 58) {
1358 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1360 } else if (num
== 61) {
1361 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1363 } else if (num
== 68) {
1364 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1366 } else if (num
== 71) {
1367 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1369 } else if (num
== 78) {
1370 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1372 } else if (num
== 81) {
1373 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1375 } else if (num
== 88) {
1376 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1378 } else if (num
== 91) {
1379 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1381 } else if (num
== 98) {
1382 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1384 } else if (num
< 100) {
1385 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1386 num
-= ((num
/ 10) * 10);
1389 if ((num
/ 100) > 1) {
1390 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1393 snprintf(fn
, sizeof(fn
), "digits/hundred");
1395 num
-= ((num
/ 100) * 100);
1397 if (num
< 1000000) { /* 1,000,000 */
1399 res
= ast_say_number_full_it(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1404 if ((tempnum
/ 1000) < 2)
1405 snprintf(fn
, sizeof(fn
), "digits/thousand");
1406 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1407 snprintf(fn
, sizeof(fn
), "digits/thousands");
1409 if (num
< 1000000000) { /* 1,000,000,000 */
1410 if ((num
/ 1000000) > 1)
1411 res
= ast_say_number_full_it(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1415 num
= num
% 1000000;
1416 if ((tempnum
/ 1000000) < 2)
1417 snprintf(fn
, sizeof(fn
), "digits/million");
1419 snprintf(fn
, sizeof(fn
), "digits/millions");
1421 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1428 if(!ast_streamfile(chan
, fn
, language
)) {
1429 if ((audiofd
> -1) && (ctrlfd
> -1))
1430 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1432 res
= ast_waitstream(chan
, ints
);
1434 ast_stopstream(chan
);
1440 /*! \brief ast_say_number_full_nl: dutch syntax */
1441 /* New files: digits/nl-en
1443 static int ast_say_number_full_nl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1450 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1451 while (!res
&& (num
|| playh
)) {
1453 snprintf(fn
, sizeof(fn
), "digits/minus");
1454 if ( num
> INT_MIN
) {
1460 snprintf(fn
, sizeof(fn
), "digits/hundred");
1462 } else if (num
< 20) {
1463 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1465 } else if (num
< 100) {
1468 res
= ast_say_number_full_nl(chan
, units
, ints
, language
, audiofd
, ctrlfd
);
1472 snprintf(fn
, sizeof(fn
), "digits/nl-en");
1474 snprintf(fn
, sizeof(fn
), "digits/%d", num
- units
);
1479 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1481 num
-= ((num
/ 100) * 100);
1483 if (num
< 1000000) { /* 1,000,000 */
1484 res
= ast_say_number_full_en(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1488 snprintf(fn
, sizeof(fn
), "digits/thousand");
1490 if (num
< 1000000000) { /* 1,000,000,000 */
1491 res
= ast_say_number_full_en(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1494 num
= num
% 1000000;
1495 snprintf(fn
, sizeof(fn
), "digits/million");
1497 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1505 if(!ast_streamfile(chan
, fn
, language
)) {
1506 if ((audiofd
> -1) && (ctrlfd
> -1))
1507 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1509 res
= ast_waitstream(chan
, ints
);
1511 ast_stopstream(chan
);
1517 /*! \brief ast_say_number_full_no: Norwegian syntax */
1519 In addition to American English, the following sounds are required: "and", "1N"
1521 static int ast_say_number_full_no(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
1526 int cn
= 1; /* +1 = commune; -1 = neuter */
1530 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1532 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
1534 while(!res
&& (num
|| playh
|| playa
)) {
1535 /* The grammar for Norwegian numbers is the same as for English except
1536 * for the following:
1537 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1538 * "and" before the last two digits, i.e. 2034 is "two thousand and
1539 * thirty-four" and 1000012 is "one million and twelve".
1542 snprintf(fn
, sizeof(fn
), "digits/minus");
1543 if ( num
> INT_MIN
) {
1549 snprintf(fn
, sizeof(fn
), "digits/hundred");
1552 snprintf(fn
, sizeof(fn
), "digits/and");
1554 } else if (num
== 1 && cn
== -1) {
1555 snprintf(fn
, sizeof(fn
), "digits/1N");
1557 } else if (num
< 20) {
1558 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1560 } else if (num
< 100) {
1561 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1562 num
-= ((num
/ 10) * 10);
1563 } else if (num
< 1000) {
1564 int hundreds
= num
/ 100;
1566 snprintf(fn
, sizeof(fn
), "digits/1N");
1568 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
1571 num
-= 100 * hundreds
;
1574 } else if (num
< 1000000) {
1575 res
= ast_say_number_full_no(chan
, num
/ 1000, ints
, language
, "n", audiofd
, ctrlfd
);
1578 snprintf(fn
, sizeof(fn
), "digits/thousand");
1580 if (num
&& num
< 100)
1582 } else if (num
< 1000000000) {
1583 int millions
= num
/ 1000000;
1584 res
= ast_say_number_full_no(chan
, millions
, ints
, language
, "c", audiofd
, ctrlfd
);
1587 snprintf(fn
, sizeof(fn
), "digits/million");
1588 num
= num
% 1000000;
1589 if (num
&& num
< 100)
1592 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1597 if(!ast_streamfile(chan
, fn
, language
)) {
1598 if ((audiofd
> -1) && (ctrlfd
> -1))
1599 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1601 res
= ast_waitstream(chan
, ints
);
1603 ast_stopstream(chan
);
1610 char *separator_dziesiatek
;
1614 char *dziesiatki
[10];
1619 static char *pl_rzad_na_tekst(odmiana
*odm
, int i
, int rzad
)
1625 return odm
->rzedy
[rzad
- 1][0];
1626 if ((i
> 21 || i
< 11) && i
%10 > 1 && i
%10 < 5)
1627 return odm
->rzedy
[rzad
- 1][1];
1629 return odm
->rzedy
[rzad
- 1][2];
1632 static char* pl_append(char* buffer
, char* str
)
1634 strcpy(buffer
, str
);
1635 buffer
+= strlen(str
);
1639 static void pl_odtworz_plik(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, char *fn
)
1641 char file_name
[255] = "digits/";
1642 strcat(file_name
, fn
);
1643 ast_log(LOG_DEBUG
, "Trying to play: %s\n", file_name
);
1644 if (!ast_streamfile(chan
, file_name
, language
)) {
1645 if ((audiofd
> -1) && (ctrlfd
> -1))
1646 ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1648 ast_waitstream(chan
, ints
);
1650 ast_stopstream(chan
);
1653 static void powiedz(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, odmiana
*odm
, int rzad
, int i
)
1655 /* Initialise variables to allow compilation on Debian-stable, etc */
1665 if (i
== 0 && rzad
> 0) {
1669 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[0]);
1673 m1000E6
= i
% 1000000000;
1674 i1000E6
= i
/ 1000000000;
1676 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+3, i1000E6
);
1678 m1000E3
= m1000E6
% 1000000;
1679 i1000E3
= m1000E6
/ 1000000;
1681 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+2, i1000E3
);
1683 m1000
= m1000E3
% 1000;
1684 i1000
= m1000E3
/ 1000;
1686 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+1, i1000
);
1692 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->setki
[i100
]);
1694 if ( m100
> 0 && m100
<=9 ) {
1696 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
]);
1698 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[m100
]);
1699 } else if (m100
% 10 == 0) {
1700 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1701 } else if (m100
<= 19 ) {
1702 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->nastki
[m100
% 10]);
1703 } else if (m100
!= 0) {
1704 if (odm
->separator_dziesiatek
[0]==' ') {
1705 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1706 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
% 10]);
1710 b
= pl_append(b
, odm
->dziesiatki
[m100
/ 10]);
1711 b
= pl_append(b
, odm
->separator_dziesiatek
);
1712 b
= pl_append(b
, odm
->cyfry2
[m100
% 10]);
1713 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, buf
);
1718 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, pl_rzad_na_tekst(odm
, i
, rzad
));
1722 /* ast_say_number_full_pl: Polish syntax */
1723 static int ast_say_number_full_pl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
1733 1000000000.2 miliardy
1734 1000000000.5 miliardow
1798 70m siedemdziesieciu
1810 90m dziewiedziesieciu
1812 and combinations of eg.: 20_1, 30m_3m, etc...
1816 char *zenski_cyfry
[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1818 char *zenski_cyfry2
[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1820 char *meski_cyfry
[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1822 char *meski_cyfry2
[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1824 char *meski_setki
[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1826 char *meski_dziesiatki
[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1828 char *meski_nastki
[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1830 char *nijaki_cyfry
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1832 char *nijaki_cyfry2
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1834 char *nijaki_setki
[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1836 char *nijaki_dziesiatki
[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1838 char *nijaki_nastki
[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1840 char *rzedy
[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1842 /* Initialise variables to allow compilation on Debian-stable, etc */
1845 static odmiana
*odmiana_nieosobowa
= NULL
;
1846 static odmiana
*odmiana_meska
= NULL
;
1847 static odmiana
*odmiana_zenska
= NULL
;
1849 if (odmiana_nieosobowa
== NULL
) {
1850 odmiana_nieosobowa
= (odmiana
*) malloc(sizeof(odmiana
));
1852 odmiana_nieosobowa
->separator_dziesiatek
= " ";
1854 memcpy(odmiana_nieosobowa
->cyfry
, nijaki_cyfry
, sizeof(odmiana_nieosobowa
->cyfry
));
1855 memcpy(odmiana_nieosobowa
->cyfry2
, nijaki_cyfry2
, sizeof(odmiana_nieosobowa
->cyfry
));
1856 memcpy(odmiana_nieosobowa
->setki
, nijaki_setki
, sizeof(odmiana_nieosobowa
->setki
));
1857 memcpy(odmiana_nieosobowa
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_nieosobowa
->dziesiatki
));
1858 memcpy(odmiana_nieosobowa
->nastki
, nijaki_nastki
, sizeof(odmiana_nieosobowa
->nastki
));
1859 memcpy(odmiana_nieosobowa
->rzedy
, rzedy
, sizeof(odmiana_nieosobowa
->rzedy
));
1862 if (odmiana_zenska
== NULL
) {
1863 odmiana_zenska
= (odmiana
*) malloc(sizeof(odmiana
));
1865 odmiana_zenska
->separator_dziesiatek
= " ";
1867 memcpy(odmiana_zenska
->cyfry
, zenski_cyfry
, sizeof(odmiana_zenska
->cyfry
));
1868 memcpy(odmiana_zenska
->cyfry2
, zenski_cyfry2
, sizeof(odmiana_zenska
->cyfry
));
1869 memcpy(odmiana_zenska
->setki
, nijaki_setki
, sizeof(odmiana_zenska
->setki
));
1870 memcpy(odmiana_zenska
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_zenska
->dziesiatki
));
1871 memcpy(odmiana_zenska
->nastki
, nijaki_nastki
, sizeof(odmiana_zenska
->nastki
));
1872 memcpy(odmiana_zenska
->rzedy
, rzedy
, sizeof(odmiana_zenska
->rzedy
));
1875 if (odmiana_meska
== NULL
) {
1876 odmiana_meska
= (odmiana
*) malloc(sizeof(odmiana
));
1878 odmiana_meska
->separator_dziesiatek
= " ";
1880 memcpy(odmiana_meska
->cyfry
, meski_cyfry
, sizeof(odmiana_meska
->cyfry
));
1881 memcpy(odmiana_meska
->cyfry2
, meski_cyfry2
, sizeof(odmiana_meska
->cyfry
));
1882 memcpy(odmiana_meska
->setki
, meski_setki
, sizeof(odmiana_meska
->setki
));
1883 memcpy(odmiana_meska
->dziesiatki
, meski_dziesiatki
, sizeof(odmiana_meska
->dziesiatki
));
1884 memcpy(odmiana_meska
->nastki
, meski_nastki
, sizeof(odmiana_meska
->nastki
));
1885 memcpy(odmiana_meska
->rzedy
, rzedy
, sizeof(odmiana_meska
->rzedy
));
1889 if (strncasecmp(options
, "f", 1) == 0)
1891 else if (strncasecmp(options
, "m", 1) == 0)
1894 o
= odmiana_nieosobowa
;
1896 o
= odmiana_nieosobowa
;
1898 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, o
, 0, num
);
1902 /* ast_say_number_full_pt: Portuguese syntax */
1903 /* Extra sounds needed: */
1904 /* For feminin all sound files end with F */
1905 /* 100E for 100+ something */
1906 /* 1000000S for plural */
1907 /* pt-e for 'and' */
1908 static int ast_say_number_full_pt(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
1912 int mf
= 1; /* +1 = male; -1 = female */
1916 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1918 if (options
&& !strncasecmp(options
, "f",1))
1921 while(!res
&& num
) {
1923 snprintf(fn
, sizeof(fn
), "digits/minus");
1924 if ( num
> INT_MIN
) {
1929 } else if (num
< 20) {
1930 if ((num
== 1 || num
== 2) && (mf
< 0))
1931 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1933 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1935 } else if (num
< 100) {
1936 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 10) * 10);
1940 } else if (num
< 1000) {
1942 snprintf(fn
, sizeof(fn
), "digits/100");
1944 snprintf(fn
, sizeof(fn
), "digits/100E");
1946 if (mf
< 0 && num
> 199)
1947 snprintf(fn
, sizeof(fn
), "digits/%dF", (num
/ 100) * 100);
1949 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100) * 100);
1954 } else if (num
< 1000000) {
1956 res
= ast_say_number_full_pt(chan
, (num
/ 1000) * mf
, ints
, language
, options
, audiofd
, ctrlfd
);
1960 snprintf(fn
, sizeof(fn
), "digits/1000");
1961 if ((num
% 1000) && ((num
% 1000) < 100 || !(num
% 100)))
1964 } else if (num
< 1000000000) {
1965 res
= ast_say_number_full_pt(chan
, (num
/ 1000000), ints
, language
, options
, audiofd
, ctrlfd
);
1969 snprintf(fn
, sizeof(fn
), "digits/1000000");
1971 snprintf(fn
, sizeof(fn
), "digits/1000000S");
1973 if ((num
% 1000000) &&
1975 ((!((num
/ 1000) % 1000) && ((num
% 1000) < 100 || !(num
% 100))) ||
1976 /* no hundreds and below */
1977 (!(num
% 1000) && (((num
/ 1000) % 1000) < 100 || !((num
/ 1000) % 100))) ) )
1979 num
= num
% 1000000;
1982 if (!ast_streamfile(chan
, fn
, language
)) {
1983 if ((audiofd
> -1) && (ctrlfd
> -1))
1984 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1986 res
= ast_waitstream(chan
, ints
);
1988 ast_stopstream(chan
);
1990 if (!res
&& playh
) {
1991 res
= wait_file(chan
, ints
, "digits/pt-e", language
);
1992 ast_stopstream(chan
);
1999 /*! \brief ast_say_number_full_se: Swedish syntax */
2000 static int ast_say_number_full_se(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2005 int cn
= 1; /* +1 = commune; -1 = neuter */
2007 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2008 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
2010 while(!res
&& (num
|| playh
)) {
2012 snprintf(fn
, sizeof(fn
), "digits/minus");
2013 if ( num
> INT_MIN
) {
2019 snprintf(fn
, sizeof(fn
), "digits/hundred");
2021 } else if (num
< 20) {
2022 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2024 } else if (num
< 100) {
2025 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2026 num
-= ((num
/ 10) * 10);
2027 } else if (num
== 1 && cn
== -1) { /* En eller ett? */
2028 snprintf(fn
, sizeof(fn
), "digits/1N");
2032 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2034 num
-= ((num
/ 100) * 100);
2036 if (num
< 1000000) { /* 1,000,000 */
2037 res
= ast_say_number_full_se(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
2042 snprintf(fn
, sizeof(fn
), "digits/thousand");
2044 if (num
< 1000000000) { /* 1,000,000,000 */
2045 res
= ast_say_number_full_se(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
2049 num
= num
% 1000000;
2050 snprintf(fn
, sizeof(fn
), "digits/million");
2052 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2059 if(!ast_streamfile(chan
, fn
, language
)) {
2060 if ((audiofd
> -1) && (ctrlfd
> -1))
2061 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2063 res
= ast_waitstream(chan
, ints
);
2064 ast_stopstream(chan
);
2071 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2072 static int ast_say_number_full_tw(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2078 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2080 while(!res
&& (num
|| playh
)) {
2082 snprintf(fn
, sizeof(fn
), "digits/minus");
2083 if ( num
> INT_MIN
) {
2089 snprintf(fn
, sizeof(fn
), "digits/hundred");
2091 } else if (num
< 10) {
2092 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2094 } else if (num
< 100) {
2095 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2096 num
-= ((num
/ 10) * 10);
2099 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2101 num
-= ((num
/ 100) * 100);
2103 if (num
< 1000000) { /* 1,000,000 */
2104 res
= ast_say_number_full_tw(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
2108 snprintf(fn
, sizeof(fn
), "digits/thousand");
2110 if (num
< 1000000000) { /* 1,000,000,000 */
2111 res
= ast_say_number_full_tw(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
2114 num
= num
% 1000000;
2115 snprintf(fn
, sizeof(fn
), "digits/million");
2117 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2124 if(!ast_streamfile(chan
, fn
, language
)) {
2125 if ((audiofd
> -1) && (ctrlfd
> -1))
2126 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2128 res
= ast_waitstream(chan
, ints
);
2130 ast_stopstream(chan
);
2137 /*! \brief determine last digits for thousands/millions (ru) */
2138 static int get_lastdigits_ru(int num
) {
2141 } else if (num
< 100) {
2142 return get_lastdigits_ru(num
% 10);
2143 } else if (num
< 1000) {
2144 return get_lastdigits_ru(num
% 100);
2146 return 0; /* number too big */
2150 /*! \brief ast_say_number_full_ru: Russian syntax */
2151 /*! \brief additional files:
2152 n00.gsm (one hundred, two hundred, ...)
2155 thousands-i.gsm (tisyachi)
2156 million-a.gsm (milliona)
2162 where 'n' from 1 to 9
2164 static int ast_say_number_full_ru(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2170 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2172 while(!res
&& (num
)) {
2174 snprintf(fn
, sizeof(fn
), "digits/minus");
2175 if ( num
> INT_MIN
) {
2180 } else if (num
< 20) {
2181 if(options
&& strlen(options
) == 1 && num
< 3) {
2182 snprintf(fn
, sizeof(fn
), "digits/%d%s", num
, options
);
2184 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2187 } else if (num
< 100) {
2188 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 10));
2190 } else if (num
< 1000){
2191 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 100));
2193 } else if (num
< 1000000) { /* 1,000,000 */
2194 lastdigits
= get_lastdigits_ru(num
/ 1000);
2196 if (lastdigits
< 3) {
2197 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, "f", audiofd
, ctrlfd
);
2199 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2203 if (lastdigits
== 1) {
2204 snprintf(fn
, sizeof(fn
), "digits/thousand");
2205 } else if (lastdigits
> 1 && lastdigits
< 5) {
2206 snprintf(fn
, sizeof(fn
), "digits/thousands-i");
2208 snprintf(fn
, sizeof(fn
), "digits/thousands");
2211 } else if (num
< 1000000000) { /* 1,000,000,000 */
2212 lastdigits
= get_lastdigits_ru(num
/ 1000000);
2214 res
= ast_say_number_full_ru(chan
, num
/ 1000000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2217 if (lastdigits
== 1) {
2218 snprintf(fn
, sizeof(fn
), "digits/million");
2219 } else if (lastdigits
> 1 && lastdigits
< 5) {
2220 snprintf(fn
, sizeof(fn
), "digits/million-a");
2222 snprintf(fn
, sizeof(fn
), "digits/millions");
2226 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2230 if (!ast_streamfile(chan
, fn
, language
)) {
2231 if ((audiofd
> -1) && (ctrlfd
> -1))
2232 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2234 res
= ast_waitstream(chan
, ints
);
2236 ast_stopstream(chan
);
2243 /*! \brief ast_say_enumeration_full: call language-specific functions */
2244 /* Called from AGI */
2245 static int say_enumeration_full(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2247 if (!strcasecmp(language
,"en") ) { /* English syntax */
2248 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2249 } else if (!strcasecmp(language
, "da") ) { /* Danish syntax */
2250 return(ast_say_enumeration_full_da(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2251 } else if (!strcasecmp(language
, "de") ) { /* German syntax */
2252 return(ast_say_enumeration_full_de(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2255 /* Default to english */
2256 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2259 /*! \brief ast_say_enumeration_full_en: English syntax */
2260 /* This is the default syntax, if no other syntax defined in this file is used */
2261 static int ast_say_enumeration_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2266 while(!res
&& num
) {
2268 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2269 if ( num
> INT_MIN
) {
2274 } else if (num
< 20) {
2275 snprintf(fn
, sizeof(fn
), "digits/h-%d", num
);
2277 } else if (num
< 100) {
2278 int tens
= num
/ 10;
2281 snprintf(fn
, sizeof(fn
), "digits/h-%d", (tens
* 10));
2283 snprintf(fn
, sizeof(fn
), "digits/%d", (tens
* 10));
2285 } else if (num
< 1000) {
2286 int hundreds
= num
/ 100;
2288 if (hundreds
> 1 || t
== 1) {
2289 res
= ast_say_number_full_en(chan
, hundreds
, ints
, language
, audiofd
, ctrlfd
);
2294 snprintf(fn
, sizeof(fn
), "digits/hundred");
2296 snprintf(fn
, sizeof(fn
), "digits/h-hundred");
2298 } else if (num
< 1000000) {
2299 int thousands
= num
/ 1000;
2301 if (thousands
> 1 || t
== 1) {
2302 res
= ast_say_number_full_en(chan
, thousands
, ints
, language
, audiofd
, ctrlfd
);
2307 snprintf(fn
, sizeof(fn
), "digits/thousand");
2309 snprintf(fn
, sizeof(fn
), "digits/h-thousand");
2312 } else if (num
< 1000000000) {
2313 int millions
= num
/ 1000000;
2314 num
= num
% 1000000;
2316 res
= ast_say_number_full_en(chan
, millions
, ints
, language
, audiofd
, ctrlfd
);
2320 snprintf(fn
, sizeof(fn
), "digits/million");
2322 snprintf(fn
, sizeof(fn
), "digits/h-million");
2324 } else if (num
< INT_MAX
) {
2325 int billions
= num
/ 1000000000;
2326 num
= num
% 1000000000;
2328 res
= ast_say_number_full_en(chan
, billions
, ints
, language
, audiofd
, ctrlfd
);
2332 snprintf(fn
, sizeof(fn
), "digits/billion");
2334 snprintf(fn
, sizeof(fn
), "digits/h-billion");
2336 } else if (num
== INT_MAX
) {
2337 snprintf(fn
, sizeof(fn
), "digits/h-last");
2340 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2345 if (!ast_streamfile(chan
, fn
, language
)) {
2346 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2347 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2349 res
= ast_waitstream(chan
, ints
);
2352 ast_stopstream(chan
);
2358 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2359 static int ast_say_enumeration_full_da(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2361 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2363 char fn
[256] = "", fna
[256] = "";
2366 if (options
&& !strncasecmp(options
, "f",1)) {
2368 } else if (options
&& !strncasecmp(options
, "n",1)) {
2375 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2377 while(!res
&& num
) {
2379 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2380 if ( num
> INT_MIN
) {
2385 } else if (num
< 100 && t
) {
2386 snprintf(fn
, sizeof(fn
), "digits/and");
2388 } else if (num
< 20) {
2389 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2391 } else if (num
< 100) {
2392 int ones
= num
% 10;
2394 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2397 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2400 } else if (num
== 100 && t
== 0) {
2401 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2403 } else if (num
< 1000) {
2404 int hundreds
= num
/ 100;
2406 if (hundreds
== 1) {
2407 snprintf(fn
, sizeof(fn
), "digits/1N");
2409 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2412 snprintf(fna
, sizeof(fna
), "digits/hundred");
2414 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2417 } else if (num
< 1000000) {
2418 int thousands
= num
/ 1000;
2420 if (thousands
== 1) {
2422 snprintf(fn
, sizeof(fn
), "digits/1N");
2423 snprintf(fna
, sizeof(fna
), "digits/thousand");
2426 snprintf(fn
, sizeof(fn
), "digits/1N");
2427 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2429 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2433 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2438 snprintf(fn
, sizeof(fn
), "digits/thousand");
2440 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2444 } else if (num
< 1000000000) {
2445 int millions
= num
/ 1000000;
2446 num
= num
% 1000000;
2447 if (millions
== 1) {
2449 snprintf(fn
, sizeof(fn
), "digits/1F");
2450 snprintf(fna
, sizeof(fna
), "digits/million");
2452 snprintf(fn
, sizeof(fn
), "digits/1N");
2453 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2456 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2461 snprintf(fn
, sizeof(fn
), "digits/millions");
2463 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2467 } else if (num
< INT_MAX
) {
2468 int billions
= num
/ 1000000000;
2469 num
= num
% 1000000000;
2470 if (billions
== 1) {
2472 snprintf(fn
, sizeof(fn
), "digits/1F");
2473 snprintf(fna
, sizeof(fna
), "digits/milliard");
2475 snprintf(fn
, sizeof(fn
), "digits/1N");
2476 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2479 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2483 snprintf(fn
, sizeof(fna
), "digits/milliards");
2485 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2489 } else if (num
== INT_MAX
) {
2490 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2493 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2498 if (!ast_streamfile(chan
, fn
, language
)) {
2499 if ((audiofd
> -1) && (ctrlfd
> -1))
2500 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2502 res
= ast_waitstream(chan
, ints
);
2504 ast_stopstream(chan
);
2506 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2507 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2508 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2510 res
= ast_waitstream(chan
, ints
);
2513 ast_stopstream(chan
);
2521 /*! \brief ast_say_enumeration_full_de: German syntax */
2522 static int ast_say_enumeration_full_de(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2524 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2526 char fn
[256] = "", fna
[256] = "";
2529 if (options
&& !strncasecmp(options
, "f",1)) {
2531 } else if (options
&& !strncasecmp(options
, "n",1)) {
2538 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2540 while(!res
&& num
) {
2542 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2543 if ( num
> INT_MIN
) {
2548 } else if (num
< 100 && t
) {
2549 snprintf(fn
, sizeof(fn
), "digits/and");
2551 } else if (num
< 20) {
2552 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2554 } else if (num
< 100) {
2555 int ones
= num
% 10;
2557 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2560 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2563 } else if (num
== 100 && t
== 0) {
2564 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2566 } else if (num
< 1000) {
2567 int hundreds
= num
/ 100;
2569 if (hundreds
== 1) {
2570 snprintf(fn
, sizeof(fn
), "digits/1N");
2572 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2575 snprintf(fna
, sizeof(fna
), "digits/hundred");
2577 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2580 } else if (num
< 1000000) {
2581 int thousands
= num
/ 1000;
2583 if (thousands
== 1) {
2585 snprintf(fn
, sizeof(fn
), "digits/1N");
2586 snprintf(fna
, sizeof(fna
), "digits/thousand");
2589 snprintf(fn
, sizeof(fn
), "digits/1N");
2590 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2592 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2596 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2601 snprintf(fn
, sizeof(fn
), "digits/thousand");
2603 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2607 } else if (num
< 1000000000) {
2608 int millions
= num
/ 1000000;
2609 num
= num
% 1000000;
2610 if (millions
== 1) {
2612 snprintf(fn
, sizeof(fn
), "digits/1F");
2613 snprintf(fna
, sizeof(fna
), "digits/million");
2615 snprintf(fn
, sizeof(fn
), "digits/1N");
2616 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2619 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2624 snprintf(fn
, sizeof(fn
), "digits/millions");
2626 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2630 } else if (num
< INT_MAX
) {
2631 int billions
= num
/ 1000000000;
2632 num
= num
% 1000000000;
2633 if (billions
== 1) {
2635 snprintf(fn
, sizeof(fn
), "digits/1F");
2636 snprintf(fna
, sizeof(fna
), "digits/milliard");
2638 snprintf(fn
, sizeof(fn
), "digits/1N");
2639 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2642 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2646 snprintf(fn
, sizeof(fna
), "digits/milliards");
2648 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2652 } else if (num
== INT_MAX
) {
2653 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2656 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2661 if (!ast_streamfile(chan
, fn
, language
)) {
2662 if ((audiofd
> -1) && (ctrlfd
> -1))
2663 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2665 res
= ast_waitstream(chan
, ints
);
2667 ast_stopstream(chan
);
2669 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2670 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2671 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2673 res
= ast_waitstream(chan
, ints
);
2676 ast_stopstream(chan
);
2684 static int say_date(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2686 if (!strcasecmp(lang
, "en") ) { /* English syntax */
2687 return(ast_say_date_en(chan
, t
, ints
, lang
));
2688 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
2689 return(ast_say_date_da(chan
, t
, ints
, lang
));
2690 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
2691 return(ast_say_date_de(chan
, t
, ints
, lang
));
2692 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
2693 return(ast_say_date_fr(chan
, t
, ints
, lang
));
2694 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
2695 return(ast_say_date_nl(chan
, t
, ints
, lang
));
2696 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
2697 return(ast_say_date_pt(chan
, t
, ints
, lang
));
2698 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
2699 return(ast_say_date_gr(chan
, t
, ints
, lang
));
2702 /* Default to English */
2703 return(ast_say_date_en(chan
, t
, ints
, lang
));
2706 /* English syntax */
2707 int ast_say_date_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2712 ast_localtime(&t
,&tm
,NULL
);
2714 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2715 res
= ast_streamfile(chan
, fn
, lang
);
2717 res
= ast_waitstream(chan
, ints
);
2720 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2721 res
= ast_streamfile(chan
, fn
, lang
);
2723 res
= ast_waitstream(chan
, ints
);
2726 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2728 res
= ast_waitstream(chan
, ints
);
2730 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2735 int ast_say_date_da(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2740 ast_localtime(&t
,&tm
,NULL
);
2742 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2743 res
= ast_streamfile(chan
, fn
, lang
);
2745 res
= ast_waitstream(chan
, ints
);
2748 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2750 res
= ast_waitstream(chan
, ints
);
2752 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2753 res
= ast_streamfile(chan
, fn
, lang
);
2755 res
= ast_waitstream(chan
, ints
);
2759 int year
= tm
.tm_year
+ 1900;
2760 if (year
> 1999) { /* year 2000 and later */
2761 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2764 /* I'm not going to handle 1100 and prior */
2765 /* We'll just be silent on the year, instead of bombing out. */
2767 /* year 1100 to 1999. will anybody need this?!? */
2768 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2769 res
= wait_file(chan
, ints
, fn
, lang
);
2771 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2772 if (!res
&& year
% 100 != 0) {
2773 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
2783 int ast_say_date_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2788 ast_localtime(&t
,&tm
,NULL
);
2790 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2791 res
= ast_streamfile(chan
, fn
, lang
);
2793 res
= ast_waitstream(chan
, ints
);
2796 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2798 res
= ast_waitstream(chan
, ints
);
2800 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2801 res
= ast_streamfile(chan
, fn
, lang
);
2803 res
= ast_waitstream(chan
, ints
);
2807 int year
= tm
.tm_year
+ 1900;
2808 if (year
> 1999) { /* year 2000 and later */
2809 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2812 /* I'm not going to handle 1100 and prior */
2813 /* We'll just be silent on the year, instead of bombing out. */
2815 /* year 1100 to 1999. will anybody need this?!? */
2816 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2817 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2818 res
= wait_file(chan
, ints
, fn
, lang
);
2820 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2821 if (!res
&& year
% 100 != 0) {
2822 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
2832 int ast_say_date_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2837 ast_localtime(&t
,&tm
,NULL
);
2839 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2840 res
= ast_streamfile(chan
, fn
, lang
);
2842 res
= ast_waitstream(chan
, ints
);
2845 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2847 res
= ast_waitstream(chan
, ints
);
2849 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2850 res
= ast_streamfile(chan
, fn
, lang
);
2852 res
= ast_waitstream(chan
, ints
);
2855 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2860 int ast_say_date_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2865 ast_localtime(&t
,&tm
,NULL
);
2867 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2868 res
= ast_streamfile(chan
, fn
, lang
);
2870 res
= ast_waitstream(chan
, ints
);
2873 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2875 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2876 res
= ast_streamfile(chan
, fn
, lang
);
2878 res
= ast_waitstream(chan
, ints
);
2881 res
= ast_waitstream(chan
, ints
);
2883 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2887 /* Portuguese syntax */
2888 int ast_say_date_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2893 ast_localtime(&t
,&tm
,NULL
);
2894 localtime_r(&t
,&tm
);
2895 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2897 res
= wait_file(chan
, ints
, fn
, lang
);
2899 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
2901 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
2902 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2904 res
= wait_file(chan
, ints
, fn
, lang
);
2906 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
2908 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2913 static int say_date_with_format(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
2915 if (!strcasecmp(lang
, "en") ) { /* English syntax */
2916 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
2917 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
2918 return(ast_say_date_with_format_da(chan
, time
, ints
, lang
, format
, timezone
));
2919 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
2920 return(ast_say_date_with_format_de(chan
, time
, ints
, lang
, format
, timezone
));
2921 } else if (!strcasecmp(lang
, "es") || !strcasecmp(lang
, "mx")) { /* Spanish syntax */
2922 return(ast_say_date_with_format_es(chan
, time
, ints
, lang
, format
, timezone
));
2923 } else if (!strcasecmp(lang
, "he")) { /* Hebrew syntax */
2924 return(ast_say_date_with_format_he(chan
, time
, ints
, lang
, format
, timezone
));
2925 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
2926 return(ast_say_date_with_format_fr(chan
, time
, ints
, lang
, format
, timezone
));
2927 } else if (!strcasecmp(lang
, "it") ) { /* Italian syntax */
2928 return(ast_say_date_with_format_it(chan
, time
, ints
, lang
, format
, timezone
));
2929 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
2930 return(ast_say_date_with_format_nl(chan
, time
, ints
, lang
, format
, timezone
));
2931 } else if (!strcasecmp(lang
, "pl") ) { /* Polish syntax */
2932 return(ast_say_date_with_format_pl(chan
, time
, ints
, lang
, format
, timezone
));
2933 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
2934 return(ast_say_date_with_format_pt(chan
, time
, ints
, lang
, format
, timezone
));
2935 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
2936 return(ast_say_date_with_format_tw(chan
, time
, ints
, lang
, format
, timezone
));
2937 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
2938 return(ast_say_date_with_format_gr(chan
, time
, ints
, lang
, format
, timezone
));
2941 /* Default to English */
2942 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
2945 /* English syntax */
2946 int ast_say_date_with_format_en(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
2949 int res
=0, offset
, sndoffset
;
2950 char sndfile
[256], nextmsg
[256];
2953 format
= "ABdY 'digits/at' IMp";
2955 ast_localtime(&time
,&tm
,timezone
);
2957 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
2958 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
2959 switch (format
[offset
]) {
2960 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2962 /* Literal name of a sound file */
2964 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
2965 sndfile
[sndoffset
] = format
[offset
];
2966 sndfile
[sndoffset
] = '\0';
2967 res
= wait_file(chan
,ints
,sndfile
,lang
);
2971 /* Sunday - Saturday */
2972 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
2973 res
= wait_file(chan
,ints
,nextmsg
,lang
);
2978 /* January - December */
2979 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
2980 res
= wait_file(chan
,ints
,nextmsg
,lang
);
2983 /* Month enumerated */
2984 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, (char *) NULL
);
2988 /* First - Thirtyfirst */
2989 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
2993 if (tm
.tm_year
> 99) {
2994 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2995 } else if (tm
.tm_year
< 1) {
2996 /* I'm not going to handle 1900 and prior */
2997 /* We'll just be silent on the year, instead of bombing out. */
2999 res
= wait_file(chan
, ints
, "digits/19", lang
);
3001 if (tm
.tm_year
<= 9) {
3003 res
= wait_file(chan
,ints
, "digits/oh", lang
);
3006 res
|= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char *) NULL
);
3013 if (tm
.tm_hour
== 0)
3014 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3015 else if (tm
.tm_hour
> 12)
3016 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3018 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3019 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3024 if (format
[offset
] == 'H') {
3026 if (tm
.tm_hour
< 10) {
3027 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3031 if (tm
.tm_hour
== 0) {
3032 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3036 if (tm
.tm_hour
!= 0) {
3037 int remainder
= tm
.tm_hour
;
3038 if (tm
.tm_hour
> 20) {
3039 res
= wait_file(chan
,ints
, "digits/20",lang
);
3043 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
3044 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3052 if (tm
.tm_min
== 0) {
3053 if (format
[offset
] == 'M') {
3054 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
3056 res
= wait_file(chan
, ints
, "digits/hundred", lang
);
3058 } else if (tm
.tm_min
< 10) {
3059 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3061 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
3062 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3065 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
3071 if (tm
.tm_hour
> 11)
3072 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3074 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3075 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3078 /* Shorthand for "Today", "Yesterday", or ABdY */
3079 /* XXX As emphasized elsewhere, this should the native way in your
3080 * language to say the date, with changes in what you say, depending
3081 * upon how recent the date is. XXX */
3085 time_t beg_today
, tt
;
3087 gettimeofday(&now
,NULL
);
3089 ast_localtime(&tt
,&tmnow
,timezone
);
3090 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3091 /* In any case, it saves not having to do ast_mktime() */
3092 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3093 if (beg_today
< time
) {
3095 res
= wait_file(chan
,ints
, "digits/today",lang
);
3096 } else if (beg_today
- 86400 < time
) {
3098 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3099 } else if (beg_today
- 86400 * 6 < time
) {
3100 /* Within the last week */
3101 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3102 } else if (beg_today
- 2628000 < time
) {
3103 /* Less than a month ago - "Sunday, October third" */
3104 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3105 } else if (beg_today
- 15768000 < time
) {
3106 /* Less than 6 months ago - "August seventh" */
3107 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3109 /* More than 6 months ago - "April nineteenth two thousand three" */
3110 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3115 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3116 /* XXX As emphasized elsewhere, this should the native way in your
3117 * language to say the date, with changes in what you say, depending
3118 * upon how recent the date is. XXX */
3122 time_t beg_today
, tt
;
3124 gettimeofday(&now
,NULL
);
3126 ast_localtime(&tt
,&tmnow
,timezone
);
3127 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3128 /* In any case, it saves not having to do ast_mktime() */
3129 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3130 if (beg_today
< time
) {
3132 } else if ((beg_today
- 86400) < time
) {
3134 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3135 } else if (beg_today
- 86400 * 6 < time
) {
3136 /* Within the last week */
3137 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3138 } else if (beg_today
- 2628000 < time
) {
3139 /* Less than a month ago - "Sunday, October third" */
3140 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3141 } else if (beg_today
- 15768000 < time
) {
3142 /* Less than 6 months ago - "August seventh" */
3143 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3145 /* More than 6 months ago - "April nineteenth two thousand three" */
3146 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3151 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HM", timezone
);
3155 if (tm
.tm_sec
== 0) {
3156 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3157 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3158 } else if (tm
.tm_sec
< 10) {
3159 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3161 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3162 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3165 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, (char *) NULL
);
3169 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HMS", timezone
);
3173 /* Just ignore spaces and tabs */
3176 /* Unknown character */
3177 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3179 /* Jump out on DTMF */
3188 int ast_say_date_with_format_da(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
3191 int res
=0, offset
, sndoffset
;
3192 char sndfile
[256], nextmsg
[256];
3195 format
= "A dBY HMS";
3197 ast_localtime(&time
,&tm
,timezone
);
3199 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3200 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3201 switch (format
[offset
]) {
3202 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3204 /* Literal name of a sound file */
3206 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3207 sndfile
[sndoffset
] = format
[offset
];
3208 sndfile
[sndoffset
] = '\0';
3209 res
= wait_file(chan
,ints
,sndfile
,lang
);
3213 /* Sunday - Saturday */
3214 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3215 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3220 /* January - December */
3221 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3222 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3225 /* Month enumerated */
3226 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3230 /* First - Thirtyfirst */
3231 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3236 int year
= tm
.tm_year
+ 1900;
3237 if (year
> 1999) { /* year 2000 and later */
3238 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3241 /* I'm not going to handle 1100 and prior */
3242 /* We'll just be silent on the year, instead of bombing out. */
3244 /* year 1100 to 1999. will anybody need this?!? */
3245 /* say 1967 as 'nineteen hundred seven and sixty' */
3246 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3247 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3249 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3250 if (!res
&& year
% 100 != 0) {
3251 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3261 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3262 if (tm
.tm_hour
== 0)
3263 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3264 else if (tm
.tm_hour
> 12)
3265 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3267 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3269 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3273 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3274 if (tm
.tm_hour
< 10 && tm
.tm_hour
> 0) {
3275 res
= wait_file(chan
,ints
, "digits/0",lang
);
3280 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3284 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3285 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3287 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3288 if (tm
.tm_min
== 1) {
3289 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3291 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3298 if (tm
.tm_hour
> 11)
3299 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3301 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3302 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3305 /* Shorthand for "Today", "Yesterday", or AdBY */
3306 /* XXX As emphasized elsewhere, this should the native way in your
3307 * language to say the date, with changes in what you say, depending
3308 * upon how recent the date is. XXX */
3312 time_t beg_today
, tt
;
3314 gettimeofday(&now
,NULL
);
3316 ast_localtime(&tt
,&tmnow
,timezone
);
3317 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3318 /* In any case, it saves not having to do ast_mktime() */
3319 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3320 if (beg_today
< time
) {
3322 res
= wait_file(chan
,ints
, "digits/today",lang
);
3323 } else if (beg_today
- 86400 < time
) {
3325 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3327 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3332 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3333 /* XXX As emphasized elsewhere, this should the native way in your
3334 * language to say the date, with changes in what you say, depending
3335 * upon how recent the date is. XXX */
3339 time_t beg_today
, tt
;
3341 gettimeofday(&now
,NULL
);
3343 ast_localtime(&tt
,&tmnow
,timezone
);
3344 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3345 /* In any case, it saves not having to do ast_mktime() */
3346 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3347 if (beg_today
< time
) {
3349 } else if ((beg_today
- 86400) < time
) {
3351 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3352 } else if (beg_today
- 86400 * 6 < time
) {
3353 /* Within the last week */
3354 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "A", timezone
);
3356 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3361 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HM", timezone
);
3365 res
= wait_file(chan
,ints
, "digits/and",lang
);
3367 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3369 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3374 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HMS", timezone
);
3378 /* Just ignore spaces and tabs */
3381 /* Unknown character */
3382 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3384 /* Jump out on DTMF */
3393 int ast_say_date_with_format_de(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
3396 int res
=0, offset
, sndoffset
;
3397 char sndfile
[256], nextmsg
[256];
3400 format
= "A dBY HMS";
3402 ast_localtime(&time
,&tm
,timezone
);
3404 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3405 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3406 switch (format
[offset
]) {
3407 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3409 /* Literal name of a sound file */
3411 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3412 sndfile
[sndoffset
] = format
[offset
];
3413 sndfile
[sndoffset
] = '\0';
3414 res
= wait_file(chan
,ints
,sndfile
,lang
);
3418 /* Sunday - Saturday */
3419 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3420 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3425 /* January - December */
3426 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3427 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3430 /* Month enumerated */
3431 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3435 /* First - Thirtyfirst */
3436 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3441 int year
= tm
.tm_year
+ 1900;
3442 if (year
> 1999) { /* year 2000 and later */
3443 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3446 /* I'm not going to handle 1100 and prior */
3447 /* We'll just be silent on the year, instead of bombing out. */
3449 /* year 1100 to 1999. will anybody need this?!? */
3450 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3451 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3452 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3454 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3455 if (!res
&& year
% 100 != 0) {
3456 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3466 if (tm
.tm_hour
== 0)
3467 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3468 else if (tm
.tm_hour
> 12)
3469 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3471 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3472 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3474 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3480 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3482 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3487 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3488 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3490 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3491 if (tm
.tm_min
== 1) {
3492 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3494 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3501 if (tm
.tm_hour
> 11)
3502 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3504 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3505 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3508 /* Shorthand for "Today", "Yesterday", or AdBY */
3509 /* XXX As emphasized elsewhere, this should the native way in your
3510 * language to say the date, with changes in what you say, depending
3511 * upon how recent the date is. XXX */
3515 time_t beg_today
, tt
;
3517 gettimeofday(&now
,NULL
);
3519 ast_localtime(&tt
,&tmnow
,timezone
);
3520 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3521 /* In any case, it saves not having to do ast_mktime() */
3522 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3523 if (beg_today
< time
) {
3525 res
= wait_file(chan
,ints
, "digits/today",lang
);
3526 } else if (beg_today
- 86400 < time
) {
3528 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3530 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3535 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3536 /* XXX As emphasized elsewhere, this should the native way in your
3537 * language to say the date, with changes in what you say, depending
3538 * upon how recent the date is. XXX */
3542 time_t beg_today
, tt
;
3544 gettimeofday(&now
,NULL
);
3546 ast_localtime(&tt
,&tmnow
,timezone
);
3547 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3548 /* In any case, it saves not having to do ast_mktime() */
3549 beg_today
= now
.tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3550 if (beg_today
< time
) {
3552 } else if ((beg_today
- 86400) < time
) {
3554 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3555 } else if (beg_today
- 86400 * 6 < time
) {
3556 /* Within the last week */
3557 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "A", timezone
);
3559 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3564 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HM", timezone
);
3568 res
= wait_file(chan
,ints
, "digits/and",lang
);
3570 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3572 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3577 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HMS", timezone
);
3581 /* Just ignore spaces and tabs */
3584 /* Unknown character */
3585 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3587 /* Jump out on DTMF */
3595 /* TODO: this probably is not the correct format for doxygen remarks */
3597 /** ast_say_date_with_format_he Say formmated date in Hebrew
3599 * \ref ast_say_date_with_format_en for the details of the options
3601 * Changes from the English version:
3603 * * don't replicate in here the logic of ast_say_number_full_he
3605 * * year is always 4-digit (because it's simpler)
3607 * * added c, x, and X. Mainly for my tests
3609 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3612 * * A "ha" is missing in the standard date format, before the 'd'.
3613 * * The numbers of 3000--19000 are not handled well
3615 #define IL_DATE_STR "AdBY"
3616 #define IL_TIME_STR "IMp"
3617 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3618 int ast_say_date_with_format_he(struct ast_channel
*chan
, time_t time
,
3619 const char *ints
, const char *lang
, const char *format
,
3620 const char *timezone
)
3622 /* TODO: This whole function is cut&paste from
3623 * ast_say_date_with_format_en . Is that considered acceptable?
3626 int res
=0, offset
, sndoffset
;
3627 char sndfile
[256], nextmsg
[256];
3630 format
= IL_DATE_STR_FULL
;
3632 ast_localtime(&time
,&tm
,timezone
);
3634 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3635 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3636 switch (format
[offset
]) {
3637 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3639 /* Literal name of a sound file */
3641 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3642 sndfile
[sndoffset
] = format
[offset
];
3643 sndfile
[sndoffset
] = '\0';
3644 res
= wait_file(chan
,ints
,sndfile
,lang
);
3648 /* Sunday - Saturday */
3649 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3650 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3655 /* January - December */
3656 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3657 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3660 case 'e': /* Day of the month */
3661 /* I'm not sure exactly what the parameters
3662 * audiofd and ctrlfd to
3663 * ast_say_number_full_he mean, but it seems
3664 * safe to pass -1 there.
3666 * At least in one of the pathes :-(
3668 res
= ast_say_number_full_he(chan
, tm
.tm_mday
,
3669 ints
, lang
, "m", -1, -1
3672 case 'Y': /* Year */
3673 res
= ast_say_number_full_he(chan
, tm
.tm_year
+1900,
3674 ints
, lang
, "f", -1, -1
3678 case 'l': /* 12-Hour */
3680 int hour
= tm
.tm_hour
;
3682 if (hour
== 0) hour
=12;
3684 res
= ast_say_number_full_he(chan
, hour
,
3685 ints
, lang
, "f", -1, -1
3690 case 'k': /* 24-Hour */
3691 /* With 'H' there is an 'oh' after a single-
3693 if ((format
[offset
] == 'H') &&
3694 (tm
.tm_hour
<10)&&(tm
.tm_hour
>0)
3695 ) { /* e.g. oh-eight */
3696 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3699 res
= ast_say_number_full_he(chan
, tm
.tm_hour
,
3700 ints
, lang
, "f", -1, -1
3703 case 'M': /* Minute */
3704 res
= ast_say_number_full_he(chan
, tm
.tm_min
,
3705 ints
, lang
,"f", -1, -1
3711 if (tm
.tm_hour
> 11)
3712 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3714 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3715 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3718 /* Shorthand for "Today", "Yesterday", or "date" */
3720 /* Shorthand for "" (today), "Yesterday", A
3721 * (weekday), or "date" */
3722 /* XXX As emphasized elsewhere, this should the native way in your
3723 * language to say the date, with changes in what you say, depending
3724 * upon how recent the date is. XXX */
3728 time_t beg_today
, tt
;
3729 char todo
= format
[offset
]; /* The letter to format*/
3731 gettimeofday(&now
,NULL
);
3733 ast_localtime(&tt
,&tmnow
,timezone
);
3734 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3735 /* In any case, it saves not having to do ast_mktime() */
3736 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3737 if (beg_today
< time
) {
3740 res
= wait_file(chan
,
3745 } else if (beg_today
- 86400 < time
) {
3747 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3748 } else if ((todo
!= 'Q') &&
3749 (beg_today
- 86400 * 6 < time
))
3751 /* Within the last week */
3752 res
= ast_say_date_with_format_he(chan
,
3756 res
= ast_say_date_with_format_he(chan
,
3758 IL_DATE_STR
, timezone
);
3763 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HM", timezone
);
3765 case 'S': /* Seconds */
3766 res
= ast_say_number_full_he(chan
, tm
.tm_sec
,
3767 ints
, lang
, "f", -1, -1
3771 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HMS", timezone
);
3773 /* c, x, and X seem useful for testing. Not sure
3774 * if thiey're good for the general public */
3776 res
= ast_say_date_with_format_he(chan
, time
,
3777 ints
, lang
, IL_DATE_STR_FULL
, timezone
);
3780 res
= ast_say_date_with_format_he(chan
, time
,
3781 ints
, lang
, IL_DATE_STR
, timezone
);
3783 case 'X': /* Currently not locale-dependent...*/
3784 res
= ast_say_date_with_format_he(chan
, time
,
3785 ints
, lang
, IL_TIME_STR
, timezone
);
3789 /* Just ignore spaces and tabs */
3792 /* Unknown character */
3793 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3795 /* Jump out on DTMF */
3804 /* Spanish syntax */
3805 int ast_say_date_with_format_es(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
3808 int res
=0, offset
, sndoffset
;
3809 char sndfile
[256], nextmsg
[256];
3812 format
= "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
3814 ast_localtime(&time
,&tm
,timezone
);
3816 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3817 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3818 switch (format
[offset
]) {
3819 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3821 /* Literal name of a sound file */
3823 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3824 sndfile
[sndoffset
] = format
[offset
];
3825 sndfile
[sndoffset
] = '\0';
3826 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
3827 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3831 /* Sunday - Saturday */
3832 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3833 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3838 /* January - December */
3839 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3840 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3843 /* First - Twelfth */
3844 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
3845 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3849 /* First - Thirtyfirst */
3850 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
3854 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
3859 if (tm
.tm_hour
== 0)
3860 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3861 else if (tm
.tm_hour
> 12)
3862 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3864 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3865 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3870 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, NULL
);
3874 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
3879 if (tm
.tm_hour
> 12)
3880 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
3881 else if (tm
.tm_hour
&& tm
.tm_hour
< 12)
3882 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
3885 /* Shorthand for "Today", "Yesterday", or ABdY */
3886 /* XXX As emphasized elsewhere, this should the native way in your
3887 * language to say the date, with changes in what you say, depending
3888 * upon how recent the date is. XXX */
3892 time_t beg_today
, tt
;
3894 gettimeofday(&now
,NULL
);
3896 ast_localtime(&tt
,&tmnow
,timezone
);
3897 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3898 /* In any case, it saves not having to do ast_mktime() */
3899 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3900 if (beg_today
< time
) {
3902 res
= wait_file(chan
,ints
, "digits/today",lang
);
3903 } else if (beg_today
- 86400 < time
) {
3905 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3907 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
3912 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3913 /* XXX As emphasized elsewhere, this should the native way in your
3914 * language to say the date, with changes in what you say, depending
3915 * upon how recent the date is. XXX */
3919 time_t beg_today
, tt
;
3921 gettimeofday(&now
,NULL
);
3923 ast_localtime(&tt
,&tmnow
,timezone
);
3924 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3925 /* In any case, it saves not having to do ast_mktime() */
3926 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3927 if (beg_today
< time
) {
3929 res
= wait_file(chan
,ints
, "digits/today",lang
);
3930 } else if ((beg_today
- 86400) < time
) {
3932 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3933 } else if (beg_today
- 86400 * 6 < time
) {
3934 /* Within the last week */
3935 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "A", timezone
);
3937 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
3942 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "H 'digits/y' M", timezone
);
3946 if (tm
.tm_sec
== 0) {
3947 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3948 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3949 } else if (tm
.tm_sec
< 10) {
3950 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3952 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3953 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3955 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
3956 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3957 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3960 ten
= (tm
.tm_sec
/ 10) * 10;
3961 one
= (tm
.tm_sec
% 10);
3962 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
3963 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3965 /* Fifty, not fifty-zero */
3967 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
3968 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3974 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "HMS", timezone
);
3978 /* Just ignore spaces and tabs */
3981 /* Unknown character */
3982 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3984 /* Jump out on DTMF */
3995 int ast_say_date_with_format_fr(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
3998 int res
=0, offset
, sndoffset
;
3999 char sndfile
[256], nextmsg
[256];
4002 format
= "AdBY 'digits/at' IMp";
4004 ast_localtime(&time
,&tm
,timezone
);
4006 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4007 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4008 switch (format
[offset
]) {
4009 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4011 /* Literal name of a sound file */
4013 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4014 sndfile
[sndoffset
] = format
[offset
];
4015 sndfile
[sndoffset
] = '\0';
4016 res
= wait_file(chan
,ints
,sndfile
,lang
);
4020 /* Sunday - Saturday */
4021 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4022 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4027 /* January - December */
4028 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4029 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4032 /* First - Twelfth */
4033 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4034 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4039 if (tm
.tm_mday
== 1) {
4040 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4041 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4043 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
4048 if (tm
.tm_year
> 99) {
4049 res
= wait_file(chan
,ints
, "digits/2",lang
);
4051 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4053 if (tm
.tm_year
> 100) {
4055 res
= ast_say_number(chan
, tm
.tm_year
- 100, ints
, lang
, (char * ) NULL
);
4059 if (tm
.tm_year
< 1) {
4060 /* I'm not going to handle 1900 and prior */
4061 /* We'll just be silent on the year, instead of bombing out. */
4063 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4065 wait_file(chan
,ints
, "digits/9",lang
);
4066 wait_file(chan
,ints
, "digits/hundred",lang
);
4067 res
= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char * ) NULL
);
4075 if (tm
.tm_hour
== 0)
4076 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4077 else if (tm
.tm_hour
> 12)
4078 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4080 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4081 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4083 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4088 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char * ) NULL
);
4090 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4094 if (tm
.tm_min
== 0) {
4097 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char * ) NULL
);
4102 if (tm
.tm_hour
> 11)
4103 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4105 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4106 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4109 /* Shorthand for "Today", "Yesterday", or AdBY */
4110 /* XXX As emphasized elsewhere, this should the native way in your
4111 * language to say the date, with changes in what you say, depending
4112 * upon how recent the date is. XXX */
4116 time_t beg_today
, tt
;
4118 gettimeofday(&now
,NULL
);
4120 ast_localtime(&tt
,&tmnow
,timezone
);
4121 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4122 /* In any case, it saves not having to do ast_mktime() */
4123 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4124 if (beg_today
< time
) {
4126 res
= wait_file(chan
,ints
, "digits/today",lang
);
4127 } else if (beg_today
- 86400 < time
) {
4129 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4131 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4136 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4137 /* XXX As emphasized elsewhere, this should the native way in your
4138 * language to say the date, with changes in what you say, depending
4139 * upon how recent the date is. XXX */
4143 time_t beg_today
, tt
;
4145 gettimeofday(&now
,NULL
);
4147 ast_localtime(&tt
,&tmnow
,timezone
);
4148 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4149 /* In any case, it saves not having to do ast_mktime() */
4150 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4151 if (beg_today
< time
) {
4153 } else if ((beg_today
- 86400) < time
) {
4155 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4156 } else if (beg_today
- 86400 * 6 < time
) {
4157 /* Within the last week */
4158 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "A", timezone
);
4160 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4165 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HM", timezone
);
4169 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char * ) NULL
);
4171 res
= wait_file(chan
,ints
, "digits/second",lang
);
4175 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HMS", timezone
);
4179 /* Just ignore spaces and tabs */
4182 /* Unknown character */
4183 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4185 /* Jump out on DTMF */
4193 int ast_say_date_with_format_it(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
4196 int res
=0, offset
, sndoffset
;
4197 char sndfile
[256], nextmsg
[256];
4200 format
= "AdB 'digits/at' IMp";
4202 ast_localtime(&time
,&tm
,timezone
);
4204 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4205 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4206 switch (format
[offset
]) {
4207 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4209 /* Literal name of a sound file */
4211 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4212 sndfile
[sndoffset
] = format
[offset
];
4213 sndfile
[sndoffset
] = '\0';
4214 res
= wait_file(chan
,ints
,sndfile
,lang
);
4218 /* Sunday - Saturday */
4219 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4220 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4225 /* January - December */
4226 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4227 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4230 /* First - Twelfth */
4231 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4232 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4236 /* First day of the month is spelled as ordinal */
4237 if (tm
.tm_mday
== 1) {
4238 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4239 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4242 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4248 if (tm
.tm_year
> 99) {
4249 res
= wait_file(chan
,ints
, "digits/ore-2000",lang
);
4250 if (tm
.tm_year
> 100) {
4252 /* This works until the end of 2021 */
4253 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4254 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4258 if (tm
.tm_year
< 1) {
4259 /* I'm not going to handle 1900 and prior */
4260 /* We'll just be silent on the year, instead of bombing out. */
4262 res
= wait_file(chan
,ints
, "digits/ore-1900",lang
);
4263 if ((!res
) && (tm
.tm_year
!= 0)) {
4264 if (tm
.tm_year
<= 21) {
4266 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4267 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4269 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4271 ten
= tm
.tm_year
/ 10;
4272 one
= tm
.tm_year
% 10;
4273 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4274 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4277 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4278 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4289 if (tm
.tm_hour
== 0)
4290 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4291 else if (tm
.tm_hour
> 12)
4292 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4294 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4295 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4300 if (tm
.tm_hour
== 0) {
4301 res
= wait_file(chan
,ints
, "digits/ore-mezzanotte",lang
);
4302 } else if (tm
.tm_hour
== 1) {
4303 res
= wait_file(chan
,ints
, "digits/ore-una",lang
);
4305 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4310 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4315 if (tm
.tm_hour
> 11)
4316 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4318 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4319 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4322 /* Shorthand for "Today", "Yesterday", or ABdY */
4323 /* XXX As emphasized elsewhere, this should the native way in your
4324 * language to say the date, with changes in what you say, depending
4325 * upon how recent the date is. XXX */
4329 time_t beg_today
, tt
;
4331 gettimeofday(&now
,NULL
);
4333 ast_localtime(&tt
,&tmnow
,timezone
);
4334 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4335 /* In any case, it saves not having to do ast_mktime() */
4336 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4337 if (beg_today
< time
) {
4339 res
= wait_file(chan
,ints
, "digits/today",lang
);
4340 } else if (beg_today
- 86400 < time
) {
4342 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4344 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4349 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4353 time_t beg_today
, tt
;
4355 gettimeofday(&now
,NULL
);
4357 ast_localtime(&tt
,&tmnow
,timezone
);
4358 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4359 /* In any case, it saves not having to do ast_mktime() */
4360 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4361 if (beg_today
< time
) {
4363 } else if ((beg_today
- 86400) < time
) {
4365 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4366 } else if (beg_today
- 86400 * 6 < time
) {
4367 /* Within the last week */
4368 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "A", timezone
);
4370 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4375 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HM", timezone
);
4379 if (tm
.tm_sec
== 0) {
4380 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4381 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4382 } else if (tm
.tm_sec
< 10) {
4383 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4385 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4386 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4388 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
4389 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4390 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4393 ten
= (tm
.tm_sec
/ 10) * 10;
4394 one
= (tm
.tm_sec
% 10);
4395 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
4396 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4398 /* Fifty, not fifty-zero */
4400 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4401 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4407 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HMS", timezone
);
4411 /* Just ignore spaces and tabs */
4414 /* Unknown character */
4415 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4417 /* Jump out on DTMF */
4426 int ast_say_date_with_format_nl(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
4429 int res
=0, offset
, sndoffset
;
4430 char sndfile
[256], nextmsg
[256];
4433 format
= "ABdY 'digits/at' IMp";
4435 ast_localtime(&time
,&tm
,timezone
);
4437 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4438 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4439 switch (format
[offset
]) {
4440 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4442 /* Literal name of a sound file */
4444 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4445 sndfile
[sndoffset
] = format
[offset
];
4446 sndfile
[sndoffset
] = '\0';
4447 res
= wait_file(chan
,ints
,sndfile
,lang
);
4451 /* Sunday - Saturday */
4452 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4453 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4458 /* January - December */
4459 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4460 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4463 /* First - Twelfth */
4464 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4465 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4469 /* First - Thirtyfirst */
4470 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, NULL
);
4474 if (tm
.tm_year
> 99) {
4475 res
= wait_file(chan
,ints
, "digits/2",lang
);
4477 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4479 if (tm
.tm_year
> 100) {
4481 /* This works until the end of 2020 */
4482 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4483 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4487 if (tm
.tm_year
< 1) {
4488 /* I'm not going to handle 1900 and prior */
4489 /* We'll just be silent on the year, instead of bombing out. */
4491 res
= wait_file(chan
,ints
, "digits/19",lang
);
4493 if (tm
.tm_year
<= 9) {
4495 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4497 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4498 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4500 } else if (tm
.tm_year
<= 20) {
4502 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4503 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4507 ten
= tm
.tm_year
/ 10;
4508 one
= tm
.tm_year
% 10;
4509 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4510 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4513 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4514 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4525 if (tm
.tm_hour
== 0)
4526 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4527 else if (tm
.tm_hour
> 12)
4528 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4530 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4531 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4536 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4538 res
= wait_file(chan
,ints
, "digits/nl-uur",lang
);
4543 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4548 if (tm
.tm_hour
> 11)
4549 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4551 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4552 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4555 /* Shorthand for "Today", "Yesterday", or ABdY */
4556 /* XXX As emphasized elsewhere, this should the native way in your
4557 * language to say the date, with changes in what you say, depending
4558 * upon how recent the date is. XXX */
4562 time_t beg_today
, tt
;
4564 gettimeofday(&now
,NULL
);
4566 ast_localtime(&tt
,&tmnow
,timezone
);
4567 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4568 /* In any case, it saves not having to do ast_mktime() */
4569 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4570 if (beg_today
< time
) {
4572 res
= wait_file(chan
,ints
, "digits/today",lang
);
4573 } else if (beg_today
- 86400 < time
) {
4575 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4577 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4582 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4586 time_t beg_today
, tt
;
4588 gettimeofday(&now
,NULL
);
4590 ast_localtime(&tt
,&tmnow
,timezone
);
4591 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4592 /* In any case, it saves not having to do ast_mktime() */
4593 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4594 if (beg_today
< time
) {
4596 } else if ((beg_today
- 86400) < time
) {
4598 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4599 } else if (beg_today
- 86400 * 6 < time
) {
4600 /* Within the last week */
4601 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "A", timezone
);
4603 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4608 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HM", timezone
);
4612 if (tm
.tm_sec
== 0) {
4613 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4614 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4615 } else if (tm
.tm_sec
< 10) {
4616 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4618 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4619 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4621 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
4622 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4623 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4626 ten
= (tm
.tm_sec
/ 10) * 10;
4627 one
= (tm
.tm_sec
% 10);
4628 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
4629 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4631 /* Fifty, not fifty-zero */
4633 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4634 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4640 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HMS", timezone
);
4644 /* Just ignore spaces and tabs */
4647 /* Unknown character */
4648 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4650 /* Jump out on DTMF */
4659 int ast_say_date_with_format_pl(struct ast_channel
*chan
, time_t thetime
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
4662 int res
=0, offset
, sndoffset
;
4663 char sndfile
[256], nextmsg
[256];
4665 ast_localtime(&thetime
, &tm
, timezone
);
4667 for (offset
= 0 ; format
[offset
] != '\0' ; offset
++) {
4669 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4670 switch (format
[offset
]) {
4671 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4673 /* Literal name of a sound file */
4675 for (sndoffset
= 0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4676 sndfile
[sndoffset
] = format
[offset
];
4677 sndfile
[sndoffset
] = '\0';
4678 res
= wait_file(chan
, ints
, sndfile
, lang
);
4682 /* Sunday - Saturday */
4683 snprintf(nextmsg
, sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4684 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4689 /* January - December */
4690 snprintf(nextmsg
, sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4691 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4694 /* Month enumerated */
4695 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, NULL
);
4699 /* First - Thirtyfirst */
4700 remainder
= tm
.tm_mday
;
4701 if (tm
.tm_mday
> 20) {
4702 res
= wait_file(chan
, ints
, "digits/h-20", lang
);
4706 snprintf(nextmsg
, sizeof(nextmsg
), "digits/h-%d", remainder
);
4707 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4712 if (tm
.tm_year
> 100) {
4713 res
= wait_file(chan
, ints
, "digits/2", lang
);
4715 res
= wait_file(chan
, ints
, "digits/1000.2",lang
);
4716 if (tm
.tm_year
> 100) {
4718 res
= ast_say_enumeration(chan
, tm
.tm_year
- 100, ints
, lang
, NULL
);
4720 } else if (tm
.tm_year
== 100) {
4721 res
= wait_file(chan
, ints
, "digits/h-2000", lang
);
4723 if (tm
.tm_year
< 1) {
4724 /* I'm not going to handle 1900 and prior */
4725 /* We'll just be silent on the year, instead of bombing out. */
4728 res
= wait_file(chan
, ints
, "digits/1000", lang
);
4730 wait_file(chan
, ints
, "digits/900", lang
);
4731 res
= ast_say_enumeration(chan
, tm
.tm_year
, ints
, lang
, NULL
);
4736 wait_file(chan
, ints
, "digits/year", lang
);
4741 if (tm
.tm_hour
== 0)
4742 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-12");
4743 else if (tm
.tm_hour
> 12)
4744 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
- 12);
4746 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4748 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4753 if (tm
.tm_hour
!= 0) {
4754 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4755 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4757 res
= wait_file(chan
, ints
, "digits/t-24", lang
);
4762 if (tm
.tm_min
== 0) {
4763 if (format
[offset
] == 'M') {
4764 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
4766 res
= wait_file(chan
, ints
, "digits/100", lang
);
4769 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4774 if (tm
.tm_hour
> 11)
4775 snprintf(nextmsg
, sizeof(nextmsg
), "digits/p-m");
4777 snprintf(nextmsg
, sizeof(nextmsg
), "digits/a-m");
4778 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4781 /* Shorthand for "Today", "Yesterday", or AdBY */
4783 time_t tv_sec
= time(NULL
);
4787 ast_localtime(&tv_sec
,&tmnow
, timezone
);
4788 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4789 /* In any case, it saves not having to do ast_mktime() */
4790 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4791 if (beg_today
< thetime
) {
4793 res
= wait_file(chan
, ints
, "digits/today", lang
);
4794 } else if (beg_today
- 86400 < thetime
) {
4796 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4798 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4803 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4805 time_t tv_sec
= time(NULL
);
4809 ast_localtime(&tv_sec
, &tmnow
, timezone
);
4810 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4811 /* In any case, it saves not having to do ast_mktime() */
4812 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4813 if (beg_today
< thetime
) {
4815 } else if ((beg_today
- 86400) < thetime
) {
4817 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4818 } else if (beg_today
- 86400 * 6 < thetime
) {
4819 /* Within the last week */
4820 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "A", timezone
);
4822 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4827 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HM", timezone
);
4831 res
= wait_file(chan
, ints
, "digits/and", lang
);
4833 if (tm
.tm_sec
== 1) {
4834 res
= wait_file(chan
, ints
, "digits/1z", lang
);
4836 res
= wait_file(chan
, ints
, "digits/second-a", lang
);
4838 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4841 ten
= tm
.tm_sec
/ 10;
4842 one
= tm
.tm_sec
% 10;
4844 if (one
> 1 && one
< 5 && ten
!= 1)
4845 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
4847 res
= wait_file(chan
,ints
, "digits/second",lang
);
4853 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HMS", timezone
);
4857 /* Just ignore spaces and tabs */
4860 /* Unknown character */
4861 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4863 /* Jump out on DTMF */
4870 /* Portuguese syntax */
4871 int ast_say_date_with_format_pt(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
4874 int res
=0, offset
, sndoffset
;
4875 char sndfile
[256], nextmsg
[256];
4878 format
= "Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/at' IMp";
4880 ast_localtime(&time
,&tm
,timezone
);
4882 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4883 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4884 switch (format
[offset
]) {
4885 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4887 /* Literal name of a sound file */
4889 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4890 sndfile
[sndoffset
] = format
[offset
];
4891 sndfile
[sndoffset
] = '\0';
4892 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
4893 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4897 /* Sunday - Saturday */
4898 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4899 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4904 /* January - December */
4905 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4906 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4909 /* First - Twelfth */
4910 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4911 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4915 /* First - Thirtyfirst */
4916 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4920 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
4925 if (tm
.tm_hour
== 0) {
4926 if (format
[offset
] == 'I')
4927 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
4929 res
= wait_file(chan
, ints
, "digits/pt-meianoite", lang
);
4931 else if (tm
.tm_hour
== 12) {
4932 if (format
[offset
] == 'I')
4933 res
= wait_file(chan
, ints
, "digits/pt-ao", lang
);
4935 res
= wait_file(chan
, ints
, "digits/pt-meiodia", lang
);
4938 if (format
[offset
] == 'I') {
4939 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
4940 if ((tm
.tm_hour
% 12) != 1)
4942 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
4945 res
= ast_say_number(chan
, (tm
.tm_hour
% 12), ints
, lang
, "f");
4951 res
= ast_say_number(chan
, -tm
.tm_hour
, ints
, lang
, NULL
);
4953 if (tm
.tm_hour
!= 0) {
4954 int remainder
= tm
.tm_hour
;
4955 if (tm
.tm_hour
> 20) {
4956 res
= wait_file(chan
,ints
, "digits/20",lang
);
4960 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
4961 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4968 if (tm
.tm_min
== 0) {
4969 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
4970 if (tm
.tm_hour
!= 1)
4972 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
); } else {
4973 res
= wait_file(chan
,ints
,"digits/pt-e",lang
);
4975 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4981 if (tm
.tm_hour
> 12)
4982 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
4983 else if (tm
.tm_hour
&& tm
.tm_hour
< 12)
4984 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
4987 /* Shorthand for "Today", "Yesterday", or ABdY */
4988 /* XXX As emphasized elsewhere, this should the native way in your
4989 * language to say the date, with changes in what you say, depending
4990 * upon how recent the date is. XXX */
4994 time_t beg_today
, tt
;
4996 gettimeofday(&now
,NULL
);
4998 ast_localtime(&tt
,&tmnow
,timezone
);
4999 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5000 /* In any case, it saves not having to do ast_mktime() */
5001 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5002 if (beg_today
< time
) {
5004 res
= wait_file(chan
,ints
, "digits/today",lang
);
5005 } else if (beg_today
- 86400 < time
) {
5007 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5009 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5014 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5015 /* XXX As emphasized elsewhere, this should the native way in your
5016 * language to say the date, with changes in what you say, depending
5017 * upon how recent the date is. XXX */
5021 time_t beg_today
, tt
;
5023 gettimeofday(&now
,NULL
);
5025 ast_localtime(&tt
,&tmnow
,timezone
);
5026 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5027 /* In any case, it saves not having to do ast_mktime() */
5028 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5029 if (beg_today
< time
) {
5031 } else if ((beg_today
- 86400) < time
) {
5033 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5034 } else if (beg_today
- 86400 * 6 < time
) {
5035 /* Within the last week */
5036 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "A", timezone
);
5038 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5043 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "H 'digits/pt-e' M", timezone
);
5047 if (tm
.tm_sec
== 0) {
5048 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5049 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5050 } else if (tm
.tm_sec
< 10) {
5051 res
= wait_file(chan
,ints
, "digits/oh",lang
);
5053 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5054 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5056 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
5057 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5058 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5061 ten
= (tm
.tm_sec
/ 10) * 10;
5062 one
= (tm
.tm_sec
% 10);
5063 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
5064 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5066 /* Fifty, not fifty-zero */
5068 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
5069 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5075 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "HMS", timezone
);
5079 /* Just ignore spaces and tabs */
5082 /* Unknown character */
5083 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5085 /* Jump out on DTMF */
5093 /* Taiwanese / Chinese syntax */
5094 int ast_say_date_with_format_tw(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
5097 int res
=0, offset
, sndoffset
;
5098 char sndfile
[256], nextmsg
[256];
5101 format
= "YBdA 'digits/at' HM";
5103 ast_localtime(&time
,&tm
,timezone
);
5105 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
5106 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
5107 switch (format
[offset
]) {
5108 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5110 /* Literal name of a sound file */
5112 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
5113 sndfile
[sndoffset
] = format
[offset
];
5114 sndfile
[sndoffset
] = '\0';
5115 res
= wait_file(chan
,ints
,sndfile
,lang
);
5119 /* Sunday - Saturday */
5120 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
5121 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5126 /* January - December */
5127 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
5128 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5131 /* First - Twelfth */
5132 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
5133 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5137 /* First - Thirtyfirst */
5138 if (!(tm
.tm_mday
% 10) || (tm
.tm_mday
< 10)) {
5139 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
5140 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5142 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%dh", tm
.tm_mday
- (tm
.tm_mday
% 10));
5143 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5145 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
% 10);
5146 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5152 if (tm
.tm_year
> 99) {
5153 res
= wait_file(chan
,ints
, "digits/2",lang
);
5155 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
5157 if (tm
.tm_year
> 100) {
5159 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) / 10);
5160 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5162 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) % 10);
5163 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5168 res
= wait_file(chan
,ints
, "digits/year",lang
);
5171 if (tm
.tm_year
< 1) {
5172 /* I'm not going to handle 1900 and prior */
5173 /* We'll just be silent on the year, instead of bombing out. */
5175 res
= wait_file(chan
,ints
, "digits/1",lang
);
5177 res
= wait_file(chan
,ints
, "digits/9",lang
);
5180 if (tm
.tm_year
<= 9) {
5182 res
= wait_file(chan
,ints
, "digits/0",lang
);
5184 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
5185 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5189 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
/ 10);
5190 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5192 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
% 10);
5193 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5199 res
= wait_file(chan
,ints
, "digits/year",lang
);
5206 if (tm
.tm_hour
== 0)
5207 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
5208 else if (tm
.tm_hour
> 12)
5209 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
5211 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5212 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5214 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5220 if (!(tm
.tm_hour
% 10) || tm
.tm_hour
< 10) {
5221 if (tm
.tm_hour
< 10) {
5222 res
= wait_file(chan
, ints
, "digits/0", lang
);
5224 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5225 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5227 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- (tm
.tm_hour
% 10));
5228 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5230 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
% 10);
5231 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5235 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5240 if (!(tm
.tm_min
% 10) || tm
.tm_min
< 10) {
5241 if (tm
.tm_min
< 10) {
5242 res
= wait_file(chan
, ints
, "digits/0", lang
);
5244 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
5245 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5247 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
- (tm
.tm_min
% 10));
5248 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5250 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
% 10);
5251 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5255 res
= wait_file(chan
,ints
, "digits/minute",lang
);
5261 if (tm
.tm_hour
> 11)
5262 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
5264 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
5265 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5268 /* Shorthand for "Today", "Yesterday", or ABdY */
5269 /* XXX As emphasized elsewhere, this should the native way in your
5270 * language to say the date, with changes in what you say, depending
5271 * upon how recent the date is. XXX */
5275 time_t beg_today
, tt
;
5277 gettimeofday(&now
,NULL
);
5279 ast_localtime(&tt
,&tmnow
,timezone
);
5280 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5281 /* In any case, it saves not having to do ast_mktime() */
5282 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5283 if (beg_today
< time
) {
5285 res
= wait_file(chan
,ints
, "digits/today",lang
);
5286 } else if (beg_today
- 86400 < time
) {
5288 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5290 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5295 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5296 /* XXX As emphasized elsewhere, this should the native way in your
5297 * language to say the date, with changes in what you say, depending
5298 * upon how recent the date is. XXX */
5302 time_t beg_today
, tt
;
5304 gettimeofday(&now
,NULL
);
5306 ast_localtime(&tt
,&tmnow
,timezone
);
5307 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5308 /* In any case, it saves not having to do ast_mktime() */
5309 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5310 if (beg_today
< time
) {
5312 } else if ((beg_today
- 86400) < time
) {
5314 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5315 } else if (beg_today
- 86400 * 6 < time
) {
5316 /* Within the last week */
5317 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "A", timezone
);
5319 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5324 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "HM", timezone
);
5328 if (!(tm
.tm_sec
% 10) || tm
.tm_sec
< 10) {
5329 if (tm
.tm_sec
< 10) {
5330 res
= wait_file(chan
, ints
, "digits/0", lang
);
5332 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5333 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5335 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
- (tm
.tm_sec
% 10));
5336 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5338 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
% 10);
5339 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5343 res
= wait_file(chan
,ints
, "digits/second",lang
);
5347 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "HMS", timezone
);
5351 /* Just ignore spaces and tabs */
5354 /* Unknown character */
5355 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5357 /* Jump out on DTMF */
5365 static int say_time(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5367 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5368 return(ast_say_time_en(chan
, t
, ints
, lang
));
5369 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5370 return(ast_say_time_de(chan
, t
, ints
, lang
));
5371 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5372 return(ast_say_time_fr(chan
, t
, ints
, lang
));
5373 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5374 return(ast_say_time_nl(chan
, t
, ints
, lang
));
5375 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5376 return(ast_say_time_pt(chan
, t
, ints
, lang
));
5377 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5378 return(ast_say_time_tw(chan
, t
, ints
, lang
));
5379 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5380 return(ast_say_time_gr(chan
, t
, ints
, lang
));
5383 /* Default to English */
5384 return(ast_say_time_en(chan
, t
, ints
, lang
));
5387 /* English syntax */
5388 int ast_say_time_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5393 localtime_r(&t
,&tm
);
5397 else if (hour
== 12)
5399 else if (hour
> 12) {
5404 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5406 if (tm
.tm_min
> 9) {
5408 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5409 } else if (tm
.tm_min
) {
5411 res
= ast_streamfile(chan
, "digits/oh", lang
);
5413 res
= ast_waitstream(chan
, ints
);
5415 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5418 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5420 res
= ast_waitstream(chan
, ints
);
5424 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5427 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5430 res
= ast_waitstream(chan
, ints
);
5435 int ast_say_time_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5439 localtime_r(&t
,&tm
);
5441 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "n");
5443 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5445 res
= ast_waitstream(chan
, ints
);
5448 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
5453 int ast_say_time_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5457 localtime_r(&t
,&tm
);
5459 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5461 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5464 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5470 int ast_say_time_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5474 localtime_r(&t
,&tm
);
5476 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
5478 res
= ast_streamfile(chan
, "digits/nl-uur", lang
);
5480 res
= ast_waitstream(chan
, ints
);
5483 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, NULL
);
5487 /* Portuguese syntax */
5488 int ast_say_time_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5493 localtime_r(&t
,&tm
);
5496 res
= ast_say_number(chan
, hour
, ints
, lang
, "f");
5499 res
= wait_file(chan
, ints
, "digits/pt-e", lang
);
5501 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5504 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
5505 if (tm
.tm_hour
!= 1)
5507 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5510 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5514 /* Taiwanese / Chinese syntax */
5515 int ast_say_time_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5520 localtime_r(&t
,&tm
);
5524 else if (hour
== 12)
5526 else if (hour
> 12) {
5532 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5535 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5538 res
= ast_waitstream(chan
, ints
);
5540 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5542 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5544 res
= ast_waitstream(chan
, ints
);
5546 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5548 res
= ast_streamfile(chan
, "digits/minute", lang
);
5550 res
= ast_waitstream(chan
, ints
);
5554 static int say_datetime(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5556 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5557 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5558 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5559 return(ast_say_datetime_de(chan
, t
, ints
, lang
));
5560 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5561 return(ast_say_datetime_fr(chan
, t
, ints
, lang
));
5562 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5563 return(ast_say_datetime_nl(chan
, t
, ints
, lang
));
5564 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5565 return(ast_say_datetime_pt(chan
, t
, ints
, lang
));
5566 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5567 return(ast_say_datetime_tw(chan
, t
, ints
, lang
));
5568 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5569 return(ast_say_datetime_gr(chan
, t
, ints
, lang
));
5572 /* Default to English */
5573 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5576 /* English syntax */
5577 int ast_say_datetime_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5583 localtime_r(&t
,&tm
);
5585 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5586 res
= ast_streamfile(chan
, fn
, lang
);
5588 res
= ast_waitstream(chan
, ints
);
5591 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5592 res
= ast_streamfile(chan
, fn
, lang
);
5594 res
= ast_waitstream(chan
, ints
);
5597 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5602 else if (hour
== 12)
5604 else if (hour
> 12) {
5609 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5611 if (tm
.tm_min
> 9) {
5613 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5614 } else if (tm
.tm_min
) {
5616 res
= ast_streamfile(chan
, "digits/oh", lang
);
5618 res
= ast_waitstream(chan
, ints
);
5620 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5623 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5625 res
= ast_waitstream(chan
, ints
);
5629 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5632 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5635 res
= ast_waitstream(chan
, ints
);
5637 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5642 int ast_say_datetime_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5646 localtime_r(&t
,&tm
);
5647 res
= ast_say_date(chan
, t
, ints
, lang
);
5649 ast_say_time(chan
, t
, ints
, lang
);
5655 int ast_say_datetime_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5660 localtime_r(&t
,&tm
);
5663 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5666 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5667 res
= ast_streamfile(chan
, fn
, lang
);
5669 res
= ast_waitstream(chan
, ints
);
5672 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5673 res
= ast_streamfile(chan
, fn
, lang
);
5675 res
= ast_waitstream(chan
, ints
);
5679 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5681 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5682 if (tm
.tm_min
> 0) {
5684 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5687 res
= ast_waitstream(chan
, ints
);
5689 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5694 int ast_say_datetime_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5698 localtime_r(&t
,&tm
);
5699 res
= ast_say_date(chan
, t
, ints
, lang
);
5701 res
= ast_streamfile(chan
, "digits/nl-om", lang
);
5703 res
= ast_waitstream(chan
, ints
);
5706 ast_say_time(chan
, t
, ints
, lang
);
5710 /* Portuguese syntax */
5711 int ast_say_datetime_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5717 localtime_r(&t
,&tm
);
5719 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5720 res
= ast_streamfile(chan
, fn
, lang
);
5722 res
= ast_waitstream(chan
, ints
);
5725 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5726 res
= ast_streamfile(chan
, fn
, lang
);
5728 res
= ast_waitstream(chan
, ints
);
5731 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5736 else if (hour
== 12)
5738 else if (hour
> 12) {
5743 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5745 if (tm
.tm_min
> 9) {
5747 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5748 } else if (tm
.tm_min
) {
5750 res
= ast_streamfile(chan
, "digits/oh", lang
);
5752 res
= ast_waitstream(chan
, ints
);
5754 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5757 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5759 res
= ast_waitstream(chan
, ints
);
5763 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5766 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5769 res
= ast_waitstream(chan
, ints
);
5771 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5775 /* Taiwanese / Chinese syntax */
5776 int ast_say_datetime_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5782 localtime_r(&t
,&tm
);
5784 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5786 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5787 res
= ast_streamfile(chan
, fn
, lang
);
5789 res
= ast_waitstream(chan
, ints
);
5792 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5794 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5795 res
= ast_streamfile(chan
, fn
, lang
);
5797 res
= ast_waitstream(chan
, ints
);
5803 else if (hour
== 12)
5805 else if (hour
> 12) {
5811 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5814 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5817 res
= ast_waitstream(chan
, ints
);
5819 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5821 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5823 res
= ast_waitstream(chan
, ints
);
5825 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5827 res
= ast_streamfile(chan
, "digits/minute", lang
);
5829 res
= ast_waitstream(chan
, ints
);
5833 static int say_datetime_from_now(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5835 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5836 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
5837 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5838 return(ast_say_datetime_from_now_fr(chan
, t
, ints
, lang
));
5839 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5840 return(ast_say_datetime_from_now_pt(chan
, t
, ints
, lang
));
5843 /* Default to English */
5844 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
5847 /* English syntax */
5848 int ast_say_datetime_from_now_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5859 localtime_r(&t
,&tm
);
5860 localtime_r(&nowt
,&now
);
5861 daydiff
= now
.tm_yday
- tm
.tm_yday
;
5862 if ((daydiff
< 0) || (daydiff
> 6)) {
5863 /* Day of month and month */
5865 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5866 res
= ast_streamfile(chan
, fn
, lang
);
5868 res
= ast_waitstream(chan
, ints
);
5871 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5873 } else if (daydiff
) {
5874 /* Just what day of the week */
5876 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5877 res
= ast_streamfile(chan
, fn
, lang
);
5879 res
= ast_waitstream(chan
, ints
);
5881 } /* Otherwise, it was today */
5883 res
= ast_say_time(chan
, t
, ints
, lang
);
5888 int ast_say_datetime_from_now_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5899 localtime_r(&t
,&tm
);
5900 localtime_r(&nowt
,&now
);
5901 daydiff
= now
.tm_yday
- tm
.tm_yday
;
5902 if ((daydiff
< 0) || (daydiff
> 6)) {
5903 /* Day of month and month */
5905 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5906 res
= ast_streamfile(chan
, fn
, lang
);
5908 res
= ast_waitstream(chan
, ints
);
5911 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5913 } else if (daydiff
) {
5914 /* Just what day of the week */
5916 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5917 res
= ast_streamfile(chan
, fn
, lang
);
5919 res
= ast_waitstream(chan
, ints
);
5921 } /* Otherwise, it was today */
5923 res
= ast_say_time(chan
, t
, ints
, lang
);
5927 /* Portuguese syntax */
5928 int ast_say_datetime_from_now_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5939 localtime_r(&t
,&tm
);
5940 localtime_r(&nowt
,&now
);
5941 daydiff
= now
.tm_yday
- tm
.tm_yday
;
5942 if ((daydiff
< 0) || (daydiff
> 6)) {
5943 /* Day of month and month */
5945 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5947 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
5948 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5950 res
= wait_file(chan
, ints
, fn
, lang
);
5952 } else if (daydiff
) {
5953 /* Just what day of the week */
5954 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5956 res
= wait_file(chan
, ints
, fn
, lang
);
5957 } /* Otherwise, it was today */
5958 snprintf(fn
, sizeof(fn
), "digits/pt-ah");
5960 res
= wait_file(chan
, ints
, fn
, lang
);
5961 if (tm
.tm_hour
!= 1)
5963 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5965 res
= ast_say_time(chan
, t
, ints
, lang
);
5970 /*********************************** GREEK SUPPORT ***************************************/
5974 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
5976 static int gr_say_number_female(int num
, struct ast_channel
*chan
, const char *ints
, const char *lang
){
5982 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
5984 snprintf(fn
, sizeof(fn
), "digits/female-%d", num
);
5985 res
= wait_file(chan
, ints
, fn
, lang
);
5986 } else if (num
< 13) {
5987 res
= ast_say_number(chan
, num
, ints
, lang
, (char *) NULL
);
5988 } else if (num
<100 ) {
5989 tmp
= (num
/10) * 10;
5991 snprintf(fn
, sizeof(fn
), "digits/%d", tmp
);
5992 res
= ast_streamfile(chan
, fn
, lang
);
5994 res
= ast_waitstream(chan
, ints
);
5996 gr_say_number_female(left
, chan
, ints
, lang
);
6007 * A list of the files that you need to create
6008 -> digits/xilia = "xilia"
6009 -> digits/myrio = "ekatomyrio"
6010 -> digits/thousands = "xiliades"
6011 -> digits/millions = "ektatomyria"
6012 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6013 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6015 Here we must note that we use digits/tens/100 to utter "ekato"
6016 and digits/hundred-100 to utter "ekaton"
6017 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6018 "terakosia". Here again we use hundreds/1000 for "xilia"
6019 and digits/thousnds for "xiliades"
6022 static int ast_say_number_full_gr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
,int audiofd
, int ctrlfd
)
6030 snprintf(fn
, sizeof(fn
), "digits/0");
6031 res
= ast_streamfile(chan
, fn
, chan
->language
);
6033 return ast_waitstream(chan
, ints
);
6036 while(!res
&& num
) {
6039 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
6041 } else if (num
<= 100) {
6042 /* 13 < num <= 100 */
6043 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
6044 num
-= ((num
/ 10) * 10);
6045 } else if (num
< 200) {
6046 /* 100 < num < 200 */
6047 snprintf(fn
, sizeof(fn
), "digits/hundred-100");
6048 num
-= ((num
/ 100) * 100);
6049 }else if (num
< 1000) {
6050 /* 200 < num < 1000 */
6051 snprintf(fn
, sizeof(fn
), "digits/hundred-%d", (num
/100)*100);
6052 num
-= ((num
/ 100) * 100);
6053 }else if (num
< 2000){
6054 snprintf(fn
, sizeof(fn
), "digits/xilia");
6055 num
-= ((num
/ 1000) * 1000);
6059 if (num
< 1000000) {
6060 res
= ast_say_number_full_gr(chan
, (num
/ 1000), ints
, chan
->language
, audiofd
, ctrlfd
);
6064 snprintf(fn
, sizeof(fn
), "digits/thousands");
6066 if (num
< 1000000000) { /* 1,000,000,000 */
6067 res
= ast_say_number_full_gr(chan
, (num
/ 1000000), ints
, chan
->language
,audiofd
, ctrlfd
);
6070 num
= num
% 1000000;
6071 snprintf(fn
, sizeof(fn
), "digits/millions");
6073 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
6079 if(!ast_streamfile(chan
, fn
, language
)) {
6080 if ((audiofd
> -1) && (ctrlfd
> -1))
6081 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
6083 res
= ast_waitstream(chan
, ints
);
6085 ast_stopstream(chan
);
6093 * The format is weekday - day - month -year
6095 * A list of the files that you need to create
6096 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6097 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6098 Attention the months are in
6103 static int ast_say_date_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6111 ast_localtime(&t
,&tm
,NULL
);
6112 /* W E E K - D A Y */
6114 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6115 res
= ast_streamfile(chan
, fn
, lang
);
6117 res
= ast_waitstream(chan
, ints
);
6121 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6125 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6126 res
= ast_streamfile(chan
, fn
, lang
);
6128 res
= ast_waitstream(chan
, ints
);
6132 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
6138 /* A list of the files that you need to create
6139 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6140 * digits/kai : "KAI"
6142 * digits/p-m : "meta meshmbrias"
6143 * digits/a-m : "pro meshmbrias"
6146 static int ast_say_time_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6153 localtime_r(&t
,&tm
);
6158 else if (hour
== 12)
6160 else if (hour
> 12) {
6165 res
= gr_say_number_female(hour
, chan
, ints
, lang
);
6168 res
= ast_streamfile(chan
, "digits/kai", lang
);
6170 res
= ast_waitstream(chan
, ints
);
6172 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
6175 res
= ast_streamfile(chan
, "digits/hwra", lang
);
6177 res
= ast_waitstream(chan
, ints
);
6181 res
= ast_streamfile(chan
, "digits/p-m", lang
);
6184 res
= ast_streamfile(chan
, "digits/a-m", lang
);
6187 res
= ast_waitstream(chan
, ints
);
6193 static int ast_say_datetime_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6198 localtime_r(&t
,&tm
);
6201 /* W E E K - D A Y */
6203 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6204 res
= ast_streamfile(chan
, fn
, lang
);
6206 res
= ast_waitstream(chan
, ints
);
6210 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6214 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6215 res
= ast_streamfile(chan
, fn
, lang
);
6217 res
= ast_waitstream(chan
, ints
);
6220 res
= ast_say_time_gr(chan
, t
, ints
, lang
);
6224 static int ast_say_date_with_format_gr(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
6228 int res
=0, offset
, sndoffset
;
6229 char sndfile
[256], nextmsg
[256];
6232 format
= "AdBY 'digits/at' IMp";
6234 ast_localtime(&time
,&tm
,timezone
);
6236 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
6237 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
6238 switch (format
[offset
]) {
6239 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6241 /* Literal name of a sound file */
6243 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
6244 sndfile
[sndoffset
] = format
[offset
];
6245 sndfile
[sndoffset
] = '\0';
6246 res
= wait_file(chan
,ints
,sndfile
,lang
);
6250 /* Sunday - Saturday */
6251 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
6252 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6257 /* January - December */
6258 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
6259 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6263 /* first - thirtyfirst */
6264 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6269 ast_say_number_full_gr(chan
, 1900+tm
.tm_year
, ints
, chan
->language
, -1, -1);
6274 if (tm
.tm_hour
== 0)
6275 gr_say_number_female(12, chan
, ints
, lang
);
6276 else if (tm
.tm_hour
> 12)
6277 gr_say_number_female(tm
.tm_hour
- 12, chan
, ints
, lang
);
6279 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6284 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6290 res
= ast_streamfile(chan
, "digits/kai", lang
);
6292 res
= ast_waitstream(chan
, ints
);
6294 res
= ast_say_number_full_gr(chan
, tm
.tm_min
, ints
, lang
, -1, -1);
6297 res
= ast_streamfile(chan
, "digits/oclock", lang
);
6299 res
= ast_waitstream(chan
, ints
);
6305 if (tm
.tm_hour
> 11)
6306 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
6308 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
6309 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6312 /* Shorthand for "Today", "Yesterday", or ABdY */
6313 /* XXX As emphasized elsewhere, this should the native way in your
6314 * language to say the date, with changes in what you say, depending
6315 * upon how recent the date is. XXX */
6319 time_t beg_today
, tt
;
6321 gettimeofday(&now
,NULL
);
6323 ast_localtime(&tt
,&tmnow
,timezone
);
6324 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6325 /* In any case, it saves not having to do ast_mktime() */
6326 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6327 if (beg_today
< time
) {
6329 res
= wait_file(chan
,ints
, "digits/today",lang
);
6330 } else if (beg_today
- 86400 < time
) {
6332 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6334 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6339 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6340 /* XXX As emphasized elsewhere, this should the native way in your
6341 * language to say the date, with changes in what you say, depending
6342 * upon how recent the date is. XXX */
6346 time_t beg_today
, tt
;
6348 gettimeofday(&now
,NULL
);
6350 ast_localtime(&tt
,&tmnow
,timezone
);
6351 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6352 /* In any case, it saves not having to do ast_mktime() */
6353 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6354 if (beg_today
< time
) {
6356 } else if ((beg_today
- 86400) < time
) {
6358 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6359 } else if (beg_today
- 86400 * 6 < time
) {
6360 /* Within the last week */
6361 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "A", timezone
);
6363 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6368 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HM", timezone
);
6372 snprintf(nextmsg
,sizeof(nextmsg
), "digits/kai");
6373 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6375 res
= ast_say_number_full_gr(chan
, tm
.tm_sec
, ints
, lang
, -1, -1);
6377 snprintf(nextmsg
,sizeof(nextmsg
), "digits/seconds");
6378 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6381 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HMS", timezone
);
6385 /* Just ignore spaces and tabs */
6388 /* Unknown character */
6389 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
6391 /* Jump out on DTMF */
6400 * remap the 'say' functions to use those in this file
6402 static void __attribute__((constructor
)) __say_init(void)
6404 ast_say_number_full
= say_number_full
;
6405 ast_say_enumeration_full
= say_enumeration_full
;
6406 ast_say_digit_str_full
= say_digit_str_full
;
6407 ast_say_character_str_full
= say_character_str_full
;
6408 ast_say_phonetic_str_full
= say_phonetic_str_full
;
6409 ast_say_datetime
= say_datetime
;
6410 ast_say_time
= say_time
;
6411 ast_say_date
= say_date
;
6412 ast_say_datetime_from_now
= say_datetime_from_now
;
6413 ast_say_date_with_format
= say_date_with_format
;