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>
32 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
34 #include <sys/types.h>
37 #include <netinet/in.h>
44 #include <iso/limits_iso.h>
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 2hundred: 2 hundreds
1149 2thousands: 2 thousand
1150 thousands: plural of 'thousand'
1151 3sF 'Smichut forms (female)
1158 3s 'Smichut' forms (male)
1175 TODO: 've' should sometimed be 'hu':
1176 * before 'shtaym' (2, F)
1177 * before 'shnaym' (2, M)
1178 * before 'shlosha' (3, M)
1179 * before 'shmone' (8, M)
1180 * before 'shlosim' (30)
1181 * before 'shmonim' (80)
1187 #define SAY_NUM_BUF_SIZE 256
1188 static int ast_say_number_full_he(struct ast_channel
*chan
, int num
,
1189 const char *ints
, const char *language
, const char *options
,
1190 int audiofd
, int ctrlfd
)
1193 int state
= 0; /* no need to save anything */
1194 int mf
= 1; /* +1 = Masculin; -1 = Feminin */
1195 char fn
[SAY_NUM_BUF_SIZE
] = "";
1196 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: started. "
1197 "num: %d, options=\"%s\"\n",
1201 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1203 if (options
&& !strncasecmp(options
, "f",1))
1206 /* Do we have work to do? */
1207 while(!res
&& (num
|| (state
>0) )) {
1208 /* first type of work: play a second sound. In this loop
1209 * we can only play one sound file at a time. Thus playing
1210 * a second one requires repeating the loop just for the
1211 * second file. The variable 'state' remembers where we were.
1212 * state==0 is the normal mode and it means that we continue
1213 * to check if the number num has yet anything left.
1215 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: num: %d, "
1216 "state=%d, options=\"%s\", mf=%d\n",
1217 num
, state
, options
, mf
1220 snprintf(fn
, sizeof(fn
), "digits/hundred");
1222 } else if (state
==2) {
1223 snprintf(fn
, sizeof(fn
), "digits/ve");
1225 } else if (state
==3) {
1226 snprintf(fn
, sizeof(fn
), "digits/thousands");
1228 } else if (num
<21) {
1230 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1232 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1234 } else if (num
< 100) {
1235 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1238 } else if (num
< 200) {
1239 snprintf(fn
, sizeof(fn
), "digits/1hundred");
1242 } else if (num
< 300) {
1243 snprintf(fn
, sizeof(fn
), "digits/2hundred");
1246 } else if (num
< 1000) {
1247 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1250 } else if (num
< 2000) {
1251 snprintf(fn
, sizeof(fn
), "digits/thousand");
1253 } else if (num
< 3000) {
1254 snprintf(fn
, sizeof(fn
), "digits/2thousand");
1257 } else if (num
< 20000) {
1258 snprintf(fn
, sizeof(fn
), "digits/%ds",(num
/1000));
1261 } else if (num
< 1000000) {
1262 res
= ast_say_number_full_he(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1265 snprintf(fn
, sizeof(fn
), "digits/thousand");
1267 } else if (num
< 1000000000) {
1268 res
= ast_say_number_full_he(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1271 snprintf(fn
, sizeof(fn
), "digits/million");
1272 num
= num
% 1000000;
1274 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1278 if(!ast_streamfile(chan
, fn
, language
)) {
1279 if ((audiofd
> -1) && (ctrlfd
> -1))
1280 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1282 res
= ast_waitstream(chan
, ints
);
1284 ast_stopstream(chan
);
1290 /*! \brief ast_say_number_full_it: Italian */
1291 static int ast_say_number_full_it(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1299 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1304 Like english, numbers up to 20 are a single 'word', and others
1305 compound, but with exceptions.
1306 For example 21 is not twenty-one, but there is a single word in 'it'.
1307 Idem for 28 (ie when a the 2nd part of a compund number
1308 starts with a vowel)
1310 There are exceptions also for hundred, thousand and million.
1311 In english 100 = one hundred, 200 is two hundred.
1312 In italian 100 = cento , like to say hundred (without one),
1313 200 and more are like english.
1315 Same applies for thousand:
1316 1000 is one thousand in en, 2000 is two thousand.
1317 In it we have 1000 = mille , 2000 = 2 mila
1319 For million(s) we use the plural, if more than one
1320 Also, one million is abbreviated in it, like on-million,
1321 or 'un milione', not 'uno milione'.
1322 So the right file is provided.
1325 while(!res
&& (num
|| playh
)) {
1327 snprintf(fn
, sizeof(fn
), "digits/minus");
1328 if ( num
> INT_MIN
) {
1334 snprintf(fn
, sizeof(fn
), "digits/hundred");
1336 } else if (num
< 20) {
1337 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1339 } else if (num
== 21) {
1340 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1342 } else if (num
== 28) {
1343 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1345 } else if (num
== 31) {
1346 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1348 } else if (num
== 38) {
1349 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1351 } else if (num
== 41) {
1352 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1354 } else if (num
== 48) {
1355 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1357 } else if (num
== 51) {
1358 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1360 } else if (num
== 58) {
1361 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1363 } else if (num
== 61) {
1364 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1366 } else if (num
== 68) {
1367 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1369 } else if (num
== 71) {
1370 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1372 } else if (num
== 78) {
1373 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1375 } else if (num
== 81) {
1376 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1378 } else if (num
== 88) {
1379 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1381 } else if (num
== 91) {
1382 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1384 } else if (num
== 98) {
1385 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1387 } else if (num
< 100) {
1388 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1389 num
-= ((num
/ 10) * 10);
1392 if ((num
/ 100) > 1) {
1393 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1396 snprintf(fn
, sizeof(fn
), "digits/hundred");
1398 num
-= ((num
/ 100) * 100);
1400 if (num
< 1000000) { /* 1,000,000 */
1402 res
= ast_say_number_full_it(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1407 if ((tempnum
/ 1000) < 2)
1408 snprintf(fn
, sizeof(fn
), "digits/thousand");
1409 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1410 snprintf(fn
, sizeof(fn
), "digits/thousands");
1412 if (num
< 1000000000) { /* 1,000,000,000 */
1413 if ((num
/ 1000000) > 1)
1414 res
= ast_say_number_full_it(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1418 num
= num
% 1000000;
1419 if ((tempnum
/ 1000000) < 2)
1420 snprintf(fn
, sizeof(fn
), "digits/million");
1422 snprintf(fn
, sizeof(fn
), "digits/millions");
1424 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1431 if(!ast_streamfile(chan
, fn
, language
)) {
1432 if ((audiofd
> -1) && (ctrlfd
> -1))
1433 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1435 res
= ast_waitstream(chan
, ints
);
1437 ast_stopstream(chan
);
1443 /*! \brief ast_say_number_full_nl: dutch syntax */
1444 /* New files: digits/nl-en
1446 static int ast_say_number_full_nl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1453 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1454 while (!res
&& (num
|| playh
)) {
1456 snprintf(fn
, sizeof(fn
), "digits/minus");
1457 if ( num
> INT_MIN
) {
1463 snprintf(fn
, sizeof(fn
), "digits/hundred");
1465 } else if (num
< 20) {
1466 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1468 } else if (num
< 100) {
1471 res
= ast_say_number_full_nl(chan
, units
, ints
, language
, audiofd
, ctrlfd
);
1475 snprintf(fn
, sizeof(fn
), "digits/nl-en");
1477 snprintf(fn
, sizeof(fn
), "digits/%d", num
- units
);
1482 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1484 num
-= ((num
/ 100) * 100);
1486 if (num
< 1000000) { /* 1,000,000 */
1487 res
= ast_say_number_full_en(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1491 snprintf(fn
, sizeof(fn
), "digits/thousand");
1493 if (num
< 1000000000) { /* 1,000,000,000 */
1494 res
= ast_say_number_full_en(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1497 num
= num
% 1000000;
1498 snprintf(fn
, sizeof(fn
), "digits/million");
1500 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1508 if(!ast_streamfile(chan
, fn
, language
)) {
1509 if ((audiofd
> -1) && (ctrlfd
> -1))
1510 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1512 res
= ast_waitstream(chan
, ints
);
1514 ast_stopstream(chan
);
1520 /*! \brief ast_say_number_full_no: Norwegian syntax */
1522 In addition to American English, the following sounds are required: "and", "1N"
1524 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
)
1529 int cn
= 1; /* +1 = commune; -1 = neuter */
1533 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1535 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
1537 while(!res
&& (num
|| playh
|| playa
)) {
1538 /* The grammar for Norwegian numbers is the same as for English except
1539 * for the following:
1540 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1541 * "and" before the last two digits, i.e. 2034 is "two thousand and
1542 * thirty-four" and 1000012 is "one million and twelve".
1545 snprintf(fn
, sizeof(fn
), "digits/minus");
1546 if ( num
> INT_MIN
) {
1552 snprintf(fn
, sizeof(fn
), "digits/hundred");
1555 snprintf(fn
, sizeof(fn
), "digits/and");
1557 } else if (num
== 1 && cn
== -1) {
1558 snprintf(fn
, sizeof(fn
), "digits/1N");
1560 } else if (num
< 20) {
1561 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1563 } else if (num
< 100) {
1564 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1565 num
-= ((num
/ 10) * 10);
1566 } else if (num
< 1000) {
1567 int hundreds
= num
/ 100;
1569 snprintf(fn
, sizeof(fn
), "digits/1N");
1571 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
1574 num
-= 100 * hundreds
;
1577 } else if (num
< 1000000) {
1578 res
= ast_say_number_full_no(chan
, num
/ 1000, ints
, language
, "n", audiofd
, ctrlfd
);
1581 snprintf(fn
, sizeof(fn
), "digits/thousand");
1583 if (num
&& num
< 100)
1585 } else if (num
< 1000000000) {
1586 int millions
= num
/ 1000000;
1587 res
= ast_say_number_full_no(chan
, millions
, ints
, language
, "c", audiofd
, ctrlfd
);
1590 snprintf(fn
, sizeof(fn
), "digits/million");
1591 num
= num
% 1000000;
1592 if (num
&& num
< 100)
1595 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1600 if(!ast_streamfile(chan
, fn
, language
)) {
1601 if ((audiofd
> -1) && (ctrlfd
> -1))
1602 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1604 res
= ast_waitstream(chan
, ints
);
1606 ast_stopstream(chan
);
1613 char *separator_dziesiatek
;
1617 char *dziesiatki
[10];
1622 static char *pl_rzad_na_tekst(odmiana
*odm
, int i
, int rzad
)
1628 return odm
->rzedy
[rzad
- 1][0];
1629 if ((i
> 21 || i
< 11) && i
%10 > 1 && i
%10 < 5)
1630 return odm
->rzedy
[rzad
- 1][1];
1632 return odm
->rzedy
[rzad
- 1][2];
1635 static char* pl_append(char* buffer
, char* str
)
1637 strcpy(buffer
, str
);
1638 buffer
+= strlen(str
);
1642 static void pl_odtworz_plik(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, char *fn
)
1644 char file_name
[255] = "digits/";
1645 strcat(file_name
, fn
);
1646 ast_log(LOG_DEBUG
, "Trying to play: %s\n", file_name
);
1647 if (!ast_streamfile(chan
, file_name
, language
)) {
1648 if ((audiofd
> -1) && (ctrlfd
> -1))
1649 ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1651 ast_waitstream(chan
, ints
);
1653 ast_stopstream(chan
);
1656 static void powiedz(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, odmiana
*odm
, int rzad
, int i
)
1658 /* Initialise variables to allow compilation on Debian-stable, etc */
1668 if (i
== 0 && rzad
> 0) {
1672 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[0]);
1676 m1000E6
= i
% 1000000000;
1677 i1000E6
= i
/ 1000000000;
1679 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+3, i1000E6
);
1681 m1000E3
= m1000E6
% 1000000;
1682 i1000E3
= m1000E6
/ 1000000;
1684 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+2, i1000E3
);
1686 m1000
= m1000E3
% 1000;
1687 i1000
= m1000E3
/ 1000;
1689 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+1, i1000
);
1695 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->setki
[i100
]);
1697 if ( m100
> 0 && m100
<=9 ) {
1699 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
]);
1701 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[m100
]);
1702 } else if (m100
% 10 == 0) {
1703 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1704 } else if (m100
<= 19 ) {
1705 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->nastki
[m100
% 10]);
1706 } else if (m100
!= 0) {
1707 if (odm
->separator_dziesiatek
[0]==' ') {
1708 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1709 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
% 10]);
1713 b
= pl_append(b
, odm
->dziesiatki
[m100
/ 10]);
1714 b
= pl_append(b
, odm
->separator_dziesiatek
);
1715 b
= pl_append(b
, odm
->cyfry2
[m100
% 10]);
1716 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, buf
);
1721 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, pl_rzad_na_tekst(odm
, i
, rzad
));
1725 /* ast_say_number_full_pl: Polish syntax */
1726 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
)
1736 1000000000.2 miliardy
1737 1000000000.5 miliardow
1801 70m siedemdziesieciu
1813 90m dziewiedziesieciu
1815 and combinations of eg.: 20_1, 30m_3m, etc...
1819 char *zenski_cyfry
[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1821 char *zenski_cyfry2
[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1823 char *meski_cyfry
[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1825 char *meski_cyfry2
[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1827 char *meski_setki
[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1829 char *meski_dziesiatki
[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1831 char *meski_nastki
[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1833 char *nijaki_cyfry
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1835 char *nijaki_cyfry2
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1837 char *nijaki_setki
[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1839 char *nijaki_dziesiatki
[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1841 char *nijaki_nastki
[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1843 char *rzedy
[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1845 /* Initialise variables to allow compilation on Debian-stable, etc */
1848 static odmiana
*odmiana_nieosobowa
= NULL
;
1849 static odmiana
*odmiana_meska
= NULL
;
1850 static odmiana
*odmiana_zenska
= NULL
;
1852 if (odmiana_nieosobowa
== NULL
) {
1853 odmiana_nieosobowa
= (odmiana
*) malloc(sizeof(odmiana
));
1855 odmiana_nieosobowa
->separator_dziesiatek
= " ";
1857 memcpy(odmiana_nieosobowa
->cyfry
, nijaki_cyfry
, sizeof(odmiana_nieosobowa
->cyfry
));
1858 memcpy(odmiana_nieosobowa
->cyfry2
, nijaki_cyfry2
, sizeof(odmiana_nieosobowa
->cyfry
));
1859 memcpy(odmiana_nieosobowa
->setki
, nijaki_setki
, sizeof(odmiana_nieosobowa
->setki
));
1860 memcpy(odmiana_nieosobowa
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_nieosobowa
->dziesiatki
));
1861 memcpy(odmiana_nieosobowa
->nastki
, nijaki_nastki
, sizeof(odmiana_nieosobowa
->nastki
));
1862 memcpy(odmiana_nieosobowa
->rzedy
, rzedy
, sizeof(odmiana_nieosobowa
->rzedy
));
1865 if (odmiana_zenska
== NULL
) {
1866 odmiana_zenska
= (odmiana
*) malloc(sizeof(odmiana
));
1868 odmiana_zenska
->separator_dziesiatek
= " ";
1870 memcpy(odmiana_zenska
->cyfry
, zenski_cyfry
, sizeof(odmiana_zenska
->cyfry
));
1871 memcpy(odmiana_zenska
->cyfry2
, zenski_cyfry2
, sizeof(odmiana_zenska
->cyfry
));
1872 memcpy(odmiana_zenska
->setki
, nijaki_setki
, sizeof(odmiana_zenska
->setki
));
1873 memcpy(odmiana_zenska
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_zenska
->dziesiatki
));
1874 memcpy(odmiana_zenska
->nastki
, nijaki_nastki
, sizeof(odmiana_zenska
->nastki
));
1875 memcpy(odmiana_zenska
->rzedy
, rzedy
, sizeof(odmiana_zenska
->rzedy
));
1878 if (odmiana_meska
== NULL
) {
1879 odmiana_meska
= (odmiana
*) malloc(sizeof(odmiana
));
1881 odmiana_meska
->separator_dziesiatek
= " ";
1883 memcpy(odmiana_meska
->cyfry
, meski_cyfry
, sizeof(odmiana_meska
->cyfry
));
1884 memcpy(odmiana_meska
->cyfry2
, meski_cyfry2
, sizeof(odmiana_meska
->cyfry
));
1885 memcpy(odmiana_meska
->setki
, meski_setki
, sizeof(odmiana_meska
->setki
));
1886 memcpy(odmiana_meska
->dziesiatki
, meski_dziesiatki
, sizeof(odmiana_meska
->dziesiatki
));
1887 memcpy(odmiana_meska
->nastki
, meski_nastki
, sizeof(odmiana_meska
->nastki
));
1888 memcpy(odmiana_meska
->rzedy
, rzedy
, sizeof(odmiana_meska
->rzedy
));
1892 if (strncasecmp(options
, "f", 1) == 0)
1894 else if (strncasecmp(options
, "m", 1) == 0)
1897 o
= odmiana_nieosobowa
;
1899 o
= odmiana_nieosobowa
;
1901 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, o
, 0, num
);
1905 /* ast_say_number_full_pt: Portuguese syntax */
1906 /* Extra sounds needed: */
1907 /* For feminin all sound files end with F */
1908 /* 100E for 100+ something */
1909 /* 1000000S for plural */
1910 /* pt-e for 'and' */
1911 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
)
1915 int mf
= 1; /* +1 = male; -1 = female */
1919 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1921 if (options
&& !strncasecmp(options
, "f",1))
1924 while(!res
&& num
) {
1926 snprintf(fn
, sizeof(fn
), "digits/minus");
1927 if ( num
> INT_MIN
) {
1932 } else if (num
< 20) {
1933 if ((num
== 1 || num
== 2) && (mf
< 0))
1934 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1936 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1938 } else if (num
< 100) {
1939 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 10) * 10);
1943 } else if (num
< 1000) {
1945 snprintf(fn
, sizeof(fn
), "digits/100");
1947 snprintf(fn
, sizeof(fn
), "digits/100E");
1949 if (mf
< 0 && num
> 199)
1950 snprintf(fn
, sizeof(fn
), "digits/%dF", (num
/ 100) * 100);
1952 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100) * 100);
1957 } else if (num
< 1000000) {
1959 res
= ast_say_number_full_pt(chan
, (num
/ 1000) * mf
, ints
, language
, options
, audiofd
, ctrlfd
);
1963 snprintf(fn
, sizeof(fn
), "digits/1000");
1964 if ((num
% 1000) && ((num
% 1000) < 100 || !(num
% 100)))
1967 } else if (num
< 1000000000) {
1968 res
= ast_say_number_full_pt(chan
, (num
/ 1000000), ints
, language
, options
, audiofd
, ctrlfd
);
1972 snprintf(fn
, sizeof(fn
), "digits/1000000");
1974 snprintf(fn
, sizeof(fn
), "digits/1000000S");
1976 if ((num
% 1000000) &&
1978 ((!((num
/ 1000) % 1000) && ((num
% 1000) < 100 || !(num
% 100))) ||
1979 /* no hundreds and below */
1980 (!(num
% 1000) && (((num
/ 1000) % 1000) < 100 || !((num
/ 1000) % 100))) ) )
1982 num
= num
% 1000000;
1985 if (!ast_streamfile(chan
, fn
, language
)) {
1986 if ((audiofd
> -1) && (ctrlfd
> -1))
1987 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1989 res
= ast_waitstream(chan
, ints
);
1991 ast_stopstream(chan
);
1993 if (!res
&& playh
) {
1994 res
= wait_file(chan
, ints
, "digits/pt-e", language
);
1995 ast_stopstream(chan
);
2002 /*! \brief ast_say_number_full_se: Swedish syntax */
2003 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
)
2008 int cn
= 1; /* +1 = commune; -1 = neuter */
2010 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2011 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
2013 while(!res
&& (num
|| playh
)) {
2015 snprintf(fn
, sizeof(fn
), "digits/minus");
2016 if ( num
> INT_MIN
) {
2022 snprintf(fn
, sizeof(fn
), "digits/hundred");
2024 } else if (num
< 20) {
2025 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2027 } else if (num
< 100) {
2028 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2029 num
-= ((num
/ 10) * 10);
2030 } else if (num
== 1 && cn
== -1) { /* En eller ett? */
2031 snprintf(fn
, sizeof(fn
), "digits/1N");
2035 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2037 num
-= ((num
/ 100) * 100);
2039 if (num
< 1000000) { /* 1,000,000 */
2040 res
= ast_say_number_full_se(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
2045 snprintf(fn
, sizeof(fn
), "digits/thousand");
2047 if (num
< 1000000000) { /* 1,000,000,000 */
2048 res
= ast_say_number_full_se(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
2052 num
= num
% 1000000;
2053 snprintf(fn
, sizeof(fn
), "digits/million");
2055 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2062 if(!ast_streamfile(chan
, fn
, language
)) {
2063 if ((audiofd
> -1) && (ctrlfd
> -1))
2064 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2066 res
= ast_waitstream(chan
, ints
);
2067 ast_stopstream(chan
);
2074 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2075 static int ast_say_number_full_tw(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2081 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2083 while(!res
&& (num
|| playh
)) {
2085 snprintf(fn
, sizeof(fn
), "digits/minus");
2086 if ( num
> INT_MIN
) {
2092 snprintf(fn
, sizeof(fn
), "digits/hundred");
2094 } else if (num
< 10) {
2095 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2097 } else if (num
< 100) {
2098 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2099 num
-= ((num
/ 10) * 10);
2102 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2104 num
-= ((num
/ 100) * 100);
2106 if (num
< 1000000) { /* 1,000,000 */
2107 res
= ast_say_number_full_tw(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
2111 snprintf(fn
, sizeof(fn
), "digits/thousand");
2113 if (num
< 1000000000) { /* 1,000,000,000 */
2114 res
= ast_say_number_full_tw(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
2117 num
= num
% 1000000;
2118 snprintf(fn
, sizeof(fn
), "digits/million");
2120 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2127 if(!ast_streamfile(chan
, fn
, language
)) {
2128 if ((audiofd
> -1) && (ctrlfd
> -1))
2129 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2131 res
= ast_waitstream(chan
, ints
);
2133 ast_stopstream(chan
);
2140 /*! \brief determine last digits for thousands/millions (ru) */
2141 static int get_lastdigits_ru(int num
) {
2144 } else if (num
< 100) {
2145 return get_lastdigits_ru(num
% 10);
2146 } else if (num
< 1000) {
2147 return get_lastdigits_ru(num
% 100);
2149 return 0; /* number too big */
2153 /*! \brief ast_say_number_full_ru: Russian syntax */
2154 /*! \brief additional files:
2155 n00.gsm (one hundred, two hundred, ...)
2158 thousands-i.gsm (tisyachi)
2159 million-a.gsm (milliona)
2165 where 'n' from 1 to 9
2167 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
)
2173 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2175 while(!res
&& (num
)) {
2177 snprintf(fn
, sizeof(fn
), "digits/minus");
2178 if ( num
> INT_MIN
) {
2183 } else if (num
< 20) {
2184 if(options
&& strlen(options
) == 1 && num
< 3) {
2185 snprintf(fn
, sizeof(fn
), "digits/%d%s", num
, options
);
2187 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2190 } else if (num
< 100) {
2191 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 10));
2193 } else if (num
< 1000){
2194 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 100));
2196 } else if (num
< 1000000) { /* 1,000,000 */
2197 lastdigits
= get_lastdigits_ru(num
/ 1000);
2199 if (lastdigits
< 3) {
2200 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, "f", audiofd
, ctrlfd
);
2202 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2206 if (lastdigits
== 1) {
2207 snprintf(fn
, sizeof(fn
), "digits/thousand");
2208 } else if (lastdigits
> 1 && lastdigits
< 5) {
2209 snprintf(fn
, sizeof(fn
), "digits/thousands-i");
2211 snprintf(fn
, sizeof(fn
), "digits/thousands");
2214 } else if (num
< 1000000000) { /* 1,000,000,000 */
2215 lastdigits
= get_lastdigits_ru(num
/ 1000000);
2217 res
= ast_say_number_full_ru(chan
, num
/ 1000000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2220 if (lastdigits
== 1) {
2221 snprintf(fn
, sizeof(fn
), "digits/million");
2222 } else if (lastdigits
> 1 && lastdigits
< 5) {
2223 snprintf(fn
, sizeof(fn
), "digits/million-a");
2225 snprintf(fn
, sizeof(fn
), "digits/millions");
2229 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2233 if (!ast_streamfile(chan
, fn
, language
)) {
2234 if ((audiofd
> -1) && (ctrlfd
> -1))
2235 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2237 res
= ast_waitstream(chan
, ints
);
2239 ast_stopstream(chan
);
2246 /*! \brief ast_say_enumeration_full: call language-specific functions */
2247 /* Called from AGI */
2248 static int say_enumeration_full(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2250 if (!strcasecmp(language
,"en") ) { /* English syntax */
2251 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2252 } else if (!strcasecmp(language
, "da") ) { /* Danish syntax */
2253 return(ast_say_enumeration_full_da(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2254 } else if (!strcasecmp(language
, "de") ) { /* German syntax */
2255 return(ast_say_enumeration_full_de(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2258 /* Default to english */
2259 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2262 /*! \brief ast_say_enumeration_full_en: English syntax */
2263 /* This is the default syntax, if no other syntax defined in this file is used */
2264 static int ast_say_enumeration_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2269 while(!res
&& num
) {
2271 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2272 if ( num
> INT_MIN
) {
2277 } else if (num
< 20) {
2278 snprintf(fn
, sizeof(fn
), "digits/h-%d", num
);
2280 } else if (num
< 100) {
2281 int tens
= num
/ 10;
2284 snprintf(fn
, sizeof(fn
), "digits/h-%d", (tens
* 10));
2286 snprintf(fn
, sizeof(fn
), "digits/%d", (tens
* 10));
2288 } else if (num
< 1000) {
2289 int hundreds
= num
/ 100;
2291 if (hundreds
> 1 || t
== 1) {
2292 res
= ast_say_number_full_en(chan
, hundreds
, ints
, language
, audiofd
, ctrlfd
);
2297 snprintf(fn
, sizeof(fn
), "digits/hundred");
2299 snprintf(fn
, sizeof(fn
), "digits/h-hundred");
2301 } else if (num
< 1000000) {
2302 int thousands
= num
/ 1000;
2304 if (thousands
> 1 || t
== 1) {
2305 res
= ast_say_number_full_en(chan
, thousands
, ints
, language
, audiofd
, ctrlfd
);
2310 snprintf(fn
, sizeof(fn
), "digits/thousand");
2312 snprintf(fn
, sizeof(fn
), "digits/h-thousand");
2315 } else if (num
< 1000000000) {
2316 int millions
= num
/ 1000000;
2317 num
= num
% 1000000;
2319 res
= ast_say_number_full_en(chan
, millions
, ints
, language
, audiofd
, ctrlfd
);
2323 snprintf(fn
, sizeof(fn
), "digits/million");
2325 snprintf(fn
, sizeof(fn
), "digits/h-million");
2327 } else if (num
< INT_MAX
) {
2328 int billions
= num
/ 1000000000;
2329 num
= num
% 1000000000;
2331 res
= ast_say_number_full_en(chan
, billions
, ints
, language
, audiofd
, ctrlfd
);
2335 snprintf(fn
, sizeof(fn
), "digits/billion");
2337 snprintf(fn
, sizeof(fn
), "digits/h-billion");
2339 } else if (num
== INT_MAX
) {
2340 snprintf(fn
, sizeof(fn
), "digits/h-last");
2343 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2348 if (!ast_streamfile(chan
, fn
, language
)) {
2349 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2350 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2352 res
= ast_waitstream(chan
, ints
);
2355 ast_stopstream(chan
);
2361 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2362 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
)
2364 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2366 char fn
[256] = "", fna
[256] = "";
2369 if (options
&& !strncasecmp(options
, "f",1)) {
2371 } else if (options
&& !strncasecmp(options
, "n",1)) {
2378 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2380 while(!res
&& num
) {
2382 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2383 if ( num
> INT_MIN
) {
2388 } else if (num
< 100 && t
) {
2389 snprintf(fn
, sizeof(fn
), "digits/and");
2391 } else if (num
< 20) {
2392 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2394 } else if (num
< 100) {
2395 int ones
= num
% 10;
2397 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2400 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2403 } else if (num
== 100 && t
== 0) {
2404 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2406 } else if (num
< 1000) {
2407 int hundreds
= num
/ 100;
2409 if (hundreds
== 1) {
2410 snprintf(fn
, sizeof(fn
), "digits/1N");
2412 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2415 snprintf(fna
, sizeof(fna
), "digits/hundred");
2417 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2420 } else if (num
< 1000000) {
2421 int thousands
= num
/ 1000;
2423 if (thousands
== 1) {
2425 snprintf(fn
, sizeof(fn
), "digits/1N");
2426 snprintf(fna
, sizeof(fna
), "digits/thousand");
2429 snprintf(fn
, sizeof(fn
), "digits/1N");
2430 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2432 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2436 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2441 snprintf(fn
, sizeof(fn
), "digits/thousand");
2443 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2447 } else if (num
< 1000000000) {
2448 int millions
= num
/ 1000000;
2449 num
= num
% 1000000;
2450 if (millions
== 1) {
2452 snprintf(fn
, sizeof(fn
), "digits/1F");
2453 snprintf(fna
, sizeof(fna
), "digits/million");
2455 snprintf(fn
, sizeof(fn
), "digits/1N");
2456 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2459 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2464 snprintf(fn
, sizeof(fn
), "digits/millions");
2466 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2470 } else if (num
< INT_MAX
) {
2471 int billions
= num
/ 1000000000;
2472 num
= num
% 1000000000;
2473 if (billions
== 1) {
2475 snprintf(fn
, sizeof(fn
), "digits/1F");
2476 snprintf(fna
, sizeof(fna
), "digits/milliard");
2478 snprintf(fn
, sizeof(fn
), "digits/1N");
2479 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2482 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2486 snprintf(fn
, sizeof(fna
), "digits/milliards");
2488 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2492 } else if (num
== INT_MAX
) {
2493 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2496 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2501 if (!ast_streamfile(chan
, fn
, language
)) {
2502 if ((audiofd
> -1) && (ctrlfd
> -1))
2503 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2505 res
= ast_waitstream(chan
, ints
);
2507 ast_stopstream(chan
);
2509 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2510 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2511 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2513 res
= ast_waitstream(chan
, ints
);
2516 ast_stopstream(chan
);
2524 /*! \brief ast_say_enumeration_full_de: German syntax */
2525 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
)
2527 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2529 char fn
[256] = "", fna
[256] = "";
2532 if (options
&& !strncasecmp(options
, "f",1)) {
2534 } else if (options
&& !strncasecmp(options
, "n",1)) {
2541 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2543 while(!res
&& num
) {
2545 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2546 if ( num
> INT_MIN
) {
2551 } else if (num
< 100 && t
) {
2552 snprintf(fn
, sizeof(fn
), "digits/and");
2554 } else if (num
< 20) {
2555 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2557 } else if (num
< 100) {
2558 int ones
= num
% 10;
2560 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2563 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2566 } else if (num
== 100 && t
== 0) {
2567 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2569 } else if (num
< 1000) {
2570 int hundreds
= num
/ 100;
2572 if (hundreds
== 1) {
2573 snprintf(fn
, sizeof(fn
), "digits/1N");
2575 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2578 snprintf(fna
, sizeof(fna
), "digits/hundred");
2580 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2583 } else if (num
< 1000000) {
2584 int thousands
= num
/ 1000;
2586 if (thousands
== 1) {
2588 snprintf(fn
, sizeof(fn
), "digits/1N");
2589 snprintf(fna
, sizeof(fna
), "digits/thousand");
2592 snprintf(fn
, sizeof(fn
), "digits/1N");
2593 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2595 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2599 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2604 snprintf(fn
, sizeof(fn
), "digits/thousand");
2606 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2610 } else if (num
< 1000000000) {
2611 int millions
= num
/ 1000000;
2612 num
= num
% 1000000;
2613 if (millions
== 1) {
2615 snprintf(fn
, sizeof(fn
), "digits/1F");
2616 snprintf(fna
, sizeof(fna
), "digits/million");
2618 snprintf(fn
, sizeof(fn
), "digits/1N");
2619 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2622 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2627 snprintf(fn
, sizeof(fn
), "digits/millions");
2629 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2633 } else if (num
< INT_MAX
) {
2634 int billions
= num
/ 1000000000;
2635 num
= num
% 1000000000;
2636 if (billions
== 1) {
2638 snprintf(fn
, sizeof(fn
), "digits/1F");
2639 snprintf(fna
, sizeof(fna
), "digits/milliard");
2641 snprintf(fn
, sizeof(fn
), "digits/1N");
2642 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2645 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2649 snprintf(fn
, sizeof(fna
), "digits/milliards");
2651 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2655 } else if (num
== INT_MAX
) {
2656 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2659 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2664 if (!ast_streamfile(chan
, fn
, language
)) {
2665 if ((audiofd
> -1) && (ctrlfd
> -1))
2666 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2668 res
= ast_waitstream(chan
, ints
);
2670 ast_stopstream(chan
);
2672 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2673 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2674 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2676 res
= ast_waitstream(chan
, ints
);
2679 ast_stopstream(chan
);
2687 static int say_date(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2689 if (!strcasecmp(lang
, "en") ) { /* English syntax */
2690 return(ast_say_date_en(chan
, t
, ints
, lang
));
2691 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
2692 return(ast_say_date_da(chan
, t
, ints
, lang
));
2693 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
2694 return(ast_say_date_de(chan
, t
, ints
, lang
));
2695 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
2696 return(ast_say_date_fr(chan
, t
, ints
, lang
));
2697 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
2698 return(ast_say_date_nl(chan
, t
, ints
, lang
));
2699 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
2700 return(ast_say_date_pt(chan
, t
, ints
, lang
));
2701 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
2702 return(ast_say_date_gr(chan
, t
, ints
, lang
));
2705 /* Default to English */
2706 return(ast_say_date_en(chan
, t
, ints
, lang
));
2709 /* English syntax */
2710 int ast_say_date_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2715 ast_localtime(&t
,&tm
,NULL
);
2717 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2718 res
= ast_streamfile(chan
, fn
, lang
);
2720 res
= ast_waitstream(chan
, ints
);
2723 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2724 res
= ast_streamfile(chan
, fn
, lang
);
2726 res
= ast_waitstream(chan
, ints
);
2729 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2731 res
= ast_waitstream(chan
, ints
);
2733 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2738 int ast_say_date_da(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2743 ast_localtime(&t
,&tm
,NULL
);
2745 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2746 res
= ast_streamfile(chan
, fn
, lang
);
2748 res
= ast_waitstream(chan
, ints
);
2751 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2753 res
= ast_waitstream(chan
, ints
);
2755 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2756 res
= ast_streamfile(chan
, fn
, lang
);
2758 res
= ast_waitstream(chan
, ints
);
2762 int year
= tm
.tm_year
+ 1900;
2763 if (year
> 1999) { /* year 2000 and later */
2764 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2767 /* I'm not going to handle 1100 and prior */
2768 /* We'll just be silent on the year, instead of bombing out. */
2770 /* year 1100 to 1999. will anybody need this?!? */
2771 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2772 res
= wait_file(chan
, ints
, fn
, lang
);
2774 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2775 if (!res
&& year
% 100 != 0) {
2776 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
2786 int ast_say_date_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2791 ast_localtime(&t
,&tm
,NULL
);
2793 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2794 res
= ast_streamfile(chan
, fn
, lang
);
2796 res
= ast_waitstream(chan
, ints
);
2799 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2801 res
= ast_waitstream(chan
, ints
);
2803 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2804 res
= ast_streamfile(chan
, fn
, lang
);
2806 res
= ast_waitstream(chan
, ints
);
2810 int year
= tm
.tm_year
+ 1900;
2811 if (year
> 1999) { /* year 2000 and later */
2812 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2815 /* I'm not going to handle 1100 and prior */
2816 /* We'll just be silent on the year, instead of bombing out. */
2818 /* year 1100 to 1999. will anybody need this?!? */
2819 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2820 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2821 res
= wait_file(chan
, ints
, fn
, lang
);
2823 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2824 if (!res
&& year
% 100 != 0) {
2825 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
2835 int ast_say_date_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2840 ast_localtime(&t
,&tm
,NULL
);
2842 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2843 res
= ast_streamfile(chan
, fn
, lang
);
2845 res
= ast_waitstream(chan
, ints
);
2848 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2850 res
= ast_waitstream(chan
, ints
);
2852 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2853 res
= ast_streamfile(chan
, fn
, lang
);
2855 res
= ast_waitstream(chan
, ints
);
2858 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2863 int ast_say_date_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2868 ast_localtime(&t
,&tm
,NULL
);
2870 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2871 res
= ast_streamfile(chan
, fn
, lang
);
2873 res
= ast_waitstream(chan
, ints
);
2876 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2878 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2879 res
= ast_streamfile(chan
, fn
, lang
);
2881 res
= ast_waitstream(chan
, ints
);
2884 res
= ast_waitstream(chan
, ints
);
2886 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2890 /* Portuguese syntax */
2891 int ast_say_date_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2896 ast_localtime(&t
,&tm
,NULL
);
2897 localtime_r(&t
,&tm
);
2898 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2900 res
= wait_file(chan
, ints
, fn
, lang
);
2902 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
2904 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
2905 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2907 res
= wait_file(chan
, ints
, fn
, lang
);
2909 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
2911 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2916 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
)
2918 if (!strcasecmp(lang
, "en") ) { /* English syntax */
2919 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
2920 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
2921 return(ast_say_date_with_format_da(chan
, time
, ints
, lang
, format
, timezone
));
2922 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
2923 return(ast_say_date_with_format_de(chan
, time
, ints
, lang
, format
, timezone
));
2924 } else if (!strcasecmp(lang
, "es") || !strcasecmp(lang
, "mx")) { /* Spanish syntax */
2925 return(ast_say_date_with_format_es(chan
, time
, ints
, lang
, format
, timezone
));
2926 } else if (!strcasecmp(lang
, "he")) { /* Hebrew syntax */
2927 return(ast_say_date_with_format_he(chan
, time
, ints
, lang
, format
, timezone
));
2928 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
2929 return(ast_say_date_with_format_fr(chan
, time
, ints
, lang
, format
, timezone
));
2930 } else if (!strcasecmp(lang
, "it") ) { /* Italian syntax */
2931 return(ast_say_date_with_format_it(chan
, time
, ints
, lang
, format
, timezone
));
2932 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
2933 return(ast_say_date_with_format_nl(chan
, time
, ints
, lang
, format
, timezone
));
2934 } else if (!strcasecmp(lang
, "pl") ) { /* Polish syntax */
2935 return(ast_say_date_with_format_pl(chan
, time
, ints
, lang
, format
, timezone
));
2936 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
2937 return(ast_say_date_with_format_pt(chan
, time
, ints
, lang
, format
, timezone
));
2938 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
2939 return(ast_say_date_with_format_tw(chan
, time
, ints
, lang
, format
, timezone
));
2940 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
2941 return(ast_say_date_with_format_gr(chan
, time
, ints
, lang
, format
, timezone
));
2944 /* Default to English */
2945 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
2948 /* English syntax */
2949 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
)
2952 int res
=0, offset
, sndoffset
;
2953 char sndfile
[256], nextmsg
[256];
2956 format
= "ABdY 'digits/at' IMp";
2958 ast_localtime(&time
,&tm
,timezone
);
2960 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
2961 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
2962 switch (format
[offset
]) {
2963 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2965 /* Literal name of a sound file */
2967 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
2968 sndfile
[sndoffset
] = format
[offset
];
2969 sndfile
[sndoffset
] = '\0';
2970 res
= wait_file(chan
,ints
,sndfile
,lang
);
2974 /* Sunday - Saturday */
2975 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
2976 res
= wait_file(chan
,ints
,nextmsg
,lang
);
2981 /* January - December */
2982 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
2983 res
= wait_file(chan
,ints
,nextmsg
,lang
);
2986 /* Month enumerated */
2987 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, (char *) NULL
);
2991 /* First - Thirtyfirst */
2992 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
2996 if (tm
.tm_year
> 99) {
2997 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2998 } else if (tm
.tm_year
< 1) {
2999 /* I'm not going to handle 1900 and prior */
3000 /* We'll just be silent on the year, instead of bombing out. */
3002 res
= wait_file(chan
, ints
, "digits/19", lang
);
3004 if (tm
.tm_year
<= 9) {
3006 res
= wait_file(chan
,ints
, "digits/oh", lang
);
3009 res
|= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char *) NULL
);
3016 if (tm
.tm_hour
== 0)
3017 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3018 else if (tm
.tm_hour
> 12)
3019 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3021 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3022 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3027 if (format
[offset
] == 'H') {
3029 if (tm
.tm_hour
< 10) {
3030 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3034 if (tm
.tm_hour
== 0) {
3035 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3039 if (tm
.tm_hour
!= 0) {
3040 int remainder
= tm
.tm_hour
;
3041 if (tm
.tm_hour
> 20) {
3042 res
= wait_file(chan
,ints
, "digits/20",lang
);
3046 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
3047 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3055 if (tm
.tm_min
== 0) {
3056 if (format
[offset
] == 'M') {
3057 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
3059 res
= wait_file(chan
, ints
, "digits/hundred", lang
);
3061 } else if (tm
.tm_min
< 10) {
3062 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3064 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
3065 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3068 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
3074 if (tm
.tm_hour
> 11)
3075 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3077 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3078 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3081 /* Shorthand for "Today", "Yesterday", or ABdY */
3082 /* XXX As emphasized elsewhere, this should the native way in your
3083 * language to say the date, with changes in what you say, depending
3084 * upon how recent the date is. XXX */
3088 time_t beg_today
, tt
;
3090 gettimeofday(&now
,NULL
);
3092 ast_localtime(&tt
,&tmnow
,timezone
);
3093 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3094 /* In any case, it saves not having to do ast_mktime() */
3095 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3096 if (beg_today
< time
) {
3098 res
= wait_file(chan
,ints
, "digits/today",lang
);
3099 } else if (beg_today
- 86400 < time
) {
3101 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3102 } else if (beg_today
- 86400 * 6 < time
) {
3103 /* Within the last week */
3104 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3105 } else if (beg_today
- 2628000 < time
) {
3106 /* Less than a month ago - "Sunday, October third" */
3107 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3108 } else if (beg_today
- 15768000 < time
) {
3109 /* Less than 6 months ago - "August seventh" */
3110 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3112 /* More than 6 months ago - "April nineteenth two thousand three" */
3113 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3118 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3119 /* XXX As emphasized elsewhere, this should the native way in your
3120 * language to say the date, with changes in what you say, depending
3121 * upon how recent the date is. XXX */
3125 time_t beg_today
, tt
;
3127 gettimeofday(&now
,NULL
);
3129 ast_localtime(&tt
,&tmnow
,timezone
);
3130 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3131 /* In any case, it saves not having to do ast_mktime() */
3132 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3133 if (beg_today
< time
) {
3135 } else if ((beg_today
- 86400) < time
) {
3137 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3138 } else if (beg_today
- 86400 * 6 < time
) {
3139 /* Within the last week */
3140 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3141 } else if (beg_today
- 2628000 < time
) {
3142 /* Less than a month ago - "Sunday, October third" */
3143 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3144 } else if (beg_today
- 15768000 < time
) {
3145 /* Less than 6 months ago - "August seventh" */
3146 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3148 /* More than 6 months ago - "April nineteenth two thousand three" */
3149 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3154 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HM", timezone
);
3158 if (tm
.tm_sec
== 0) {
3159 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3160 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3161 } else if (tm
.tm_sec
< 10) {
3162 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3164 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3165 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3168 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, (char *) NULL
);
3172 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HMS", timezone
);
3176 /* Just ignore spaces and tabs */
3179 /* Unknown character */
3180 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3182 /* Jump out on DTMF */
3191 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
)
3194 int res
=0, offset
, sndoffset
;
3195 char sndfile
[256], nextmsg
[256];
3198 format
= "A dBY HMS";
3200 ast_localtime(&time
,&tm
,timezone
);
3202 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3203 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3204 switch (format
[offset
]) {
3205 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3207 /* Literal name of a sound file */
3209 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3210 sndfile
[sndoffset
] = format
[offset
];
3211 sndfile
[sndoffset
] = '\0';
3212 res
= wait_file(chan
,ints
,sndfile
,lang
);
3216 /* Sunday - Saturday */
3217 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3218 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3223 /* January - December */
3224 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3225 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3228 /* Month enumerated */
3229 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3233 /* First - Thirtyfirst */
3234 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3239 int year
= tm
.tm_year
+ 1900;
3240 if (year
> 1999) { /* year 2000 and later */
3241 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3244 /* I'm not going to handle 1100 and prior */
3245 /* We'll just be silent on the year, instead of bombing out. */
3247 /* year 1100 to 1999. will anybody need this?!? */
3248 /* say 1967 as 'nineteen hundred seven and sixty' */
3249 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3250 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3252 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3253 if (!res
&& year
% 100 != 0) {
3254 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3264 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3265 if (tm
.tm_hour
== 0)
3266 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3267 else if (tm
.tm_hour
> 12)
3268 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3270 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3272 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3276 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3277 if (tm
.tm_hour
< 10 && tm
.tm_hour
> 0) {
3278 res
= wait_file(chan
,ints
, "digits/0",lang
);
3283 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3287 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3288 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3290 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3291 if (tm
.tm_min
== 1) {
3292 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3294 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3301 if (tm
.tm_hour
> 11)
3302 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3304 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3305 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3308 /* Shorthand for "Today", "Yesterday", or AdBY */
3309 /* XXX As emphasized elsewhere, this should the native way in your
3310 * language to say the date, with changes in what you say, depending
3311 * upon how recent the date is. XXX */
3315 time_t beg_today
, tt
;
3317 gettimeofday(&now
,NULL
);
3319 ast_localtime(&tt
,&tmnow
,timezone
);
3320 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3321 /* In any case, it saves not having to do ast_mktime() */
3322 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3323 if (beg_today
< time
) {
3325 res
= wait_file(chan
,ints
, "digits/today",lang
);
3326 } else if (beg_today
- 86400 < time
) {
3328 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3330 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3335 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3336 /* XXX As emphasized elsewhere, this should the native way in your
3337 * language to say the date, with changes in what you say, depending
3338 * upon how recent the date is. XXX */
3342 time_t beg_today
, tt
;
3344 gettimeofday(&now
,NULL
);
3346 ast_localtime(&tt
,&tmnow
,timezone
);
3347 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3348 /* In any case, it saves not having to do ast_mktime() */
3349 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3350 if (beg_today
< time
) {
3352 } else if ((beg_today
- 86400) < time
) {
3354 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3355 } else if (beg_today
- 86400 * 6 < time
) {
3356 /* Within the last week */
3357 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "A", timezone
);
3359 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3364 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HM", timezone
);
3368 res
= wait_file(chan
,ints
, "digits/and",lang
);
3370 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3372 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3377 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HMS", timezone
);
3381 /* Just ignore spaces and tabs */
3384 /* Unknown character */
3385 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3387 /* Jump out on DTMF */
3396 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
)
3399 int res
=0, offset
, sndoffset
;
3400 char sndfile
[256], nextmsg
[256];
3403 format
= "A dBY HMS";
3405 ast_localtime(&time
,&tm
,timezone
);
3407 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3408 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3409 switch (format
[offset
]) {
3410 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3412 /* Literal name of a sound file */
3414 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3415 sndfile
[sndoffset
] = format
[offset
];
3416 sndfile
[sndoffset
] = '\0';
3417 res
= wait_file(chan
,ints
,sndfile
,lang
);
3421 /* Sunday - Saturday */
3422 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3423 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3428 /* January - December */
3429 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3430 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3433 /* Month enumerated */
3434 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3438 /* First - Thirtyfirst */
3439 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3444 int year
= tm
.tm_year
+ 1900;
3445 if (year
> 1999) { /* year 2000 and later */
3446 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3449 /* I'm not going to handle 1100 and prior */
3450 /* We'll just be silent on the year, instead of bombing out. */
3452 /* year 1100 to 1999. will anybody need this?!? */
3453 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3454 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3455 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3457 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3458 if (!res
&& year
% 100 != 0) {
3459 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3469 if (tm
.tm_hour
== 0)
3470 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3471 else if (tm
.tm_hour
> 12)
3472 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3474 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3475 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3477 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3483 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3485 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3490 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3491 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3493 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3494 if (tm
.tm_min
== 1) {
3495 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3497 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3504 if (tm
.tm_hour
> 11)
3505 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3507 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3508 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3511 /* Shorthand for "Today", "Yesterday", or AdBY */
3512 /* XXX As emphasized elsewhere, this should the native way in your
3513 * language to say the date, with changes in what you say, depending
3514 * upon how recent the date is. XXX */
3518 time_t beg_today
, tt
;
3520 gettimeofday(&now
,NULL
);
3522 ast_localtime(&tt
,&tmnow
,timezone
);
3523 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3524 /* In any case, it saves not having to do ast_mktime() */
3525 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3526 if (beg_today
< time
) {
3528 res
= wait_file(chan
,ints
, "digits/today",lang
);
3529 } else if (beg_today
- 86400 < time
) {
3531 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3533 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3538 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3539 /* XXX As emphasized elsewhere, this should the native way in your
3540 * language to say the date, with changes in what you say, depending
3541 * upon how recent the date is. XXX */
3545 time_t beg_today
, tt
;
3547 gettimeofday(&now
,NULL
);
3549 ast_localtime(&tt
,&tmnow
,timezone
);
3550 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3551 /* In any case, it saves not having to do ast_mktime() */
3552 beg_today
= now
.tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3553 if (beg_today
< time
) {
3555 } else if ((beg_today
- 86400) < time
) {
3557 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3558 } else if (beg_today
- 86400 * 6 < time
) {
3559 /* Within the last week */
3560 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "A", timezone
);
3562 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3567 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HM", timezone
);
3571 res
= wait_file(chan
,ints
, "digits/and",lang
);
3573 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3575 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3580 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HMS", timezone
);
3584 /* Just ignore spaces and tabs */
3587 /* Unknown character */
3588 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3590 /* Jump out on DTMF */
3598 /* TODO: this probably is not the correct format for doxygen remarks */
3600 /** ast_say_date_with_format_he Say formmated date in Hebrew
3602 * \ref ast_say_date_with_format_en for the details of the options
3604 * Changes from the English version:
3606 * * don't replicate in here the logic of ast_say_number_full_he
3608 * * year is always 4-digit (because it's simpler)
3610 * * added c, x, and X. Mainly for my tests
3612 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3615 * * A "ha" is missing in the standard date format, before the 'd'.
3616 * * The numbers of 3000--19000 are not handled well
3618 #define IL_DATE_STR "AdBY"
3619 #define IL_TIME_STR "IMp"
3620 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3621 int ast_say_date_with_format_he(struct ast_channel
*chan
, time_t time
,
3622 const char *ints
, const char *lang
, const char *format
,
3623 const char *timezone
)
3625 /* TODO: This whole function is cut&paste from
3626 * ast_say_date_with_format_en . Is that considered acceptable?
3629 int res
=0, offset
, sndoffset
;
3630 char sndfile
[256], nextmsg
[256];
3633 format
= IL_DATE_STR_FULL
;
3635 ast_localtime(&time
,&tm
,timezone
);
3637 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3638 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3639 switch (format
[offset
]) {
3640 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3642 /* Literal name of a sound file */
3644 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3645 sndfile
[sndoffset
] = format
[offset
];
3646 sndfile
[sndoffset
] = '\0';
3647 res
= wait_file(chan
,ints
,sndfile
,lang
);
3651 /* Sunday - Saturday */
3652 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3653 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3658 /* January - December */
3659 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3660 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3663 case 'e': /* Day of the month */
3664 /* I'm not sure exactly what the parameters
3665 * audiofd and ctrlfd to
3666 * ast_say_number_full_he mean, but it seems
3667 * safe to pass -1 there.
3669 * At least in one of the pathes :-(
3671 res
= ast_say_number_full_he(chan
, tm
.tm_mday
,
3672 ints
, lang
, "m", -1, -1
3675 case 'Y': /* Year */
3676 res
= ast_say_number_full_he(chan
, tm
.tm_year
+1900,
3677 ints
, lang
, "f", -1, -1
3681 case 'l': /* 12-Hour */
3683 int hour
= tm
.tm_hour
;
3685 if (hour
== 0) hour
=12;
3687 res
= ast_say_number_full_he(chan
, hour
,
3688 ints
, lang
, "f", -1, -1
3693 case 'k': /* 24-Hour */
3694 /* With 'H' there is an 'oh' after a single-
3696 if ((format
[offset
] == 'H') &&
3697 (tm
.tm_hour
<10)&&(tm
.tm_hour
>0)
3698 ) { /* e.g. oh-eight */
3699 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3702 res
= ast_say_number_full_he(chan
, tm
.tm_hour
,
3703 ints
, lang
, "f", -1, -1
3706 case 'M': /* Minute */
3707 res
= ast_say_number_full_he(chan
, tm
.tm_min
,
3708 ints
, lang
,"f", -1, -1
3714 if (tm
.tm_hour
> 11)
3715 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3717 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3718 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3721 /* Shorthand for "Today", "Yesterday", or "date" */
3723 /* Shorthand for "" (today), "Yesterday", A
3724 * (weekday), or "date" */
3725 /* XXX As emphasized elsewhere, this should the native way in your
3726 * language to say the date, with changes in what you say, depending
3727 * upon how recent the date is. XXX */
3731 time_t beg_today
, tt
;
3732 char todo
= format
[offset
]; /* The letter to format*/
3734 gettimeofday(&now
,NULL
);
3736 ast_localtime(&tt
,&tmnow
,timezone
);
3737 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3738 /* In any case, it saves not having to do ast_mktime() */
3739 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3740 if (beg_today
< time
) {
3743 res
= wait_file(chan
,
3748 } else if (beg_today
- 86400 < time
) {
3750 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3751 } else if ((todo
!= 'Q') &&
3752 (beg_today
- 86400 * 6 < time
))
3754 /* Within the last week */
3755 res
= ast_say_date_with_format_he(chan
,
3759 res
= ast_say_date_with_format_he(chan
,
3761 IL_DATE_STR
, timezone
);
3766 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HM", timezone
);
3768 case 'S': /* Seconds */
3769 res
= ast_say_number_full_he(chan
, tm
.tm_sec
,
3770 ints
, lang
, "f", -1, -1
3774 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HMS", timezone
);
3776 /* c, x, and X seem useful for testing. Not sure
3777 * if thiey're good for the general public */
3779 res
= ast_say_date_with_format_he(chan
, time
,
3780 ints
, lang
, IL_DATE_STR_FULL
, timezone
);
3783 res
= ast_say_date_with_format_he(chan
, time
,
3784 ints
, lang
, IL_DATE_STR
, timezone
);
3786 case 'X': /* Currently not locale-dependent...*/
3787 res
= ast_say_date_with_format_he(chan
, time
,
3788 ints
, lang
, IL_TIME_STR
, timezone
);
3792 /* Just ignore spaces and tabs */
3795 /* Unknown character */
3796 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3798 /* Jump out on DTMF */
3807 /* Spanish syntax */
3808 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
)
3811 int res
=0, offset
, sndoffset
;
3812 char sndfile
[256], nextmsg
[256];
3815 format
= "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
3817 ast_localtime(&time
,&tm
,timezone
);
3819 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3820 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3821 switch (format
[offset
]) {
3822 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3824 /* Literal name of a sound file */
3826 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3827 sndfile
[sndoffset
] = format
[offset
];
3828 sndfile
[sndoffset
] = '\0';
3829 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
3830 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3834 /* Sunday - Saturday */
3835 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3836 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3841 /* January - December */
3842 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3843 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3846 /* First - Twelfth */
3847 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
3848 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3852 /* First - Thirtyfirst */
3853 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
3857 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
3862 if (tm
.tm_hour
== 0)
3863 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3864 else if (tm
.tm_hour
> 12)
3865 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3867 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3868 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3873 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, NULL
);
3877 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
3882 if (tm
.tm_hour
> 18)
3883 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
3884 else if (tm
.tm_hour
> 12)
3885 res
= wait_file(chan
, ints
, "digits/afternoon", lang
);
3886 else if (tm
.tm_hour
)
3887 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
3890 /* Shorthand for "Today", "Yesterday", or ABdY */
3891 /* XXX As emphasized elsewhere, this should the native way in your
3892 * language to say the date, with changes in what you say, depending
3893 * upon how recent the date is. XXX */
3897 time_t beg_today
, tt
;
3899 gettimeofday(&now
,NULL
);
3901 ast_localtime(&tt
,&tmnow
,timezone
);
3902 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3903 /* In any case, it saves not having to do ast_mktime() */
3904 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3905 if (beg_today
< time
) {
3907 res
= wait_file(chan
,ints
, "digits/today",lang
);
3908 } else if (beg_today
- 86400 < time
) {
3910 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3912 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
3917 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3918 /* XXX As emphasized elsewhere, this should the native way in your
3919 * language to say the date, with changes in what you say, depending
3920 * upon how recent the date is. XXX */
3924 time_t beg_today
, tt
;
3926 gettimeofday(&now
,NULL
);
3928 ast_localtime(&tt
,&tmnow
,timezone
);
3929 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3930 /* In any case, it saves not having to do ast_mktime() */
3931 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3932 if (beg_today
< time
) {
3934 res
= wait_file(chan
,ints
, "digits/today",lang
);
3935 } else if ((beg_today
- 86400) < time
) {
3937 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3938 } else if (beg_today
- 86400 * 6 < time
) {
3939 /* Within the last week */
3940 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "A", timezone
);
3942 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
3947 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "H 'digits/y' M", timezone
);
3951 if (tm
.tm_sec
== 0) {
3952 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3953 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3954 } else if (tm
.tm_sec
< 10) {
3955 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3957 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3958 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3960 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
3961 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3962 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3965 ten
= (tm
.tm_sec
/ 10) * 10;
3966 one
= (tm
.tm_sec
% 10);
3967 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
3968 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3970 /* Fifty, not fifty-zero */
3972 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
3973 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3979 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "HMS", timezone
);
3983 /* Just ignore spaces and tabs */
3986 /* Unknown character */
3987 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3989 /* Jump out on DTMF */
4000 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
)
4003 int res
=0, offset
, sndoffset
;
4004 char sndfile
[256], nextmsg
[256];
4007 format
= "AdBY 'digits/at' IMp";
4009 ast_localtime(&time
,&tm
,timezone
);
4011 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4012 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4013 switch (format
[offset
]) {
4014 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4016 /* Literal name of a sound file */
4018 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4019 sndfile
[sndoffset
] = format
[offset
];
4020 sndfile
[sndoffset
] = '\0';
4021 res
= wait_file(chan
,ints
,sndfile
,lang
);
4025 /* Sunday - Saturday */
4026 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4027 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4032 /* January - December */
4033 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4034 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4037 /* First - Twelfth */
4038 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4039 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4044 if (tm
.tm_mday
== 1) {
4045 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4046 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4048 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
4053 if (tm
.tm_year
> 99) {
4054 res
= wait_file(chan
,ints
, "digits/2",lang
);
4056 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4058 if (tm
.tm_year
> 100) {
4060 res
= ast_say_number(chan
, tm
.tm_year
- 100, ints
, lang
, (char * ) NULL
);
4064 if (tm
.tm_year
< 1) {
4065 /* I'm not going to handle 1900 and prior */
4066 /* We'll just be silent on the year, instead of bombing out. */
4068 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4070 wait_file(chan
,ints
, "digits/9",lang
);
4071 wait_file(chan
,ints
, "digits/hundred",lang
);
4072 res
= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char * ) NULL
);
4080 if (tm
.tm_hour
== 0)
4081 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4082 else if (tm
.tm_hour
> 12)
4083 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4085 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4086 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4088 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4093 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char * ) NULL
);
4095 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4099 if (tm
.tm_min
== 0) {
4102 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char * ) NULL
);
4107 if (tm
.tm_hour
> 11)
4108 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4110 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4111 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4114 /* Shorthand for "Today", "Yesterday", or AdBY */
4115 /* XXX As emphasized elsewhere, this should the native way in your
4116 * language to say the date, with changes in what you say, depending
4117 * upon how recent the date is. XXX */
4121 time_t beg_today
, tt
;
4123 gettimeofday(&now
,NULL
);
4125 ast_localtime(&tt
,&tmnow
,timezone
);
4126 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4127 /* In any case, it saves not having to do ast_mktime() */
4128 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4129 if (beg_today
< time
) {
4131 res
= wait_file(chan
,ints
, "digits/today",lang
);
4132 } else if (beg_today
- 86400 < time
) {
4134 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4136 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4141 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4142 /* XXX As emphasized elsewhere, this should the native way in your
4143 * language to say the date, with changes in what you say, depending
4144 * upon how recent the date is. XXX */
4148 time_t beg_today
, tt
;
4150 gettimeofday(&now
,NULL
);
4152 ast_localtime(&tt
,&tmnow
,timezone
);
4153 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4154 /* In any case, it saves not having to do ast_mktime() */
4155 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4156 if (beg_today
< time
) {
4158 } else if ((beg_today
- 86400) < time
) {
4160 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4161 } else if (beg_today
- 86400 * 6 < time
) {
4162 /* Within the last week */
4163 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "A", timezone
);
4165 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4170 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HM", timezone
);
4174 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char * ) NULL
);
4176 res
= wait_file(chan
,ints
, "digits/second",lang
);
4180 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HMS", timezone
);
4184 /* Just ignore spaces and tabs */
4187 /* Unknown character */
4188 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4190 /* Jump out on DTMF */
4198 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
)
4201 int res
=0, offset
, sndoffset
;
4202 char sndfile
[256], nextmsg
[256];
4205 format
= "AdB 'digits/at' IMp";
4207 ast_localtime(&time
,&tm
,timezone
);
4209 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4210 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4211 switch (format
[offset
]) {
4212 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4214 /* Literal name of a sound file */
4216 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4217 sndfile
[sndoffset
] = format
[offset
];
4218 sndfile
[sndoffset
] = '\0';
4219 res
= wait_file(chan
,ints
,sndfile
,lang
);
4223 /* Sunday - Saturday */
4224 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4225 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4230 /* January - December */
4231 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4232 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4235 /* First - Twelfth */
4236 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4237 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4241 /* First day of the month is spelled as ordinal */
4242 if (tm
.tm_mday
== 1) {
4243 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4244 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4247 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4253 if (tm
.tm_year
> 99) {
4254 res
= wait_file(chan
,ints
, "digits/ore-2000",lang
);
4255 if (tm
.tm_year
> 100) {
4257 /* This works until the end of 2021 */
4258 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4259 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4263 if (tm
.tm_year
< 1) {
4264 /* I'm not going to handle 1900 and prior */
4265 /* We'll just be silent on the year, instead of bombing out. */
4267 res
= wait_file(chan
,ints
, "digits/ore-1900",lang
);
4268 if ((!res
) && (tm
.tm_year
!= 0)) {
4269 if (tm
.tm_year
<= 21) {
4271 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4272 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4274 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4276 ten
= tm
.tm_year
/ 10;
4277 one
= tm
.tm_year
% 10;
4278 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4279 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4282 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4283 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4294 if (tm
.tm_hour
== 0)
4295 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4296 else if (tm
.tm_hour
> 12)
4297 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4299 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4300 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4305 if (tm
.tm_hour
== 0) {
4306 res
= wait_file(chan
,ints
, "digits/ore-mezzanotte",lang
);
4307 } else if (tm
.tm_hour
== 1) {
4308 res
= wait_file(chan
,ints
, "digits/ore-una",lang
);
4310 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4315 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4320 if (tm
.tm_hour
> 11)
4321 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4323 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4324 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4327 /* Shorthand for "Today", "Yesterday", or ABdY */
4328 /* XXX As emphasized elsewhere, this should the native way in your
4329 * language to say the date, with changes in what you say, depending
4330 * upon how recent the date is. XXX */
4334 time_t beg_today
, tt
;
4336 gettimeofday(&now
,NULL
);
4338 ast_localtime(&tt
,&tmnow
,timezone
);
4339 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4340 /* In any case, it saves not having to do ast_mktime() */
4341 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4342 if (beg_today
< time
) {
4344 res
= wait_file(chan
,ints
, "digits/today",lang
);
4345 } else if (beg_today
- 86400 < time
) {
4347 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4349 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4354 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4358 time_t beg_today
, tt
;
4360 gettimeofday(&now
,NULL
);
4362 ast_localtime(&tt
,&tmnow
,timezone
);
4363 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4364 /* In any case, it saves not having to do ast_mktime() */
4365 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4366 if (beg_today
< time
) {
4368 } else if ((beg_today
- 86400) < time
) {
4370 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4371 } else if (beg_today
- 86400 * 6 < time
) {
4372 /* Within the last week */
4373 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "A", timezone
);
4375 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4380 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HM", timezone
);
4384 if (tm
.tm_sec
== 0) {
4385 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4386 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4387 } else if (tm
.tm_sec
< 10) {
4388 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4390 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4391 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4393 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
4394 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4395 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4398 ten
= (tm
.tm_sec
/ 10) * 10;
4399 one
= (tm
.tm_sec
% 10);
4400 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
4401 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4403 /* Fifty, not fifty-zero */
4405 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4406 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4412 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HMS", timezone
);
4416 /* Just ignore spaces and tabs */
4419 /* Unknown character */
4420 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4422 /* Jump out on DTMF */
4431 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
)
4434 int res
=0, offset
, sndoffset
;
4435 char sndfile
[256], nextmsg
[256];
4438 format
= "ABdY 'digits/at' IMp";
4440 ast_localtime(&time
,&tm
,timezone
);
4442 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4443 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4444 switch (format
[offset
]) {
4445 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4447 /* Literal name of a sound file */
4449 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4450 sndfile
[sndoffset
] = format
[offset
];
4451 sndfile
[sndoffset
] = '\0';
4452 res
= wait_file(chan
,ints
,sndfile
,lang
);
4456 /* Sunday - Saturday */
4457 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4458 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4463 /* January - December */
4464 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4465 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4468 /* First - Twelfth */
4469 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4470 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4474 /* First - Thirtyfirst */
4475 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, NULL
);
4479 if (tm
.tm_year
> 99) {
4480 res
= wait_file(chan
,ints
, "digits/2",lang
);
4482 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4484 if (tm
.tm_year
> 100) {
4486 /* This works until the end of 2020 */
4487 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4488 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4492 if (tm
.tm_year
< 1) {
4493 /* I'm not going to handle 1900 and prior */
4494 /* We'll just be silent on the year, instead of bombing out. */
4496 res
= wait_file(chan
,ints
, "digits/19",lang
);
4498 if (tm
.tm_year
<= 9) {
4500 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4502 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4503 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4505 } else if (tm
.tm_year
<= 20) {
4507 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4508 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4512 ten
= tm
.tm_year
/ 10;
4513 one
= tm
.tm_year
% 10;
4514 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4515 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4518 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4519 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4530 if (tm
.tm_hour
== 0)
4531 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4532 else if (tm
.tm_hour
> 12)
4533 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4535 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4536 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4541 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4543 res
= wait_file(chan
,ints
, "digits/nl-uur",lang
);
4548 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4553 if (tm
.tm_hour
> 11)
4554 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4556 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4557 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4560 /* Shorthand for "Today", "Yesterday", or ABdY */
4561 /* XXX As emphasized elsewhere, this should the native way in your
4562 * language to say the date, with changes in what you say, depending
4563 * upon how recent the date is. XXX */
4567 time_t beg_today
, tt
;
4569 gettimeofday(&now
,NULL
);
4571 ast_localtime(&tt
,&tmnow
,timezone
);
4572 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4573 /* In any case, it saves not having to do ast_mktime() */
4574 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4575 if (beg_today
< time
) {
4577 res
= wait_file(chan
,ints
, "digits/today",lang
);
4578 } else if (beg_today
- 86400 < time
) {
4580 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4582 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4587 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4591 time_t beg_today
, tt
;
4593 gettimeofday(&now
,NULL
);
4595 ast_localtime(&tt
,&tmnow
,timezone
);
4596 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4597 /* In any case, it saves not having to do ast_mktime() */
4598 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4599 if (beg_today
< time
) {
4601 } else if ((beg_today
- 86400) < time
) {
4603 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4604 } else if (beg_today
- 86400 * 6 < time
) {
4605 /* Within the last week */
4606 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "A", timezone
);
4608 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4613 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HM", timezone
);
4617 if (tm
.tm_sec
== 0) {
4618 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4619 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4620 } else if (tm
.tm_sec
< 10) {
4621 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4623 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4624 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4626 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
4627 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4628 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4631 ten
= (tm
.tm_sec
/ 10) * 10;
4632 one
= (tm
.tm_sec
% 10);
4633 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
4634 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4636 /* Fifty, not fifty-zero */
4638 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4639 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4645 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HMS", timezone
);
4649 /* Just ignore spaces and tabs */
4652 /* Unknown character */
4653 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4655 /* Jump out on DTMF */
4664 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
)
4667 int res
=0, offset
, sndoffset
;
4668 char sndfile
[256], nextmsg
[256];
4670 ast_localtime(&thetime
, &tm
, timezone
);
4672 for (offset
= 0 ; format
[offset
] != '\0' ; offset
++) {
4674 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4675 switch (format
[offset
]) {
4676 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4678 /* Literal name of a sound file */
4680 for (sndoffset
= 0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4681 sndfile
[sndoffset
] = format
[offset
];
4682 sndfile
[sndoffset
] = '\0';
4683 res
= wait_file(chan
, ints
, sndfile
, lang
);
4687 /* Sunday - Saturday */
4688 snprintf(nextmsg
, sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4689 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4694 /* January - December */
4695 snprintf(nextmsg
, sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4696 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4699 /* Month enumerated */
4700 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, NULL
);
4704 /* First - Thirtyfirst */
4705 remainder
= tm
.tm_mday
;
4706 if (tm
.tm_mday
> 30) {
4707 res
= wait_file(chan
, ints
, "digits/h-30", lang
);
4710 if (tm
.tm_mday
> 20 && tm
.tm_mday
< 30) {
4711 res
= wait_file(chan
, ints
, "digits/h-20", lang
);
4715 snprintf(nextmsg
, sizeof(nextmsg
), "digits/h-%d", remainder
);
4716 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4721 if (tm
.tm_year
> 100) {
4722 res
= wait_file(chan
, ints
, "digits/2", lang
);
4724 res
= wait_file(chan
, ints
, "digits/1000.2",lang
);
4725 if (tm
.tm_year
> 100) {
4727 res
= ast_say_enumeration(chan
, tm
.tm_year
- 100, ints
, lang
, NULL
);
4729 } else if (tm
.tm_year
== 100) {
4730 res
= wait_file(chan
, ints
, "digits/h-2000", lang
);
4732 if (tm
.tm_year
< 1) {
4733 /* I'm not going to handle 1900 and prior */
4734 /* We'll just be silent on the year, instead of bombing out. */
4737 res
= wait_file(chan
, ints
, "digits/1000", lang
);
4739 wait_file(chan
, ints
, "digits/900", lang
);
4740 res
= ast_say_enumeration(chan
, tm
.tm_year
, ints
, lang
, NULL
);
4745 wait_file(chan
, ints
, "digits/year", lang
);
4750 if (tm
.tm_hour
== 0)
4751 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-12");
4752 else if (tm
.tm_hour
> 12)
4753 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
- 12);
4755 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4757 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4762 if (tm
.tm_hour
!= 0) {
4763 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4764 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4766 res
= wait_file(chan
, ints
, "digits/t-24", lang
);
4771 if (tm
.tm_min
== 0) {
4772 if (format
[offset
] == 'M') {
4773 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
4775 res
= wait_file(chan
, ints
, "digits/100", lang
);
4778 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4783 if (tm
.tm_hour
> 11)
4784 snprintf(nextmsg
, sizeof(nextmsg
), "digits/p-m");
4786 snprintf(nextmsg
, sizeof(nextmsg
), "digits/a-m");
4787 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4790 /* Shorthand for "Today", "Yesterday", or AdBY */
4792 time_t tv_sec
= time(NULL
);
4796 ast_localtime(&tv_sec
,&tmnow
, timezone
);
4797 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4798 /* In any case, it saves not having to do ast_mktime() */
4799 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4800 if (beg_today
< thetime
) {
4802 res
= wait_file(chan
, ints
, "digits/today", lang
);
4803 } else if (beg_today
- 86400 < thetime
) {
4805 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4807 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4812 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4814 time_t tv_sec
= time(NULL
);
4818 ast_localtime(&tv_sec
, &tmnow
, timezone
);
4819 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4820 /* In any case, it saves not having to do ast_mktime() */
4821 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4822 if (beg_today
< thetime
) {
4824 } else if ((beg_today
- 86400) < thetime
) {
4826 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4827 } else if (beg_today
- 86400 * 6 < thetime
) {
4828 /* Within the last week */
4829 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "A", timezone
);
4831 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4836 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HM", timezone
);
4840 res
= wait_file(chan
, ints
, "digits/and", lang
);
4842 if (tm
.tm_sec
== 1) {
4843 res
= wait_file(chan
, ints
, "digits/1z", lang
);
4845 res
= wait_file(chan
, ints
, "digits/second-a", lang
);
4847 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4850 ten
= tm
.tm_sec
/ 10;
4851 one
= tm
.tm_sec
% 10;
4853 if (one
> 1 && one
< 5 && ten
!= 1)
4854 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
4856 res
= wait_file(chan
,ints
, "digits/second",lang
);
4862 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HMS", timezone
);
4866 /* Just ignore spaces and tabs */
4869 /* Unknown character */
4870 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4872 /* Jump out on DTMF */
4879 /* Portuguese syntax */
4880 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
)
4883 int res
=0, offset
, sndoffset
;
4884 char sndfile
[256], nextmsg
[256];
4887 format
= "Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/at' IMp";
4889 ast_localtime(&time
,&tm
,timezone
);
4891 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4892 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4893 switch (format
[offset
]) {
4894 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4896 /* Literal name of a sound file */
4898 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4899 sndfile
[sndoffset
] = format
[offset
];
4900 sndfile
[sndoffset
] = '\0';
4901 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
4902 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4906 /* Sunday - Saturday */
4907 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4908 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4913 /* January - December */
4914 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4915 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4918 /* First - Twelfth */
4919 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4920 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4924 /* First - Thirtyfirst */
4925 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4929 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
4934 if (tm
.tm_hour
== 0) {
4935 if (format
[offset
] == 'I')
4936 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
4938 res
= wait_file(chan
, ints
, "digits/pt-meianoite", lang
);
4940 else if (tm
.tm_hour
== 12) {
4941 if (format
[offset
] == 'I')
4942 res
= wait_file(chan
, ints
, "digits/pt-ao", lang
);
4944 res
= wait_file(chan
, ints
, "digits/pt-meiodia", lang
);
4947 if (format
[offset
] == 'I') {
4948 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
4949 if ((tm
.tm_hour
% 12) != 1)
4951 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
4954 res
= ast_say_number(chan
, (tm
.tm_hour
% 12), ints
, lang
, "f");
4960 res
= ast_say_number(chan
, -tm
.tm_hour
, ints
, lang
, NULL
);
4962 if (tm
.tm_hour
!= 0) {
4963 int remainder
= tm
.tm_hour
;
4964 if (tm
.tm_hour
> 20) {
4965 res
= wait_file(chan
,ints
, "digits/20",lang
);
4969 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
4970 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4977 if (tm
.tm_min
== 0) {
4978 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
4979 if (tm
.tm_hour
!= 1)
4981 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
); } else {
4982 res
= wait_file(chan
,ints
,"digits/pt-e",lang
);
4984 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4990 if (tm
.tm_hour
> 12)
4991 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
4992 else if (tm
.tm_hour
&& tm
.tm_hour
< 12)
4993 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
4996 /* Shorthand for "Today", "Yesterday", or ABdY */
4997 /* XXX As emphasized elsewhere, this should the native way in your
4998 * language to say the date, with changes in what you say, depending
4999 * upon how recent the date is. XXX */
5003 time_t beg_today
, tt
;
5005 gettimeofday(&now
,NULL
);
5007 ast_localtime(&tt
,&tmnow
,timezone
);
5008 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5009 /* In any case, it saves not having to do ast_mktime() */
5010 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5011 if (beg_today
< time
) {
5013 res
= wait_file(chan
,ints
, "digits/today",lang
);
5014 } else if (beg_today
- 86400 < time
) {
5016 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5018 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5023 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5024 /* XXX As emphasized elsewhere, this should the native way in your
5025 * language to say the date, with changes in what you say, depending
5026 * upon how recent the date is. XXX */
5030 time_t beg_today
, tt
;
5032 gettimeofday(&now
,NULL
);
5034 ast_localtime(&tt
,&tmnow
,timezone
);
5035 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5036 /* In any case, it saves not having to do ast_mktime() */
5037 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5038 if (beg_today
< time
) {
5040 } else if ((beg_today
- 86400) < time
) {
5042 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5043 } else if (beg_today
- 86400 * 6 < time
) {
5044 /* Within the last week */
5045 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "A", timezone
);
5047 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5052 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "H 'digits/pt-e' M", timezone
);
5056 if (tm
.tm_sec
== 0) {
5057 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5058 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5059 } else if (tm
.tm_sec
< 10) {
5060 res
= wait_file(chan
,ints
, "digits/oh",lang
);
5062 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5063 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5065 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
5066 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5067 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5070 ten
= (tm
.tm_sec
/ 10) * 10;
5071 one
= (tm
.tm_sec
% 10);
5072 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
5073 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5075 /* Fifty, not fifty-zero */
5077 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
5078 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5084 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "HMS", timezone
);
5088 /* Just ignore spaces and tabs */
5091 /* Unknown character */
5092 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5094 /* Jump out on DTMF */
5102 /* Taiwanese / Chinese syntax */
5103 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
)
5106 int res
=0, offset
, sndoffset
;
5107 char sndfile
[256], nextmsg
[256];
5110 format
= "YBdA 'digits/at' HM";
5112 ast_localtime(&time
,&tm
,timezone
);
5114 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
5115 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
5116 switch (format
[offset
]) {
5117 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5119 /* Literal name of a sound file */
5121 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
5122 sndfile
[sndoffset
] = format
[offset
];
5123 sndfile
[sndoffset
] = '\0';
5124 res
= wait_file(chan
,ints
,sndfile
,lang
);
5128 /* Sunday - Saturday */
5129 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
5130 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5135 /* January - December */
5136 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
5137 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5140 /* First - Twelfth */
5141 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
5142 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5146 /* First - Thirtyfirst */
5147 if (!(tm
.tm_mday
% 10) || (tm
.tm_mday
< 10)) {
5148 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
5149 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5151 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%dh", tm
.tm_mday
- (tm
.tm_mday
% 10));
5152 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5154 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
% 10);
5155 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5161 if (tm
.tm_year
> 99) {
5162 res
= wait_file(chan
,ints
, "digits/2",lang
);
5164 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
5166 if (tm
.tm_year
> 100) {
5168 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) / 10);
5169 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5171 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) % 10);
5172 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5177 res
= wait_file(chan
,ints
, "digits/year",lang
);
5180 if (tm
.tm_year
< 1) {
5181 /* I'm not going to handle 1900 and prior */
5182 /* We'll just be silent on the year, instead of bombing out. */
5184 res
= wait_file(chan
,ints
, "digits/1",lang
);
5186 res
= wait_file(chan
,ints
, "digits/9",lang
);
5189 if (tm
.tm_year
<= 9) {
5191 res
= wait_file(chan
,ints
, "digits/0",lang
);
5193 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
5194 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5198 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
/ 10);
5199 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5201 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
% 10);
5202 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5208 res
= wait_file(chan
,ints
, "digits/year",lang
);
5215 if (tm
.tm_hour
== 0)
5216 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
5217 else if (tm
.tm_hour
> 12)
5218 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
5220 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5221 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5223 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5229 if (!(tm
.tm_hour
% 10) || tm
.tm_hour
< 10) {
5230 if (tm
.tm_hour
< 10) {
5231 res
= wait_file(chan
, ints
, "digits/0", lang
);
5233 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5234 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5236 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- (tm
.tm_hour
% 10));
5237 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5239 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
% 10);
5240 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5244 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5249 if (!(tm
.tm_min
% 10) || tm
.tm_min
< 10) {
5250 if (tm
.tm_min
< 10) {
5251 res
= wait_file(chan
, ints
, "digits/0", lang
);
5253 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
5254 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5256 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
- (tm
.tm_min
% 10));
5257 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5259 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
% 10);
5260 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5264 res
= wait_file(chan
,ints
, "digits/minute",lang
);
5270 if (tm
.tm_hour
> 11)
5271 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
5273 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
5274 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5277 /* Shorthand for "Today", "Yesterday", or ABdY */
5278 /* XXX As emphasized elsewhere, this should the native way in your
5279 * language to say the date, with changes in what you say, depending
5280 * upon how recent the date is. XXX */
5284 time_t beg_today
, tt
;
5286 gettimeofday(&now
,NULL
);
5288 ast_localtime(&tt
,&tmnow
,timezone
);
5289 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5290 /* In any case, it saves not having to do ast_mktime() */
5291 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5292 if (beg_today
< time
) {
5294 res
= wait_file(chan
,ints
, "digits/today",lang
);
5295 } else if (beg_today
- 86400 < time
) {
5297 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5299 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5304 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5305 /* XXX As emphasized elsewhere, this should the native way in your
5306 * language to say the date, with changes in what you say, depending
5307 * upon how recent the date is. XXX */
5311 time_t beg_today
, tt
;
5313 gettimeofday(&now
,NULL
);
5315 ast_localtime(&tt
,&tmnow
,timezone
);
5316 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5317 /* In any case, it saves not having to do ast_mktime() */
5318 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5319 if (beg_today
< time
) {
5321 } else if ((beg_today
- 86400) < time
) {
5323 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5324 } else if (beg_today
- 86400 * 6 < time
) {
5325 /* Within the last week */
5326 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "A", timezone
);
5328 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5333 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "HM", timezone
);
5337 if (!(tm
.tm_sec
% 10) || tm
.tm_sec
< 10) {
5338 if (tm
.tm_sec
< 10) {
5339 res
= wait_file(chan
, ints
, "digits/0", lang
);
5341 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5342 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5344 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
- (tm
.tm_sec
% 10));
5345 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5347 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
% 10);
5348 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5352 res
= wait_file(chan
,ints
, "digits/second",lang
);
5356 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "HMS", timezone
);
5360 /* Just ignore spaces and tabs */
5363 /* Unknown character */
5364 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5366 /* Jump out on DTMF */
5374 static int say_time(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5376 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5377 return(ast_say_time_en(chan
, t
, ints
, lang
));
5378 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5379 return(ast_say_time_de(chan
, t
, ints
, lang
));
5380 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5381 return(ast_say_time_fr(chan
, t
, ints
, lang
));
5382 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5383 return(ast_say_time_nl(chan
, t
, ints
, lang
));
5384 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5385 return(ast_say_time_pt(chan
, t
, ints
, lang
));
5386 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5387 return(ast_say_time_tw(chan
, t
, ints
, lang
));
5388 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5389 return(ast_say_time_gr(chan
, t
, ints
, lang
));
5392 /* Default to English */
5393 return(ast_say_time_en(chan
, t
, ints
, lang
));
5396 /* English syntax */
5397 int ast_say_time_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5402 localtime_r(&t
,&tm
);
5406 else if (hour
== 12)
5408 else if (hour
> 12) {
5413 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5415 if (tm
.tm_min
> 9) {
5417 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5418 } else if (tm
.tm_min
) {
5420 res
= ast_streamfile(chan
, "digits/oh", lang
);
5422 res
= ast_waitstream(chan
, ints
);
5424 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5427 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5429 res
= ast_waitstream(chan
, ints
);
5433 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5436 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5439 res
= ast_waitstream(chan
, ints
);
5444 int ast_say_time_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5448 localtime_r(&t
,&tm
);
5450 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "n");
5452 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5454 res
= ast_waitstream(chan
, ints
);
5457 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
5462 int ast_say_time_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5466 localtime_r(&t
,&tm
);
5468 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5470 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5473 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5479 int ast_say_time_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5483 localtime_r(&t
,&tm
);
5485 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
5487 res
= ast_streamfile(chan
, "digits/nl-uur", lang
);
5489 res
= ast_waitstream(chan
, ints
);
5492 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, NULL
);
5496 /* Portuguese syntax */
5497 int ast_say_time_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5502 localtime_r(&t
,&tm
);
5505 res
= ast_say_number(chan
, hour
, ints
, lang
, "f");
5508 res
= wait_file(chan
, ints
, "digits/pt-e", lang
);
5510 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5513 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
5514 if (tm
.tm_hour
!= 1)
5516 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5519 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5523 /* Taiwanese / Chinese syntax */
5524 int ast_say_time_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5529 localtime_r(&t
,&tm
);
5533 else if (hour
== 12)
5535 else if (hour
> 12) {
5541 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5544 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5547 res
= ast_waitstream(chan
, ints
);
5549 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5551 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5553 res
= ast_waitstream(chan
, ints
);
5555 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5557 res
= ast_streamfile(chan
, "digits/minute", lang
);
5559 res
= ast_waitstream(chan
, ints
);
5563 static int say_datetime(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5565 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5566 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5567 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5568 return(ast_say_datetime_de(chan
, t
, ints
, lang
));
5569 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5570 return(ast_say_datetime_fr(chan
, t
, ints
, lang
));
5571 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5572 return(ast_say_datetime_nl(chan
, t
, ints
, lang
));
5573 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5574 return(ast_say_datetime_pt(chan
, t
, ints
, lang
));
5575 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5576 return(ast_say_datetime_tw(chan
, t
, ints
, lang
));
5577 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5578 return(ast_say_datetime_gr(chan
, t
, ints
, lang
));
5581 /* Default to English */
5582 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5585 /* English syntax */
5586 int ast_say_datetime_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5592 localtime_r(&t
,&tm
);
5594 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5595 res
= ast_streamfile(chan
, fn
, lang
);
5597 res
= ast_waitstream(chan
, ints
);
5600 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5601 res
= ast_streamfile(chan
, fn
, lang
);
5603 res
= ast_waitstream(chan
, ints
);
5606 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5611 else if (hour
== 12)
5613 else if (hour
> 12) {
5618 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5620 if (tm
.tm_min
> 9) {
5622 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5623 } else if (tm
.tm_min
) {
5625 res
= ast_streamfile(chan
, "digits/oh", lang
);
5627 res
= ast_waitstream(chan
, ints
);
5629 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5632 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5634 res
= ast_waitstream(chan
, ints
);
5638 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5641 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5644 res
= ast_waitstream(chan
, ints
);
5646 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5651 int ast_say_datetime_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5655 localtime_r(&t
,&tm
);
5656 res
= ast_say_date(chan
, t
, ints
, lang
);
5658 ast_say_time(chan
, t
, ints
, lang
);
5664 int ast_say_datetime_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5669 localtime_r(&t
,&tm
);
5672 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5675 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5676 res
= ast_streamfile(chan
, fn
, lang
);
5678 res
= ast_waitstream(chan
, ints
);
5681 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5682 res
= ast_streamfile(chan
, fn
, lang
);
5684 res
= ast_waitstream(chan
, ints
);
5688 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5690 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5691 if (tm
.tm_min
> 0) {
5693 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5696 res
= ast_waitstream(chan
, ints
);
5698 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5703 int ast_say_datetime_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5707 localtime_r(&t
,&tm
);
5708 res
= ast_say_date(chan
, t
, ints
, lang
);
5710 res
= ast_streamfile(chan
, "digits/nl-om", lang
);
5712 res
= ast_waitstream(chan
, ints
);
5715 ast_say_time(chan
, t
, ints
, lang
);
5719 /* Portuguese syntax */
5720 int ast_say_datetime_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5726 localtime_r(&t
,&tm
);
5728 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5729 res
= ast_streamfile(chan
, fn
, lang
);
5731 res
= ast_waitstream(chan
, ints
);
5734 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5735 res
= ast_streamfile(chan
, fn
, lang
);
5737 res
= ast_waitstream(chan
, ints
);
5740 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5745 else if (hour
== 12)
5747 else if (hour
> 12) {
5752 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5754 if (tm
.tm_min
> 9) {
5756 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5757 } else if (tm
.tm_min
) {
5759 res
= ast_streamfile(chan
, "digits/oh", lang
);
5761 res
= ast_waitstream(chan
, ints
);
5763 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5766 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5768 res
= ast_waitstream(chan
, ints
);
5772 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5775 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5778 res
= ast_waitstream(chan
, ints
);
5780 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5784 /* Taiwanese / Chinese syntax */
5785 int ast_say_datetime_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5791 localtime_r(&t
,&tm
);
5793 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5795 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5796 res
= ast_streamfile(chan
, fn
, lang
);
5798 res
= ast_waitstream(chan
, ints
);
5801 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5803 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5804 res
= ast_streamfile(chan
, fn
, lang
);
5806 res
= ast_waitstream(chan
, ints
);
5812 else if (hour
== 12)
5814 else if (hour
> 12) {
5820 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5823 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5826 res
= ast_waitstream(chan
, ints
);
5828 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5830 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5832 res
= ast_waitstream(chan
, ints
);
5834 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5836 res
= ast_streamfile(chan
, "digits/minute", lang
);
5838 res
= ast_waitstream(chan
, ints
);
5842 static int say_datetime_from_now(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5844 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5845 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
5846 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5847 return(ast_say_datetime_from_now_fr(chan
, t
, ints
, lang
));
5848 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5849 return(ast_say_datetime_from_now_pt(chan
, t
, ints
, lang
));
5852 /* Default to English */
5853 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
5856 /* English syntax */
5857 int ast_say_datetime_from_now_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5868 localtime_r(&t
,&tm
);
5869 localtime_r(&nowt
,&now
);
5870 daydiff
= now
.tm_yday
- tm
.tm_yday
;
5871 if ((daydiff
< 0) || (daydiff
> 6)) {
5872 /* Day of month and month */
5874 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5875 res
= ast_streamfile(chan
, fn
, lang
);
5877 res
= ast_waitstream(chan
, ints
);
5880 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5882 } else if (daydiff
) {
5883 /* Just what day of the week */
5885 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5886 res
= ast_streamfile(chan
, fn
, lang
);
5888 res
= ast_waitstream(chan
, ints
);
5890 } /* Otherwise, it was today */
5892 res
= ast_say_time(chan
, t
, ints
, lang
);
5897 int ast_say_datetime_from_now_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5908 localtime_r(&t
,&tm
);
5909 localtime_r(&nowt
,&now
);
5910 daydiff
= now
.tm_yday
- tm
.tm_yday
;
5911 if ((daydiff
< 0) || (daydiff
> 6)) {
5912 /* Day of month and month */
5914 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5915 res
= ast_streamfile(chan
, fn
, lang
);
5917 res
= ast_waitstream(chan
, ints
);
5920 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5922 } else if (daydiff
) {
5923 /* Just what day of the week */
5925 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5926 res
= ast_streamfile(chan
, fn
, lang
);
5928 res
= ast_waitstream(chan
, ints
);
5930 } /* Otherwise, it was today */
5932 res
= ast_say_time(chan
, t
, ints
, lang
);
5936 /* Portuguese syntax */
5937 int ast_say_datetime_from_now_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5948 localtime_r(&t
,&tm
);
5949 localtime_r(&nowt
,&now
);
5950 daydiff
= now
.tm_yday
- tm
.tm_yday
;
5951 if ((daydiff
< 0) || (daydiff
> 6)) {
5952 /* Day of month and month */
5954 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5956 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
5957 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5959 res
= wait_file(chan
, ints
, fn
, lang
);
5961 } else if (daydiff
) {
5962 /* Just what day of the week */
5963 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5965 res
= wait_file(chan
, ints
, fn
, lang
);
5966 } /* Otherwise, it was today */
5967 snprintf(fn
, sizeof(fn
), "digits/pt-ah");
5969 res
= wait_file(chan
, ints
, fn
, lang
);
5970 if (tm
.tm_hour
!= 1)
5972 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5974 res
= ast_say_time(chan
, t
, ints
, lang
);
5979 /*********************************** GREEK SUPPORT ***************************************/
5983 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
5985 static int gr_say_number_female(int num
, struct ast_channel
*chan
, const char *ints
, const char *lang
){
5991 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
5993 snprintf(fn
, sizeof(fn
), "digits/female-%d", num
);
5994 res
= wait_file(chan
, ints
, fn
, lang
);
5995 } else if (num
< 13) {
5996 res
= ast_say_number(chan
, num
, ints
, lang
, (char *) NULL
);
5997 } else if (num
<100 ) {
5998 tmp
= (num
/10) * 10;
6000 snprintf(fn
, sizeof(fn
), "digits/%d", tmp
);
6001 res
= ast_streamfile(chan
, fn
, lang
);
6003 res
= ast_waitstream(chan
, ints
);
6005 gr_say_number_female(left
, chan
, ints
, lang
);
6016 * A list of the files that you need to create
6017 -> digits/xilia = "xilia"
6018 -> digits/myrio = "ekatomyrio"
6019 -> digits/thousands = "xiliades"
6020 -> digits/millions = "ektatomyria"
6021 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6022 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6024 Here we must note that we use digits/tens/100 to utter "ekato"
6025 and digits/hundred-100 to utter "ekaton"
6026 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6027 "terakosia". Here again we use hundreds/1000 for "xilia"
6028 and digits/thousnds for "xiliades"
6031 static int ast_say_number_full_gr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
,int audiofd
, int ctrlfd
)
6039 snprintf(fn
, sizeof(fn
), "digits/0");
6040 res
= ast_streamfile(chan
, fn
, chan
->language
);
6042 return ast_waitstream(chan
, ints
);
6045 while(!res
&& num
) {
6048 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
6050 } else if (num
<= 100) {
6051 /* 13 < num <= 100 */
6052 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
6053 num
-= ((num
/ 10) * 10);
6054 } else if (num
< 200) {
6055 /* 100 < num < 200 */
6056 snprintf(fn
, sizeof(fn
), "digits/hundred-100");
6057 num
-= ((num
/ 100) * 100);
6058 }else if (num
< 1000) {
6059 /* 200 < num < 1000 */
6060 snprintf(fn
, sizeof(fn
), "digits/hundred-%d", (num
/100)*100);
6061 num
-= ((num
/ 100) * 100);
6062 }else if (num
< 2000){
6063 snprintf(fn
, sizeof(fn
), "digits/xilia");
6064 num
-= ((num
/ 1000) * 1000);
6068 if (num
< 1000000) {
6069 res
= ast_say_number_full_gr(chan
, (num
/ 1000), ints
, chan
->language
, audiofd
, ctrlfd
);
6073 snprintf(fn
, sizeof(fn
), "digits/thousands");
6075 if (num
< 1000000000) { /* 1,000,000,000 */
6076 res
= ast_say_number_full_gr(chan
, (num
/ 1000000), ints
, chan
->language
,audiofd
, ctrlfd
);
6079 num
= num
% 1000000;
6080 snprintf(fn
, sizeof(fn
), "digits/millions");
6082 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
6088 if(!ast_streamfile(chan
, fn
, language
)) {
6089 if ((audiofd
> -1) && (ctrlfd
> -1))
6090 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
6092 res
= ast_waitstream(chan
, ints
);
6094 ast_stopstream(chan
);
6102 * The format is weekday - day - month -year
6104 * A list of the files that you need to create
6105 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6106 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6107 Attention the months are in
6112 static int ast_say_date_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6120 ast_localtime(&t
,&tm
,NULL
);
6121 /* W E E K - D A Y */
6123 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6124 res
= ast_streamfile(chan
, fn
, lang
);
6126 res
= ast_waitstream(chan
, ints
);
6130 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6134 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6135 res
= ast_streamfile(chan
, fn
, lang
);
6137 res
= ast_waitstream(chan
, ints
);
6141 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
6147 /* A list of the files that you need to create
6148 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6149 * digits/kai : "KAI"
6151 * digits/p-m : "meta meshmbrias"
6152 * digits/a-m : "pro meshmbrias"
6155 static int ast_say_time_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6162 localtime_r(&t
,&tm
);
6167 else if (hour
== 12)
6169 else if (hour
> 12) {
6174 res
= gr_say_number_female(hour
, chan
, ints
, lang
);
6177 res
= ast_streamfile(chan
, "digits/kai", lang
);
6179 res
= ast_waitstream(chan
, ints
);
6181 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
6184 res
= ast_streamfile(chan
, "digits/hwra", lang
);
6186 res
= ast_waitstream(chan
, ints
);
6190 res
= ast_streamfile(chan
, "digits/p-m", lang
);
6193 res
= ast_streamfile(chan
, "digits/a-m", lang
);
6196 res
= ast_waitstream(chan
, ints
);
6202 static int ast_say_datetime_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6207 localtime_r(&t
,&tm
);
6210 /* W E E K - D A Y */
6212 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6213 res
= ast_streamfile(chan
, fn
, lang
);
6215 res
= ast_waitstream(chan
, ints
);
6219 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6223 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6224 res
= ast_streamfile(chan
, fn
, lang
);
6226 res
= ast_waitstream(chan
, ints
);
6229 res
= ast_say_time_gr(chan
, t
, ints
, lang
);
6233 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
)
6237 int res
=0, offset
, sndoffset
;
6238 char sndfile
[256], nextmsg
[256];
6241 format
= "AdBY 'digits/at' IMp";
6243 ast_localtime(&time
,&tm
,timezone
);
6245 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
6246 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
6247 switch (format
[offset
]) {
6248 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6250 /* Literal name of a sound file */
6252 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
6253 sndfile
[sndoffset
] = format
[offset
];
6254 sndfile
[sndoffset
] = '\0';
6255 res
= wait_file(chan
,ints
,sndfile
,lang
);
6259 /* Sunday - Saturday */
6260 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
6261 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6266 /* January - December */
6267 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
6268 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6272 /* first - thirtyfirst */
6273 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6278 ast_say_number_full_gr(chan
, 1900+tm
.tm_year
, ints
, chan
->language
, -1, -1);
6283 if (tm
.tm_hour
== 0)
6284 gr_say_number_female(12, chan
, ints
, lang
);
6285 else if (tm
.tm_hour
> 12)
6286 gr_say_number_female(tm
.tm_hour
- 12, chan
, ints
, lang
);
6288 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6293 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6299 res
= ast_streamfile(chan
, "digits/kai", lang
);
6301 res
= ast_waitstream(chan
, ints
);
6303 res
= ast_say_number_full_gr(chan
, tm
.tm_min
, ints
, lang
, -1, -1);
6306 res
= ast_streamfile(chan
, "digits/oclock", lang
);
6308 res
= ast_waitstream(chan
, ints
);
6314 if (tm
.tm_hour
> 11)
6315 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
6317 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
6318 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6321 /* Shorthand for "Today", "Yesterday", or ABdY */
6322 /* XXX As emphasized elsewhere, this should the native way in your
6323 * language to say the date, with changes in what you say, depending
6324 * upon how recent the date is. XXX */
6328 time_t beg_today
, tt
;
6330 gettimeofday(&now
,NULL
);
6332 ast_localtime(&tt
,&tmnow
,timezone
);
6333 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6334 /* In any case, it saves not having to do ast_mktime() */
6335 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6336 if (beg_today
< time
) {
6338 res
= wait_file(chan
,ints
, "digits/today",lang
);
6339 } else if (beg_today
- 86400 < time
) {
6341 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6343 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6348 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6349 /* XXX As emphasized elsewhere, this should the native way in your
6350 * language to say the date, with changes in what you say, depending
6351 * upon how recent the date is. XXX */
6355 time_t beg_today
, tt
;
6357 gettimeofday(&now
,NULL
);
6359 ast_localtime(&tt
,&tmnow
,timezone
);
6360 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6361 /* In any case, it saves not having to do ast_mktime() */
6362 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6363 if (beg_today
< time
) {
6365 } else if ((beg_today
- 86400) < time
) {
6367 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6368 } else if (beg_today
- 86400 * 6 < time
) {
6369 /* Within the last week */
6370 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "A", timezone
);
6372 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6377 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HM", timezone
);
6381 snprintf(nextmsg
,sizeof(nextmsg
), "digits/kai");
6382 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6384 res
= ast_say_number_full_gr(chan
, tm
.tm_sec
, ints
, lang
, -1, -1);
6386 snprintf(nextmsg
,sizeof(nextmsg
), "digits/seconds");
6387 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6390 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HMS", timezone
);
6394 /* Just ignore spaces and tabs */
6397 /* Unknown character */
6398 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
6400 /* Jump out on DTMF */
6409 * remap the 'say' functions to use those in this file
6411 static void __attribute__((constructor
)) __say_init(void)
6413 ast_say_number_full
= say_number_full
;
6414 ast_say_enumeration_full
= say_enumeration_full
;
6415 ast_say_digit_str_full
= say_digit_str_full
;
6416 ast_say_character_str_full
= say_character_str_full
;
6417 ast_say_phonetic_str_full
= say_phonetic_str_full
;
6418 ast_say_datetime
= say_datetime
;
6419 ast_say_time
= say_time
;
6420 ast_say_date
= say_date
;
6421 ast_say_datetime_from_now
= say_datetime_from_now
;
6422 ast_say_date_with_format
= say_date_with_format
;