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 2thousands: 2 thousand
1149 thousands: plural of 'thousand'
1150 3sF 'Smichut forms (female)
1157 3s 'Smichut' forms (male)
1174 TODO: 've' should sometimed be 'hu':
1175 * before 'shtaym' (2, F)
1176 * before 'shnaym' (2, M)
1177 * before 'shlosha' (3, M)
1178 * before 'shmone' (8, M)
1179 * before 'shlosim' (30)
1180 * before 'shmonim' (80)
1186 #define SAY_NUM_BUF_SIZE 256
1187 static int ast_say_number_full_he(struct ast_channel
*chan
, int num
,
1188 const char *ints
, const char *language
, const char *options
,
1189 int audiofd
, int ctrlfd
)
1192 int state
= 0; /* no need to save anything */
1193 int mf
= 1; /* +1 = Masculin; -1 = Feminin */
1194 char fn
[SAY_NUM_BUF_SIZE
] = "";
1195 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: started. "
1196 "num: %d, options=\"%s\"\n",
1200 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1202 if (options
&& !strncasecmp(options
, "f",1))
1205 /* Do we have work to do? */
1206 while(!res
&& (num
|| (state
>0) )) {
1207 /* first type of work: play a second sound. In this loop
1208 * we can only play one sound file at a time. Thus playing
1209 * a second one requires repeating the loop just for the
1210 * second file. The variable 'state' remembers where we were.
1211 * state==0 is the normal mode and it means that we continue
1212 * to check if the number num has yet anything left.
1214 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: num: %d, "
1215 "state=%d, options=\"%s\", mf=%d\n",
1216 num
, state
, options
, mf
1219 snprintf(fn
, sizeof(fn
), "digits/hundred");
1221 } else if (state
==2) {
1222 snprintf(fn
, sizeof(fn
), "digits/ve");
1224 } else if (state
==3) {
1225 snprintf(fn
, sizeof(fn
), "digits/thousands");
1227 } else if (num
<21) {
1229 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1231 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1233 } else if (num
< 100) {
1234 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1237 } else if (num
< 200) {
1238 snprintf(fn
, sizeof(fn
), "digits/hundred");
1240 } else if (num
< 300) {
1241 snprintf(fn
, sizeof(fn
), "digits/hundred");
1243 } else if (num
< 1000) {
1244 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1247 } else if (num
< 2000) {
1248 snprintf(fn
, sizeof(fn
), "digits/thousand");
1250 } else if (num
< 3000) {
1251 snprintf(fn
, sizeof(fn
), "digits/2thousand");
1254 } else if (num
< 20000) {
1255 snprintf(fn
, sizeof(fn
), "digits/%ds",(num
/1000));
1258 } else if (num
< 1000000) {
1259 res
= ast_say_number_full_he(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1262 snprintf(fn
, sizeof(fn
), "digits/thousand");
1264 } else if (num
< 1000000000) {
1265 res
= ast_say_number_full_he(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1268 snprintf(fn
, sizeof(fn
), "digits/million");
1269 num
= num
% 1000000;
1271 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1275 if(!ast_streamfile(chan
, fn
, language
)) {
1276 if ((audiofd
> -1) && (ctrlfd
> -1))
1277 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1279 res
= ast_waitstream(chan
, ints
);
1281 ast_stopstream(chan
);
1287 /*! \brief ast_say_number_full_it: Italian */
1288 static int ast_say_number_full_it(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1296 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1301 Like english, numbers up to 20 are a single 'word', and others
1302 compound, but with exceptions.
1303 For example 21 is not twenty-one, but there is a single word in 'it'.
1304 Idem for 28 (ie when a the 2nd part of a compund number
1305 starts with a vowel)
1307 There are exceptions also for hundred, thousand and million.
1308 In english 100 = one hundred, 200 is two hundred.
1309 In italian 100 = cento , like to say hundred (without one),
1310 200 and more are like english.
1312 Same applies for thousand:
1313 1000 is one thousand in en, 2000 is two thousand.
1314 In it we have 1000 = mille , 2000 = 2 mila
1316 For million(s) we use the plural, if more than one
1317 Also, one million is abbreviated in it, like on-million,
1318 or 'un milione', not 'uno milione'.
1319 So the right file is provided.
1322 while(!res
&& (num
|| playh
)) {
1324 snprintf(fn
, sizeof(fn
), "digits/minus");
1325 if ( num
> INT_MIN
) {
1331 snprintf(fn
, sizeof(fn
), "digits/hundred");
1333 } else if (num
< 20) {
1334 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1336 } else if (num
== 21) {
1337 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1339 } else if (num
== 28) {
1340 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1342 } else if (num
== 31) {
1343 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1345 } else if (num
== 38) {
1346 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1348 } else if (num
== 41) {
1349 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1351 } else if (num
== 48) {
1352 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1354 } else if (num
== 51) {
1355 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1357 } else if (num
== 58) {
1358 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1360 } else if (num
== 61) {
1361 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1363 } else if (num
== 68) {
1364 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1366 } else if (num
== 71) {
1367 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1369 } else if (num
== 78) {
1370 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1372 } else if (num
== 81) {
1373 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1375 } else if (num
== 88) {
1376 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1378 } else if (num
== 91) {
1379 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1381 } else if (num
== 98) {
1382 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1384 } else if (num
< 100) {
1385 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1386 num
-= ((num
/ 10) * 10);
1389 if ((num
/ 100) > 1) {
1390 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1393 snprintf(fn
, sizeof(fn
), "digits/hundred");
1395 num
-= ((num
/ 100) * 100);
1397 if (num
< 1000000) { /* 1,000,000 */
1399 res
= ast_say_number_full_it(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1404 if ((tempnum
/ 1000) < 2)
1405 snprintf(fn
, sizeof(fn
), "digits/thousand");
1406 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1407 snprintf(fn
, sizeof(fn
), "digits/thousands");
1409 if (num
< 1000000000) { /* 1,000,000,000 */
1410 if ((num
/ 1000000) > 1)
1411 res
= ast_say_number_full_it(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1415 num
= num
% 1000000;
1416 if ((tempnum
/ 1000000) < 2)
1417 snprintf(fn
, sizeof(fn
), "digits/million");
1419 snprintf(fn
, sizeof(fn
), "digits/millions");
1421 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1428 if(!ast_streamfile(chan
, fn
, language
)) {
1429 if ((audiofd
> -1) && (ctrlfd
> -1))
1430 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1432 res
= ast_waitstream(chan
, ints
);
1434 ast_stopstream(chan
);
1440 /*! \brief ast_say_number_full_nl: dutch syntax */
1441 /* New files: digits/nl-en
1443 static int ast_say_number_full_nl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1450 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1451 while (!res
&& (num
|| playh
)) {
1453 snprintf(fn
, sizeof(fn
), "digits/minus");
1454 if ( num
> INT_MIN
) {
1460 snprintf(fn
, sizeof(fn
), "digits/hundred");
1462 } else if (num
< 20) {
1463 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1465 } else if (num
< 100) {
1468 res
= ast_say_number_full_nl(chan
, units
, ints
, language
, audiofd
, ctrlfd
);
1472 snprintf(fn
, sizeof(fn
), "digits/nl-en");
1474 snprintf(fn
, sizeof(fn
), "digits/%d", num
- units
);
1479 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1481 num
-= ((num
/ 100) * 100);
1483 if (num
< 1000000) { /* 1,000,000 */
1484 res
= ast_say_number_full_en(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1488 snprintf(fn
, sizeof(fn
), "digits/thousand");
1490 if (num
< 1000000000) { /* 1,000,000,000 */
1491 res
= ast_say_number_full_en(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1494 num
= num
% 1000000;
1495 snprintf(fn
, sizeof(fn
), "digits/million");
1497 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1505 if(!ast_streamfile(chan
, fn
, language
)) {
1506 if ((audiofd
> -1) && (ctrlfd
> -1))
1507 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1509 res
= ast_waitstream(chan
, ints
);
1511 ast_stopstream(chan
);
1517 /*! \brief ast_say_number_full_no: Norwegian syntax */
1519 In addition to American English, the following sounds are required: "and", "1N"
1521 static int ast_say_number_full_no(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
1526 int cn
= 1; /* +1 = commune; -1 = neuter */
1530 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1532 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
1534 while(!res
&& (num
|| playh
|| playa
)) {
1535 /* The grammar for Norwegian numbers is the same as for English except
1536 * for the following:
1537 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1538 * "and" before the last two digits, i.e. 2034 is "two thousand and
1539 * thirty-four" and 1000012 is "one million and twelve".
1542 snprintf(fn
, sizeof(fn
), "digits/minus");
1543 if ( num
> INT_MIN
) {
1549 snprintf(fn
, sizeof(fn
), "digits/hundred");
1552 snprintf(fn
, sizeof(fn
), "digits/and");
1554 } else if (num
== 1 && cn
== -1) {
1555 snprintf(fn
, sizeof(fn
), "digits/1N");
1557 } else if (num
< 20) {
1558 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1560 } else if (num
< 100) {
1561 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1562 num
-= ((num
/ 10) * 10);
1563 } else if (num
< 1000) {
1564 int hundreds
= num
/ 100;
1566 snprintf(fn
, sizeof(fn
), "digits/1N");
1568 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
1571 num
-= 100 * hundreds
;
1574 } else if (num
< 1000000) {
1575 res
= ast_say_number_full_no(chan
, num
/ 1000, ints
, language
, "n", audiofd
, ctrlfd
);
1578 snprintf(fn
, sizeof(fn
), "digits/thousand");
1580 if (num
&& num
< 100)
1582 } else if (num
< 1000000000) {
1583 int millions
= num
/ 1000000;
1584 res
= ast_say_number_full_no(chan
, millions
, ints
, language
, "c", audiofd
, ctrlfd
);
1587 snprintf(fn
, sizeof(fn
), "digits/million");
1588 num
= num
% 1000000;
1589 if (num
&& num
< 100)
1592 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1597 if(!ast_streamfile(chan
, fn
, language
)) {
1598 if ((audiofd
> -1) && (ctrlfd
> -1))
1599 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1601 res
= ast_waitstream(chan
, ints
);
1603 ast_stopstream(chan
);
1610 char *separator_dziesiatek
;
1614 char *dziesiatki
[10];
1619 static char *pl_rzad_na_tekst(odmiana
*odm
, int i
, int rzad
)
1625 return odm
->rzedy
[rzad
- 1][0];
1626 if ((i
> 21 || i
< 11) && i
%10 > 1 && i
%10 < 5)
1627 return odm
->rzedy
[rzad
- 1][1];
1629 return odm
->rzedy
[rzad
- 1][2];
1632 static char* pl_append(char* buffer
, char* str
)
1634 strcpy(buffer
, str
);
1635 buffer
+= strlen(str
);
1639 static void pl_odtworz_plik(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, char *fn
)
1641 char file_name
[255] = "digits/";
1642 strcat(file_name
, fn
);
1643 ast_log(LOG_DEBUG
, "Trying to play: %s\n", file_name
);
1644 if (!ast_streamfile(chan
, file_name
, language
)) {
1645 if ((audiofd
> -1) && (ctrlfd
> -1))
1646 ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1648 ast_waitstream(chan
, ints
);
1650 ast_stopstream(chan
);
1653 static void powiedz(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, odmiana
*odm
, int rzad
, int i
)
1655 /* Initialise variables to allow compilation on Debian-stable, etc */
1665 if (i
== 0 && rzad
> 0) {
1669 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[0]);
1673 m1000E6
= i
% 1000000000;
1674 i1000E6
= i
/ 1000000000;
1676 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+3, i1000E6
);
1678 m1000E3
= m1000E6
% 1000000;
1679 i1000E3
= m1000E6
/ 1000000;
1681 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+2, i1000E3
);
1683 m1000
= m1000E3
% 1000;
1684 i1000
= m1000E3
/ 1000;
1686 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+1, i1000
);
1692 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->setki
[i100
]);
1694 if ( m100
> 0 && m100
<=9 ) {
1696 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
]);
1698 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[m100
]);
1699 } else if (m100
% 10 == 0) {
1700 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1701 } else if (m100
<= 19 ) {
1702 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->nastki
[m100
% 10]);
1703 } else if (m100
!= 0) {
1704 if (odm
->separator_dziesiatek
[0]==' ') {
1705 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1706 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
% 10]);
1710 b
= pl_append(b
, odm
->dziesiatki
[m100
/ 10]);
1711 b
= pl_append(b
, odm
->separator_dziesiatek
);
1712 b
= pl_append(b
, odm
->cyfry2
[m100
% 10]);
1713 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, buf
);
1718 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, pl_rzad_na_tekst(odm
, i
, rzad
));
1722 /* ast_say_number_full_pl: Polish syntax */
1723 static int ast_say_number_full_pl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
1733 1000000000.2 miliardy
1734 1000000000.5 miliardow
1798 70m siedemdziesieciu
1810 90m dziewiedziesieciu
1812 and combinations of eg.: 20_1, 30m_3m, etc...
1816 char *zenski_cyfry
[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1818 char *zenski_cyfry2
[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1820 char *meski_cyfry
[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1822 char *meski_cyfry2
[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1824 char *meski_setki
[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1826 char *meski_dziesiatki
[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1828 char *meski_nastki
[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1830 char *nijaki_cyfry
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1832 char *nijaki_cyfry2
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1834 char *nijaki_setki
[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1836 char *nijaki_dziesiatki
[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1838 char *nijaki_nastki
[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1840 char *rzedy
[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1842 /* Initialise variables to allow compilation on Debian-stable, etc */
1845 static odmiana
*odmiana_nieosobowa
= NULL
;
1846 static odmiana
*odmiana_meska
= NULL
;
1847 static odmiana
*odmiana_zenska
= NULL
;
1849 if (odmiana_nieosobowa
== NULL
) {
1850 odmiana_nieosobowa
= (odmiana
*) malloc(sizeof(odmiana
));
1852 odmiana_nieosobowa
->separator_dziesiatek
= " ";
1854 memcpy(odmiana_nieosobowa
->cyfry
, nijaki_cyfry
, sizeof(odmiana_nieosobowa
->cyfry
));
1855 memcpy(odmiana_nieosobowa
->cyfry2
, nijaki_cyfry2
, sizeof(odmiana_nieosobowa
->cyfry
));
1856 memcpy(odmiana_nieosobowa
->setki
, nijaki_setki
, sizeof(odmiana_nieosobowa
->setki
));
1857 memcpy(odmiana_nieosobowa
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_nieosobowa
->dziesiatki
));
1858 memcpy(odmiana_nieosobowa
->nastki
, nijaki_nastki
, sizeof(odmiana_nieosobowa
->nastki
));
1859 memcpy(odmiana_nieosobowa
->rzedy
, rzedy
, sizeof(odmiana_nieosobowa
->rzedy
));
1862 if (odmiana_zenska
== NULL
) {
1863 odmiana_zenska
= (odmiana
*) malloc(sizeof(odmiana
));
1865 odmiana_zenska
->separator_dziesiatek
= " ";
1867 memcpy(odmiana_zenska
->cyfry
, zenski_cyfry
, sizeof(odmiana_zenska
->cyfry
));
1868 memcpy(odmiana_zenska
->cyfry2
, zenski_cyfry2
, sizeof(odmiana_zenska
->cyfry
));
1869 memcpy(odmiana_zenska
->setki
, nijaki_setki
, sizeof(odmiana_zenska
->setki
));
1870 memcpy(odmiana_zenska
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_zenska
->dziesiatki
));
1871 memcpy(odmiana_zenska
->nastki
, nijaki_nastki
, sizeof(odmiana_zenska
->nastki
));
1872 memcpy(odmiana_zenska
->rzedy
, rzedy
, sizeof(odmiana_zenska
->rzedy
));
1875 if (odmiana_meska
== NULL
) {
1876 odmiana_meska
= (odmiana
*) malloc(sizeof(odmiana
));
1878 odmiana_meska
->separator_dziesiatek
= " ";
1880 memcpy(odmiana_meska
->cyfry
, meski_cyfry
, sizeof(odmiana_meska
->cyfry
));
1881 memcpy(odmiana_meska
->cyfry2
, meski_cyfry2
, sizeof(odmiana_meska
->cyfry
));
1882 memcpy(odmiana_meska
->setki
, meski_setki
, sizeof(odmiana_meska
->setki
));
1883 memcpy(odmiana_meska
->dziesiatki
, meski_dziesiatki
, sizeof(odmiana_meska
->dziesiatki
));
1884 memcpy(odmiana_meska
->nastki
, meski_nastki
, sizeof(odmiana_meska
->nastki
));
1885 memcpy(odmiana_meska
->rzedy
, rzedy
, sizeof(odmiana_meska
->rzedy
));
1889 if (strncasecmp(options
, "f", 1) == 0)
1891 else if (strncasecmp(options
, "m", 1) == 0)
1894 o
= odmiana_nieosobowa
;
1896 o
= odmiana_nieosobowa
;
1898 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, o
, 0, num
);
1902 /* ast_say_number_full_pt: Portuguese syntax */
1903 /* Extra sounds needed: */
1904 /* For feminin all sound files end with F */
1905 /* 100E for 100+ something */
1906 /* 1000000S for plural */
1907 /* pt-e for 'and' */
1908 static int ast_say_number_full_pt(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
1912 int mf
= 1; /* +1 = male; -1 = female */
1916 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1918 if (options
&& !strncasecmp(options
, "f",1))
1921 while(!res
&& num
) {
1923 snprintf(fn
, sizeof(fn
), "digits/minus");
1924 if ( num
> INT_MIN
) {
1929 } else if (num
< 20) {
1930 if ((num
== 1 || num
== 2) && (mf
< 0))
1931 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1933 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1935 } else if (num
< 100) {
1936 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 10) * 10);
1940 } else if (num
< 1000) {
1942 snprintf(fn
, sizeof(fn
), "digits/100");
1944 snprintf(fn
, sizeof(fn
), "digits/100E");
1946 if (mf
< 0 && num
> 199)
1947 snprintf(fn
, sizeof(fn
), "digits/%dF", (num
/ 100) * 100);
1949 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100) * 100);
1954 } else if (num
< 1000000) {
1956 res
= ast_say_number_full_pt(chan
, (num
/ 1000) * mf
, ints
, language
, options
, audiofd
, ctrlfd
);
1960 snprintf(fn
, sizeof(fn
), "digits/1000");
1961 if ((num
% 1000) && ((num
% 1000) < 100 || !(num
% 100)))
1964 } else if (num
< 1000000000) {
1965 res
= ast_say_number_full_pt(chan
, (num
/ 1000000), ints
, language
, options
, audiofd
, ctrlfd
);
1969 snprintf(fn
, sizeof(fn
), "digits/1000000");
1971 snprintf(fn
, sizeof(fn
), "digits/1000000S");
1973 if ((num
% 1000000) &&
1975 ((!((num
/ 1000) % 1000) && ((num
% 1000) < 100 || !(num
% 100))) ||
1976 /* no hundreds and below */
1977 (!(num
% 1000) && (((num
/ 1000) % 1000) < 100 || !((num
/ 1000) % 100))) ) )
1979 num
= num
% 1000000;
1982 if (!ast_streamfile(chan
, fn
, language
)) {
1983 if ((audiofd
> -1) && (ctrlfd
> -1))
1984 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1986 res
= ast_waitstream(chan
, ints
);
1988 ast_stopstream(chan
);
1990 if (!res
&& playh
) {
1991 res
= wait_file(chan
, ints
, "digits/pt-e", language
);
1992 ast_stopstream(chan
);
1999 /*! \brief ast_say_number_full_se: Swedish syntax */
2000 static int ast_say_number_full_se(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2005 int cn
= 1; /* +1 = commune; -1 = neuter */
2007 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2008 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
2010 while(!res
&& (num
|| playh
)) {
2012 snprintf(fn
, sizeof(fn
), "digits/minus");
2013 if ( num
> INT_MIN
) {
2019 snprintf(fn
, sizeof(fn
), "digits/hundred");
2021 } else if (num
< 20) {
2022 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2024 } else if (num
< 100) {
2025 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2026 num
-= ((num
/ 10) * 10);
2027 } else if (num
== 1 && cn
== -1) { /* En eller ett? */
2028 snprintf(fn
, sizeof(fn
), "digits/1N");
2032 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2034 num
-= ((num
/ 100) * 100);
2036 if (num
< 1000000) { /* 1,000,000 */
2037 res
= ast_say_number_full_se(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
2042 snprintf(fn
, sizeof(fn
), "digits/thousand");
2044 if (num
< 1000000000) { /* 1,000,000,000 */
2045 res
= ast_say_number_full_se(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
2049 num
= num
% 1000000;
2050 snprintf(fn
, sizeof(fn
), "digits/million");
2052 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2059 if(!ast_streamfile(chan
, fn
, language
)) {
2060 if ((audiofd
> -1) && (ctrlfd
> -1))
2061 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2063 res
= ast_waitstream(chan
, ints
);
2064 ast_stopstream(chan
);
2071 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2072 static int ast_say_number_full_tw(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2078 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2080 while(!res
&& (num
|| playh
)) {
2082 snprintf(fn
, sizeof(fn
), "digits/minus");
2083 if ( num
> INT_MIN
) {
2089 snprintf(fn
, sizeof(fn
), "digits/hundred");
2091 } else if (num
< 10) {
2092 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2094 } else if (num
< 100) {
2095 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2096 num
-= ((num
/ 10) * 10);
2099 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2101 num
-= ((num
/ 100) * 100);
2103 if (num
< 1000000) { /* 1,000,000 */
2104 res
= ast_say_number_full_tw(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
2108 snprintf(fn
, sizeof(fn
), "digits/thousand");
2110 if (num
< 1000000000) { /* 1,000,000,000 */
2111 res
= ast_say_number_full_tw(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
2114 num
= num
% 1000000;
2115 snprintf(fn
, sizeof(fn
), "digits/million");
2117 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2124 if(!ast_streamfile(chan
, fn
, language
)) {
2125 if ((audiofd
> -1) && (ctrlfd
> -1))
2126 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2128 res
= ast_waitstream(chan
, ints
);
2130 ast_stopstream(chan
);
2137 /*! \brief determine last digits for thousands/millions (ru) */
2138 static int get_lastdigits_ru(int num
) {
2141 } else if (num
< 100) {
2142 return get_lastdigits_ru(num
% 10);
2143 } else if (num
< 1000) {
2144 return get_lastdigits_ru(num
% 100);
2146 return 0; /* number too big */
2150 /*! \brief ast_say_number_full_ru: Russian syntax */
2151 /*! \brief additional files:
2152 n00.gsm (one hundred, two hundred, ...)
2155 thousands-i.gsm (tisyachi)
2156 million-a.gsm (milliona)
2162 where 'n' from 1 to 9
2164 static int ast_say_number_full_ru(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2170 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2172 while(!res
&& (num
)) {
2174 snprintf(fn
, sizeof(fn
), "digits/minus");
2175 if ( num
> INT_MIN
) {
2180 } else if (num
< 20) {
2181 if(options
&& strlen(options
) == 1 && num
< 3) {
2182 snprintf(fn
, sizeof(fn
), "digits/%d%s", num
, options
);
2184 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2187 } else if (num
< 100) {
2188 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 10));
2190 } else if (num
< 1000){
2191 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 100));
2193 } else if (num
< 1000000) { /* 1,000,000 */
2194 lastdigits
= get_lastdigits_ru(num
/ 1000);
2196 if (lastdigits
< 3) {
2197 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, "f", audiofd
, ctrlfd
);
2199 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2203 if (lastdigits
== 1) {
2204 snprintf(fn
, sizeof(fn
), "digits/thousand");
2205 } else if (lastdigits
> 1 && lastdigits
< 5) {
2206 snprintf(fn
, sizeof(fn
), "digits/thousands-i");
2208 snprintf(fn
, sizeof(fn
), "digits/thousands");
2211 } else if (num
< 1000000000) { /* 1,000,000,000 */
2212 lastdigits
= get_lastdigits_ru(num
/ 1000000);
2214 res
= ast_say_number_full_ru(chan
, num
/ 1000000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2217 if (lastdigits
== 1) {
2218 snprintf(fn
, sizeof(fn
), "digits/million");
2219 } else if (lastdigits
> 1 && lastdigits
< 5) {
2220 snprintf(fn
, sizeof(fn
), "digits/million-a");
2222 snprintf(fn
, sizeof(fn
), "digits/millions");
2226 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2230 if (!ast_streamfile(chan
, fn
, language
)) {
2231 if ((audiofd
> -1) && (ctrlfd
> -1))
2232 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2234 res
= ast_waitstream(chan
, ints
);
2236 ast_stopstream(chan
);
2243 /*! \brief ast_say_enumeration_full: call language-specific functions */
2244 /* Called from AGI */
2245 static int say_enumeration_full(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2247 if (!strcasecmp(language
,"en") ) { /* English syntax */
2248 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2249 } else if (!strcasecmp(language
, "da") ) { /* Danish syntax */
2250 return(ast_say_enumeration_full_da(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2251 } else if (!strcasecmp(language
, "de") ) { /* German syntax */
2252 return(ast_say_enumeration_full_de(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2255 /* Default to english */
2256 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2259 /*! \brief ast_say_enumeration_full_en: English syntax */
2260 /* This is the default syntax, if no other syntax defined in this file is used */
2261 static int ast_say_enumeration_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2266 while(!res
&& num
) {
2268 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2269 if ( num
> INT_MIN
) {
2274 } else if (num
< 20) {
2275 snprintf(fn
, sizeof(fn
), "digits/h-%d", num
);
2277 } else if (num
< 100) {
2278 int tens
= num
/ 10;
2281 snprintf(fn
, sizeof(fn
), "digits/h-%d", (tens
* 10));
2283 snprintf(fn
, sizeof(fn
), "digits/%d", (tens
* 10));
2285 } else if (num
< 1000) {
2286 int hundreds
= num
/ 100;
2288 if (hundreds
> 1 || t
== 1) {
2289 res
= ast_say_number_full_en(chan
, hundreds
, ints
, language
, audiofd
, ctrlfd
);
2294 snprintf(fn
, sizeof(fn
), "digits/hundred");
2296 snprintf(fn
, sizeof(fn
), "digits/h-hundred");
2298 } else if (num
< 1000000) {
2299 int thousands
= num
/ 1000;
2301 if (thousands
> 1 || t
== 1) {
2302 res
= ast_say_number_full_en(chan
, thousands
, ints
, language
, audiofd
, ctrlfd
);
2307 snprintf(fn
, sizeof(fn
), "digits/thousand");
2309 snprintf(fn
, sizeof(fn
), "digits/h-thousand");
2312 } else if (num
< 1000000000) {
2313 int millions
= num
/ 1000000;
2314 num
= num
% 1000000;
2316 res
= ast_say_number_full_en(chan
, millions
, ints
, language
, audiofd
, ctrlfd
);
2320 snprintf(fn
, sizeof(fn
), "digits/million");
2322 snprintf(fn
, sizeof(fn
), "digits/h-million");
2324 } else if (num
< INT_MAX
) {
2325 int billions
= num
/ 1000000000;
2326 num
= num
% 1000000000;
2328 res
= ast_say_number_full_en(chan
, billions
, ints
, language
, audiofd
, ctrlfd
);
2332 snprintf(fn
, sizeof(fn
), "digits/billion");
2334 snprintf(fn
, sizeof(fn
), "digits/h-billion");
2336 } else if (num
== INT_MAX
) {
2337 snprintf(fn
, sizeof(fn
), "digits/h-last");
2340 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2345 if (!ast_streamfile(chan
, fn
, language
)) {
2346 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2347 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2349 res
= ast_waitstream(chan
, ints
);
2352 ast_stopstream(chan
);
2358 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2359 static int ast_say_enumeration_full_da(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2361 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2363 char fn
[256] = "", fna
[256] = "";
2366 if (options
&& !strncasecmp(options
, "f",1)) {
2368 } else if (options
&& !strncasecmp(options
, "n",1)) {
2375 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2377 while(!res
&& num
) {
2379 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2380 if ( num
> INT_MIN
) {
2385 } else if (num
< 100 && t
) {
2386 snprintf(fn
, sizeof(fn
), "digits/and");
2388 } else if (num
< 20) {
2389 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2391 } else if (num
< 100) {
2392 int ones
= num
% 10;
2394 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2397 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2400 } else if (num
== 100 && t
== 0) {
2401 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2403 } else if (num
< 1000) {
2404 int hundreds
= num
/ 100;
2406 if (hundreds
== 1) {
2407 snprintf(fn
, sizeof(fn
), "digits/1N");
2409 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2412 snprintf(fna
, sizeof(fna
), "digits/hundred");
2414 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2417 } else if (num
< 1000000) {
2418 int thousands
= num
/ 1000;
2420 if (thousands
== 1) {
2422 snprintf(fn
, sizeof(fn
), "digits/1N");
2423 snprintf(fna
, sizeof(fna
), "digits/thousand");
2426 snprintf(fn
, sizeof(fn
), "digits/1N");
2427 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2429 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2433 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2438 snprintf(fn
, sizeof(fn
), "digits/thousand");
2440 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2444 } else if (num
< 1000000000) {
2445 int millions
= num
/ 1000000;
2446 num
= num
% 1000000;
2447 if (millions
== 1) {
2449 snprintf(fn
, sizeof(fn
), "digits/1F");
2450 snprintf(fna
, sizeof(fna
), "digits/million");
2452 snprintf(fn
, sizeof(fn
), "digits/1N");
2453 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2456 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2461 snprintf(fn
, sizeof(fn
), "digits/millions");
2463 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2467 } else if (num
< INT_MAX
) {
2468 int billions
= num
/ 1000000000;
2469 num
= num
% 1000000000;
2470 if (billions
== 1) {
2472 snprintf(fn
, sizeof(fn
), "digits/1F");
2473 snprintf(fna
, sizeof(fna
), "digits/milliard");
2475 snprintf(fn
, sizeof(fn
), "digits/1N");
2476 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2479 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2483 snprintf(fn
, sizeof(fna
), "digits/milliards");
2485 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2489 } else if (num
== INT_MAX
) {
2490 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2493 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2498 if (!ast_streamfile(chan
, fn
, language
)) {
2499 if ((audiofd
> -1) && (ctrlfd
> -1))
2500 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2502 res
= ast_waitstream(chan
, ints
);
2504 ast_stopstream(chan
);
2506 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2507 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2508 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2510 res
= ast_waitstream(chan
, ints
);
2513 ast_stopstream(chan
);
2521 /*! \brief ast_say_enumeration_full_de: German syntax */
2522 static int ast_say_enumeration_full_de(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2524 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2526 char fn
[256] = "", fna
[256] = "";
2529 if (options
&& !strncasecmp(options
, "f",1)) {
2531 } else if (options
&& !strncasecmp(options
, "n",1)) {
2538 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2540 while(!res
&& num
) {
2542 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2543 if ( num
> INT_MIN
) {
2548 } else if (num
< 100 && t
) {
2549 snprintf(fn
, sizeof(fn
), "digits/and");
2551 } else if (num
< 20) {
2552 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2554 } else if (num
< 100) {
2555 int ones
= num
% 10;
2557 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2560 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2563 } else if (num
== 100 && t
== 0) {
2564 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2566 } else if (num
< 1000) {
2567 int hundreds
= num
/ 100;
2569 if (hundreds
== 1) {
2570 snprintf(fn
, sizeof(fn
), "digits/1N");
2572 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2575 snprintf(fna
, sizeof(fna
), "digits/hundred");
2577 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2580 } else if (num
< 1000000) {
2581 int thousands
= num
/ 1000;
2583 if (thousands
== 1) {
2585 snprintf(fn
, sizeof(fn
), "digits/1N");
2586 snprintf(fna
, sizeof(fna
), "digits/thousand");
2589 snprintf(fn
, sizeof(fn
), "digits/1N");
2590 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2592 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2596 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2601 snprintf(fn
, sizeof(fn
), "digits/thousand");
2603 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2607 } else if (num
< 1000000000) {
2608 int millions
= num
/ 1000000;
2609 num
= num
% 1000000;
2610 if (millions
== 1) {
2612 snprintf(fn
, sizeof(fn
), "digits/1F");
2613 snprintf(fna
, sizeof(fna
), "digits/million");
2615 snprintf(fn
, sizeof(fn
), "digits/1N");
2616 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2619 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2624 snprintf(fn
, sizeof(fn
), "digits/millions");
2626 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2630 } else if (num
< INT_MAX
) {
2631 int billions
= num
/ 1000000000;
2632 num
= num
% 1000000000;
2633 if (billions
== 1) {
2635 snprintf(fn
, sizeof(fn
), "digits/1F");
2636 snprintf(fna
, sizeof(fna
), "digits/milliard");
2638 snprintf(fn
, sizeof(fn
), "digits/1N");
2639 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2642 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2646 snprintf(fn
, sizeof(fna
), "digits/milliards");
2648 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2652 } else if (num
== INT_MAX
) {
2653 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2656 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2661 if (!ast_streamfile(chan
, fn
, language
)) {
2662 if ((audiofd
> -1) && (ctrlfd
> -1))
2663 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2665 res
= ast_waitstream(chan
, ints
);
2667 ast_stopstream(chan
);
2669 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2670 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2671 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2673 res
= ast_waitstream(chan
, ints
);
2676 ast_stopstream(chan
);
2684 static int say_date(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2686 if (!strcasecmp(lang
, "en") ) { /* English syntax */
2687 return(ast_say_date_en(chan
, t
, ints
, lang
));
2688 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
2689 return(ast_say_date_da(chan
, t
, ints
, lang
));
2690 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
2691 return(ast_say_date_de(chan
, t
, ints
, lang
));
2692 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
2693 return(ast_say_date_fr(chan
, t
, ints
, lang
));
2694 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
2695 return(ast_say_date_nl(chan
, t
, ints
, lang
));
2696 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
2697 return(ast_say_date_pt(chan
, t
, ints
, lang
));
2698 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
2699 return(ast_say_date_gr(chan
, t
, ints
, lang
));
2702 /* Default to English */
2703 return(ast_say_date_en(chan
, t
, ints
, lang
));
2706 /* English syntax */
2707 int ast_say_date_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2712 ast_localtime(&t
,&tm
,NULL
);
2714 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2715 res
= ast_streamfile(chan
, fn
, lang
);
2717 res
= ast_waitstream(chan
, ints
);
2720 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2721 res
= ast_streamfile(chan
, fn
, lang
);
2723 res
= ast_waitstream(chan
, ints
);
2726 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2728 res
= ast_waitstream(chan
, ints
);
2730 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2735 int ast_say_date_da(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2740 ast_localtime(&t
,&tm
,NULL
);
2742 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2743 res
= ast_streamfile(chan
, fn
, lang
);
2745 res
= ast_waitstream(chan
, ints
);
2748 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2750 res
= ast_waitstream(chan
, ints
);
2752 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2753 res
= ast_streamfile(chan
, fn
, lang
);
2755 res
= ast_waitstream(chan
, ints
);
2759 int year
= tm
.tm_year
+ 1900;
2760 if (year
> 1999) { /* year 2000 and later */
2761 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2764 /* I'm not going to handle 1100 and prior */
2765 /* We'll just be silent on the year, instead of bombing out. */
2767 /* year 1100 to 1999. will anybody need this?!? */
2768 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2769 res
= wait_file(chan
, ints
, fn
, lang
);
2771 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2772 if (!res
&& year
% 100 != 0) {
2773 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
2783 int ast_say_date_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2788 ast_localtime(&t
,&tm
,NULL
);
2790 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2791 res
= ast_streamfile(chan
, fn
, lang
);
2793 res
= ast_waitstream(chan
, ints
);
2796 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2798 res
= ast_waitstream(chan
, ints
);
2800 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2801 res
= ast_streamfile(chan
, fn
, lang
);
2803 res
= ast_waitstream(chan
, ints
);
2807 int year
= tm
.tm_year
+ 1900;
2808 if (year
> 1999) { /* year 2000 and later */
2809 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2812 /* I'm not going to handle 1100 and prior */
2813 /* We'll just be silent on the year, instead of bombing out. */
2815 /* year 1100 to 1999. will anybody need this?!? */
2816 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2817 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2818 res
= wait_file(chan
, ints
, fn
, lang
);
2820 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2821 if (!res
&& year
% 100 != 0) {
2822 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
2832 int ast_say_date_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2837 ast_localtime(&t
,&tm
,NULL
);
2839 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2840 res
= ast_streamfile(chan
, fn
, lang
);
2842 res
= ast_waitstream(chan
, ints
);
2845 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2847 res
= ast_waitstream(chan
, ints
);
2849 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2850 res
= ast_streamfile(chan
, fn
, lang
);
2852 res
= ast_waitstream(chan
, ints
);
2855 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2860 int ast_say_date_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2865 ast_localtime(&t
,&tm
,NULL
);
2867 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2868 res
= ast_streamfile(chan
, fn
, lang
);
2870 res
= ast_waitstream(chan
, ints
);
2873 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2875 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2876 res
= ast_streamfile(chan
, fn
, lang
);
2878 res
= ast_waitstream(chan
, ints
);
2881 res
= ast_waitstream(chan
, ints
);
2883 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2887 /* Portuguese syntax */
2888 int ast_say_date_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2893 ast_localtime(&t
,&tm
,NULL
);
2894 localtime_r(&t
,&tm
);
2895 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2897 res
= wait_file(chan
, ints
, fn
, lang
);
2899 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
2901 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
2902 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2904 res
= wait_file(chan
, ints
, fn
, lang
);
2906 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
2908 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2913 static int say_date_with_format(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
2915 if (!strcasecmp(lang
, "en") ) { /* English syntax */
2916 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
2917 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
2918 return(ast_say_date_with_format_da(chan
, time
, ints
, lang
, format
, timezone
));
2919 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
2920 return(ast_say_date_with_format_de(chan
, time
, ints
, lang
, format
, timezone
));
2921 } else if (!strcasecmp(lang
, "es") || !strcasecmp(lang
, "mx")) { /* Spanish syntax */
2922 return(ast_say_date_with_format_es(chan
, time
, ints
, lang
, format
, timezone
));
2923 } else if (!strcasecmp(lang
, "he")) { /* Hebrew syntax */
2924 return(ast_say_date_with_format_he(chan
, time
, ints
, lang
, format
, timezone
));
2925 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
2926 return(ast_say_date_with_format_fr(chan
, time
, ints
, lang
, format
, timezone
));
2927 } else if (!strcasecmp(lang
, "it") ) { /* Italian syntax */
2928 return(ast_say_date_with_format_it(chan
, time
, ints
, lang
, format
, timezone
));
2929 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
2930 return(ast_say_date_with_format_nl(chan
, time
, ints
, lang
, format
, timezone
));
2931 } else if (!strcasecmp(lang
, "pl") ) { /* Polish syntax */
2932 return(ast_say_date_with_format_pl(chan
, time
, ints
, lang
, format
, timezone
));
2933 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
2934 return(ast_say_date_with_format_pt(chan
, time
, ints
, lang
, format
, timezone
));
2935 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
2936 return(ast_say_date_with_format_tw(chan
, time
, ints
, lang
, format
, timezone
));
2937 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
2938 return(ast_say_date_with_format_gr(chan
, time
, ints
, lang
, format
, timezone
));
2941 /* Default to English */
2942 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
2945 /* English syntax */
2946 int ast_say_date_with_format_en(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
2949 int res
=0, offset
, sndoffset
;
2950 char sndfile
[256], nextmsg
[256];
2953 format
= "ABdY 'digits/at' IMp";
2955 ast_localtime(&time
,&tm
,timezone
);
2957 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
2958 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
2959 switch (format
[offset
]) {
2960 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2962 /* Literal name of a sound file */
2964 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
2965 sndfile
[sndoffset
] = format
[offset
];
2966 sndfile
[sndoffset
] = '\0';
2967 res
= wait_file(chan
,ints
,sndfile
,lang
);
2971 /* Sunday - Saturday */
2972 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
2973 res
= wait_file(chan
,ints
,nextmsg
,lang
);
2978 /* January - December */
2979 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
2980 res
= wait_file(chan
,ints
,nextmsg
,lang
);
2983 /* Month enumerated */
2984 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, (char *) NULL
);
2988 /* First - Thirtyfirst */
2989 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
2993 if (tm
.tm_year
> 99) {
2994 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2995 } else if (tm
.tm_year
< 1) {
2996 /* I'm not going to handle 1900 and prior */
2997 /* We'll just be silent on the year, instead of bombing out. */
2999 res
= wait_file(chan
, ints
, "digits/19", lang
);
3001 if (tm
.tm_year
<= 9) {
3003 res
= wait_file(chan
,ints
, "digits/oh", lang
);
3006 res
|= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char *) NULL
);
3013 if (tm
.tm_hour
== 0)
3014 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3015 else if (tm
.tm_hour
> 12)
3016 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3018 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3019 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3024 if (format
[offset
] == 'H') {
3026 if (tm
.tm_hour
< 10) {
3027 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3031 if (tm
.tm_hour
== 0) {
3032 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3036 if (tm
.tm_hour
!= 0) {
3037 int remainder
= tm
.tm_hour
;
3038 if (tm
.tm_hour
> 20) {
3039 res
= wait_file(chan
,ints
, "digits/20",lang
);
3043 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
3044 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3052 if (tm
.tm_min
== 0) {
3053 if (format
[offset
] == 'M') {
3054 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
3056 res
= wait_file(chan
, ints
, "digits/hundred", lang
);
3058 } else if (tm
.tm_min
< 10) {
3059 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3061 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
3062 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3065 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
3071 if (tm
.tm_hour
> 11)
3072 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3074 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3075 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3078 /* Shorthand for "Today", "Yesterday", or ABdY */
3079 /* XXX As emphasized elsewhere, this should the native way in your
3080 * language to say the date, with changes in what you say, depending
3081 * upon how recent the date is. XXX */
3085 time_t beg_today
, tt
;
3087 gettimeofday(&now
,NULL
);
3089 ast_localtime(&tt
,&tmnow
,timezone
);
3090 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3091 /* In any case, it saves not having to do ast_mktime() */
3092 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3093 if (beg_today
< time
) {
3095 res
= wait_file(chan
,ints
, "digits/today",lang
);
3096 } else if (beg_today
- 86400 < time
) {
3098 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3099 } else if (beg_today
- 86400 * 6 < time
) {
3100 /* Within the last week */
3101 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3102 } else if (beg_today
- 2628000 < time
) {
3103 /* Less than a month ago - "Sunday, October third" */
3104 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3105 } else if (beg_today
- 15768000 < time
) {
3106 /* Less than 6 months ago - "August seventh" */
3107 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3109 /* More than 6 months ago - "April nineteenth two thousand three" */
3110 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3115 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3116 /* XXX As emphasized elsewhere, this should the native way in your
3117 * language to say the date, with changes in what you say, depending
3118 * upon how recent the date is. XXX */
3122 time_t beg_today
, tt
;
3124 gettimeofday(&now
,NULL
);
3126 ast_localtime(&tt
,&tmnow
,timezone
);
3127 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3128 /* In any case, it saves not having to do ast_mktime() */
3129 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3130 if (beg_today
< time
) {
3132 } else if ((beg_today
- 86400) < time
) {
3134 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3135 } else if (beg_today
- 86400 * 6 < time
) {
3136 /* Within the last week */
3137 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3138 } else if (beg_today
- 2628000 < time
) {
3139 /* Less than a month ago - "Sunday, October third" */
3140 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3141 } else if (beg_today
- 15768000 < time
) {
3142 /* Less than 6 months ago - "August seventh" */
3143 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3145 /* More than 6 months ago - "April nineteenth two thousand three" */
3146 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3151 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HM", timezone
);
3155 if (tm
.tm_sec
== 0) {
3156 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3157 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3158 } else if (tm
.tm_sec
< 10) {
3159 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3161 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3162 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3165 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, (char *) NULL
);
3169 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HMS", timezone
);
3173 /* Just ignore spaces and tabs */
3176 /* Unknown character */
3177 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3179 /* Jump out on DTMF */
3188 int ast_say_date_with_format_da(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
3191 int res
=0, offset
, sndoffset
;
3192 char sndfile
[256], nextmsg
[256];
3195 format
= "A dBY HMS";
3197 ast_localtime(&time
,&tm
,timezone
);
3199 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3200 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3201 switch (format
[offset
]) {
3202 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3204 /* Literal name of a sound file */
3206 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3207 sndfile
[sndoffset
] = format
[offset
];
3208 sndfile
[sndoffset
] = '\0';
3209 res
= wait_file(chan
,ints
,sndfile
,lang
);
3213 /* Sunday - Saturday */
3214 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3215 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3220 /* January - December */
3221 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3222 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3225 /* Month enumerated */
3226 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3230 /* First - Thirtyfirst */
3231 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3236 int year
= tm
.tm_year
+ 1900;
3237 if (year
> 1999) { /* year 2000 and later */
3238 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3241 /* I'm not going to handle 1100 and prior */
3242 /* We'll just be silent on the year, instead of bombing out. */
3244 /* year 1100 to 1999. will anybody need this?!? */
3245 /* say 1967 as 'nineteen hundred seven and sixty' */
3246 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3247 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3249 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3250 if (!res
&& year
% 100 != 0) {
3251 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3261 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3262 if (tm
.tm_hour
== 0)
3263 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3264 else if (tm
.tm_hour
> 12)
3265 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3267 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3269 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3273 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3274 if (tm
.tm_hour
< 10 && tm
.tm_hour
> 0) {
3275 res
= wait_file(chan
,ints
, "digits/0",lang
);
3280 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3284 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3285 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3287 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3288 if (tm
.tm_min
== 1) {
3289 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3291 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3298 if (tm
.tm_hour
> 11)
3299 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3301 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3302 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3305 /* Shorthand for "Today", "Yesterday", or AdBY */
3306 /* XXX As emphasized elsewhere, this should the native way in your
3307 * language to say the date, with changes in what you say, depending
3308 * upon how recent the date is. XXX */
3312 time_t beg_today
, tt
;
3314 gettimeofday(&now
,NULL
);
3316 ast_localtime(&tt
,&tmnow
,timezone
);
3317 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3318 /* In any case, it saves not having to do ast_mktime() */
3319 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3320 if (beg_today
< time
) {
3322 res
= wait_file(chan
,ints
, "digits/today",lang
);
3323 } else if (beg_today
- 86400 < time
) {
3325 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3327 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3332 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3333 /* XXX As emphasized elsewhere, this should the native way in your
3334 * language to say the date, with changes in what you say, depending
3335 * upon how recent the date is. XXX */
3339 time_t beg_today
, tt
;
3341 gettimeofday(&now
,NULL
);
3343 ast_localtime(&tt
,&tmnow
,timezone
);
3344 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3345 /* In any case, it saves not having to do ast_mktime() */
3346 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3347 if (beg_today
< time
) {
3349 } else if ((beg_today
- 86400) < time
) {
3351 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3352 } else if (beg_today
- 86400 * 6 < time
) {
3353 /* Within the last week */
3354 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "A", timezone
);
3356 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3361 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HM", timezone
);
3365 res
= wait_file(chan
,ints
, "digits/and",lang
);
3367 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3369 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3374 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HMS", timezone
);
3378 /* Just ignore spaces and tabs */
3381 /* Unknown character */
3382 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3384 /* Jump out on DTMF */
3393 int ast_say_date_with_format_de(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
3396 int res
=0, offset
, sndoffset
;
3397 char sndfile
[256], nextmsg
[256];
3400 format
= "A dBY HMS";
3402 ast_localtime(&time
,&tm
,timezone
);
3404 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3405 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3406 switch (format
[offset
]) {
3407 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3409 /* Literal name of a sound file */
3411 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3412 sndfile
[sndoffset
] = format
[offset
];
3413 sndfile
[sndoffset
] = '\0';
3414 res
= wait_file(chan
,ints
,sndfile
,lang
);
3418 /* Sunday - Saturday */
3419 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3420 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3425 /* January - December */
3426 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3427 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3430 /* Month enumerated */
3431 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3435 /* First - Thirtyfirst */
3436 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3441 int year
= tm
.tm_year
+ 1900;
3442 if (year
> 1999) { /* year 2000 and later */
3443 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3446 /* I'm not going to handle 1100 and prior */
3447 /* We'll just be silent on the year, instead of bombing out. */
3449 /* year 1100 to 1999. will anybody need this?!? */
3450 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3451 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3452 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3454 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3455 if (!res
&& year
% 100 != 0) {
3456 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3466 if (tm
.tm_hour
== 0)
3467 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3468 else if (tm
.tm_hour
> 12)
3469 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3471 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3472 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3474 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3480 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3482 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3487 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3488 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3490 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3491 if (tm
.tm_min
== 1) {
3492 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3494 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3501 if (tm
.tm_hour
> 11)
3502 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3504 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3505 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3508 /* Shorthand for "Today", "Yesterday", or AdBY */
3509 /* XXX As emphasized elsewhere, this should the native way in your
3510 * language to say the date, with changes in what you say, depending
3511 * upon how recent the date is. XXX */
3515 time_t beg_today
, tt
;
3517 gettimeofday(&now
,NULL
);
3519 ast_localtime(&tt
,&tmnow
,timezone
);
3520 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3521 /* In any case, it saves not having to do ast_mktime() */
3522 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3523 if (beg_today
< time
) {
3525 res
= wait_file(chan
,ints
, "digits/today",lang
);
3526 } else if (beg_today
- 86400 < time
) {
3528 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3530 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3535 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3536 /* XXX As emphasized elsewhere, this should the native way in your
3537 * language to say the date, with changes in what you say, depending
3538 * upon how recent the date is. XXX */
3542 time_t beg_today
, tt
;
3544 gettimeofday(&now
,NULL
);
3546 ast_localtime(&tt
,&tmnow
,timezone
);
3547 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3548 /* In any case, it saves not having to do ast_mktime() */
3549 beg_today
= now
.tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3550 if (beg_today
< time
) {
3552 } else if ((beg_today
- 86400) < time
) {
3554 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3555 } else if (beg_today
- 86400 * 6 < time
) {
3556 /* Within the last week */
3557 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "A", timezone
);
3559 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3564 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HM", timezone
);
3568 res
= wait_file(chan
,ints
, "digits/and",lang
);
3570 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3572 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3577 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HMS", timezone
);
3581 /* Just ignore spaces and tabs */
3584 /* Unknown character */
3585 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3587 /* Jump out on DTMF */
3595 /* TODO: this probably is not the correct format for doxygen remarks */
3597 /** ast_say_date_with_format_he Say formmated date in Hebrew
3599 * \ref ast_say_date_with_format_en for the details of the options
3601 * Changes from the English version:
3603 * * don't replicate in here the logic of ast_say_number_full_he
3605 * * year is always 4-digit (because it's simpler)
3607 * * added c, x, and X. Mainly for my tests
3609 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3612 * * A "ha" is missing in the standard date format, before the 'd'.
3613 * * The numbers of 3000--19000 are not handled well
3615 #define IL_DATE_STR "AdBY"
3616 #define IL_TIME_STR "IMp"
3617 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3618 int ast_say_date_with_format_he(struct ast_channel
*chan
, time_t time
,
3619 const char *ints
, const char *lang
, const char *format
,
3620 const char *timezone
)
3622 /* TODO: This whole function is cut&paste from
3623 * ast_say_date_with_format_en . Is that considered acceptable?
3626 int res
=0, offset
, sndoffset
;
3627 char sndfile
[256], nextmsg
[256];
3630 format
= IL_DATE_STR_FULL
;
3632 ast_localtime(&time
,&tm
,timezone
);
3634 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3635 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3636 switch (format
[offset
]) {
3637 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3639 /* Literal name of a sound file */
3641 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3642 sndfile
[sndoffset
] = format
[offset
];
3643 sndfile
[sndoffset
] = '\0';
3644 res
= wait_file(chan
,ints
,sndfile
,lang
);
3648 /* Sunday - Saturday */
3649 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3650 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3655 /* January - December */
3656 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3657 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3660 case 'e': /* Day of the month */
3661 /* I'm not sure exactly what the parameters
3662 * audiofd and ctrlfd to
3663 * ast_say_number_full_he mean, but it seems
3664 * safe to pass -1 there.
3666 * At least in one of the pathes :-(
3668 res
= ast_say_number_full_he(chan
, tm
.tm_mday
,
3669 ints
, lang
, "m", -1, -1
3672 case 'Y': /* Year */
3673 res
= ast_say_number_full_he(chan
, tm
.tm_year
+1900,
3674 ints
, lang
, "f", -1, -1
3678 case 'l': /* 12-Hour */
3680 int hour
= tm
.tm_hour
;
3682 if (hour
== 0) hour
=12;
3684 res
= ast_say_number_full_he(chan
, hour
,
3685 ints
, lang
, "f", -1, -1
3690 case 'k': /* 24-Hour */
3691 /* With 'H' there is an 'oh' after a single-
3693 if ((format
[offset
] == 'H') &&
3694 (tm
.tm_hour
<10)&&(tm
.tm_hour
>0)
3695 ) { /* e.g. oh-eight */
3696 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3699 res
= ast_say_number_full_he(chan
, tm
.tm_hour
,
3700 ints
, lang
, "f", -1, -1
3703 case 'M': /* Minute */
3704 res
= ast_say_number_full_he(chan
, tm
.tm_min
,
3705 ints
, lang
,"f", -1, -1
3711 if (tm
.tm_hour
> 11)
3712 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3714 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3715 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3718 /* Shorthand for "Today", "Yesterday", or "date" */
3720 /* Shorthand for "" (today), "Yesterday", A
3721 * (weekday), or "date" */
3722 /* XXX As emphasized elsewhere, this should the native way in your
3723 * language to say the date, with changes in what you say, depending
3724 * upon how recent the date is. XXX */
3728 time_t beg_today
, tt
;
3729 char todo
= format
[offset
]; /* The letter to format*/
3731 gettimeofday(&now
,NULL
);
3733 ast_localtime(&tt
,&tmnow
,timezone
);
3734 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3735 /* In any case, it saves not having to do ast_mktime() */
3736 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3737 if (beg_today
< time
) {
3740 res
= wait_file(chan
,
3745 } else if (beg_today
- 86400 < time
) {
3747 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3748 } else if ((todo
!= 'Q') &&
3749 (beg_today
- 86400 * 6 < time
))
3751 /* Within the last week */
3752 res
= ast_say_date_with_format_he(chan
,
3756 res
= ast_say_date_with_format_he(chan
,
3758 IL_DATE_STR
, timezone
);
3763 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HM", timezone
);
3765 case 'S': /* Seconds */
3766 res
= ast_say_number_full_he(chan
, tm
.tm_sec
,
3767 ints
, lang
, "f", -1, -1
3771 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HMS", timezone
);
3773 /* c, x, and X seem useful for testing. Not sure
3774 * if thiey're good for the general public */
3776 res
= ast_say_date_with_format_he(chan
, time
,
3777 ints
, lang
, IL_DATE_STR_FULL
, timezone
);
3780 res
= ast_say_date_with_format_he(chan
, time
,
3781 ints
, lang
, IL_DATE_STR
, timezone
);
3783 case 'X': /* Currently not locale-dependent...*/
3784 res
= ast_say_date_with_format_he(chan
, time
,
3785 ints
, lang
, IL_TIME_STR
, timezone
);
3789 /* Just ignore spaces and tabs */
3792 /* Unknown character */
3793 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3795 /* Jump out on DTMF */
3804 /* Spanish syntax */
3805 int ast_say_date_with_format_es(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
3808 int res
=0, offset
, sndoffset
;
3809 char sndfile
[256], nextmsg
[256];
3812 format
= "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
3814 ast_localtime(&time
,&tm
,timezone
);
3816 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3817 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3818 switch (format
[offset
]) {
3819 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3821 /* Literal name of a sound file */
3823 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3824 sndfile
[sndoffset
] = format
[offset
];
3825 sndfile
[sndoffset
] = '\0';
3826 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
3827 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3831 /* Sunday - Saturday */
3832 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3833 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3838 /* January - December */
3839 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3840 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3843 /* First - Twelfth */
3844 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
3845 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3849 /* First - Thirtyfirst */
3850 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
3854 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
3859 if (tm
.tm_hour
== 0)
3860 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3861 else if (tm
.tm_hour
> 12)
3862 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3864 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3865 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3870 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, NULL
);
3874 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
3879 if (tm
.tm_hour
> 18)
3880 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
3881 else if (tm
.tm_hour
> 12)
3882 res
= wait_file(chan
, ints
, "digits/afternoon", lang
);
3883 else if (tm
.tm_hour
)
3884 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
3887 /* Shorthand for "Today", "Yesterday", or ABdY */
3888 /* XXX As emphasized elsewhere, this should the native way in your
3889 * language to say the date, with changes in what you say, depending
3890 * upon how recent the date is. XXX */
3894 time_t beg_today
, tt
;
3896 gettimeofday(&now
,NULL
);
3898 ast_localtime(&tt
,&tmnow
,timezone
);
3899 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3900 /* In any case, it saves not having to do ast_mktime() */
3901 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3902 if (beg_today
< time
) {
3904 res
= wait_file(chan
,ints
, "digits/today",lang
);
3905 } else if (beg_today
- 86400 < time
) {
3907 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3909 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
3914 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3915 /* XXX As emphasized elsewhere, this should the native way in your
3916 * language to say the date, with changes in what you say, depending
3917 * upon how recent the date is. XXX */
3921 time_t beg_today
, tt
;
3923 gettimeofday(&now
,NULL
);
3925 ast_localtime(&tt
,&tmnow
,timezone
);
3926 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3927 /* In any case, it saves not having to do ast_mktime() */
3928 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3929 if (beg_today
< time
) {
3931 res
= wait_file(chan
,ints
, "digits/today",lang
);
3932 } else if ((beg_today
- 86400) < time
) {
3934 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3935 } else if (beg_today
- 86400 * 6 < time
) {
3936 /* Within the last week */
3937 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "A", timezone
);
3939 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
3944 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "H 'digits/y' M", timezone
);
3948 if (tm
.tm_sec
== 0) {
3949 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3950 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3951 } else if (tm
.tm_sec
< 10) {
3952 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3954 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3955 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3957 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
3958 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3959 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3962 ten
= (tm
.tm_sec
/ 10) * 10;
3963 one
= (tm
.tm_sec
% 10);
3964 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
3965 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3967 /* Fifty, not fifty-zero */
3969 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
3970 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3976 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "HMS", timezone
);
3980 /* Just ignore spaces and tabs */
3983 /* Unknown character */
3984 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3986 /* Jump out on DTMF */
3997 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
)
4000 int res
=0, offset
, sndoffset
;
4001 char sndfile
[256], nextmsg
[256];
4004 format
= "AdBY 'digits/at' IMp";
4006 ast_localtime(&time
,&tm
,timezone
);
4008 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4009 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4010 switch (format
[offset
]) {
4011 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4013 /* Literal name of a sound file */
4015 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4016 sndfile
[sndoffset
] = format
[offset
];
4017 sndfile
[sndoffset
] = '\0';
4018 res
= wait_file(chan
,ints
,sndfile
,lang
);
4022 /* Sunday - Saturday */
4023 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4024 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4029 /* January - December */
4030 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4031 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4034 /* First - Twelfth */
4035 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4036 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4041 if (tm
.tm_mday
== 1) {
4042 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4043 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4045 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
4050 if (tm
.tm_year
> 99) {
4051 res
= wait_file(chan
,ints
, "digits/2",lang
);
4053 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4055 if (tm
.tm_year
> 100) {
4057 res
= ast_say_number(chan
, tm
.tm_year
- 100, ints
, lang
, (char * ) NULL
);
4061 if (tm
.tm_year
< 1) {
4062 /* I'm not going to handle 1900 and prior */
4063 /* We'll just be silent on the year, instead of bombing out. */
4065 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4067 wait_file(chan
,ints
, "digits/9",lang
);
4068 wait_file(chan
,ints
, "digits/hundred",lang
);
4069 res
= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char * ) NULL
);
4077 if (tm
.tm_hour
== 0)
4078 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4079 else if (tm
.tm_hour
> 12)
4080 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4082 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4083 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4085 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4090 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char * ) NULL
);
4092 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4096 if (tm
.tm_min
== 0) {
4099 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char * ) NULL
);
4104 if (tm
.tm_hour
> 11)
4105 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4107 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4108 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4111 /* Shorthand for "Today", "Yesterday", or AdBY */
4112 /* XXX As emphasized elsewhere, this should the native way in your
4113 * language to say the date, with changes in what you say, depending
4114 * upon how recent the date is. XXX */
4118 time_t beg_today
, tt
;
4120 gettimeofday(&now
,NULL
);
4122 ast_localtime(&tt
,&tmnow
,timezone
);
4123 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4124 /* In any case, it saves not having to do ast_mktime() */
4125 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4126 if (beg_today
< time
) {
4128 res
= wait_file(chan
,ints
, "digits/today",lang
);
4129 } else if (beg_today
- 86400 < time
) {
4131 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4133 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4138 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4139 /* XXX As emphasized elsewhere, this should the native way in your
4140 * language to say the date, with changes in what you say, depending
4141 * upon how recent the date is. XXX */
4145 time_t beg_today
, tt
;
4147 gettimeofday(&now
,NULL
);
4149 ast_localtime(&tt
,&tmnow
,timezone
);
4150 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4151 /* In any case, it saves not having to do ast_mktime() */
4152 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4153 if (beg_today
< time
) {
4155 } else if ((beg_today
- 86400) < time
) {
4157 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4158 } else if (beg_today
- 86400 * 6 < time
) {
4159 /* Within the last week */
4160 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "A", timezone
);
4162 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4167 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HM", timezone
);
4171 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char * ) NULL
);
4173 res
= wait_file(chan
,ints
, "digits/second",lang
);
4177 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HMS", timezone
);
4181 /* Just ignore spaces and tabs */
4184 /* Unknown character */
4185 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4187 /* Jump out on DTMF */
4195 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
)
4198 int res
=0, offset
, sndoffset
;
4199 char sndfile
[256], nextmsg
[256];
4202 format
= "AdB 'digits/at' IMp";
4204 ast_localtime(&time
,&tm
,timezone
);
4206 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4207 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4208 switch (format
[offset
]) {
4209 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4211 /* Literal name of a sound file */
4213 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4214 sndfile
[sndoffset
] = format
[offset
];
4215 sndfile
[sndoffset
] = '\0';
4216 res
= wait_file(chan
,ints
,sndfile
,lang
);
4220 /* Sunday - Saturday */
4221 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4222 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4227 /* January - December */
4228 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4229 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4232 /* First - Twelfth */
4233 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4234 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4238 /* First day of the month is spelled as ordinal */
4239 if (tm
.tm_mday
== 1) {
4240 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4241 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4244 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4250 if (tm
.tm_year
> 99) {
4251 res
= wait_file(chan
,ints
, "digits/ore-2000",lang
);
4252 if (tm
.tm_year
> 100) {
4254 /* This works until the end of 2021 */
4255 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4256 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4260 if (tm
.tm_year
< 1) {
4261 /* I'm not going to handle 1900 and prior */
4262 /* We'll just be silent on the year, instead of bombing out. */
4264 res
= wait_file(chan
,ints
, "digits/ore-1900",lang
);
4265 if ((!res
) && (tm
.tm_year
!= 0)) {
4266 if (tm
.tm_year
<= 21) {
4268 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4269 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4271 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4273 ten
= tm
.tm_year
/ 10;
4274 one
= tm
.tm_year
% 10;
4275 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4276 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4279 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4280 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4291 if (tm
.tm_hour
== 0)
4292 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4293 else if (tm
.tm_hour
> 12)
4294 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4296 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4297 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4302 if (tm
.tm_hour
== 0) {
4303 res
= wait_file(chan
,ints
, "digits/ore-mezzanotte",lang
);
4304 } else if (tm
.tm_hour
== 1) {
4305 res
= wait_file(chan
,ints
, "digits/ore-una",lang
);
4307 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4312 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4317 if (tm
.tm_hour
> 11)
4318 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4320 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4321 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4324 /* Shorthand for "Today", "Yesterday", or ABdY */
4325 /* XXX As emphasized elsewhere, this should the native way in your
4326 * language to say the date, with changes in what you say, depending
4327 * upon how recent the date is. XXX */
4331 time_t beg_today
, tt
;
4333 gettimeofday(&now
,NULL
);
4335 ast_localtime(&tt
,&tmnow
,timezone
);
4336 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4337 /* In any case, it saves not having to do ast_mktime() */
4338 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4339 if (beg_today
< time
) {
4341 res
= wait_file(chan
,ints
, "digits/today",lang
);
4342 } else if (beg_today
- 86400 < time
) {
4344 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4346 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4351 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4355 time_t beg_today
, tt
;
4357 gettimeofday(&now
,NULL
);
4359 ast_localtime(&tt
,&tmnow
,timezone
);
4360 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4361 /* In any case, it saves not having to do ast_mktime() */
4362 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4363 if (beg_today
< time
) {
4365 } else if ((beg_today
- 86400) < time
) {
4367 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4368 } else if (beg_today
- 86400 * 6 < time
) {
4369 /* Within the last week */
4370 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "A", timezone
);
4372 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4377 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HM", timezone
);
4381 if (tm
.tm_sec
== 0) {
4382 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4383 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4384 } else if (tm
.tm_sec
< 10) {
4385 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4387 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4388 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4390 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
4391 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4392 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4395 ten
= (tm
.tm_sec
/ 10) * 10;
4396 one
= (tm
.tm_sec
% 10);
4397 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
4398 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4400 /* Fifty, not fifty-zero */
4402 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4403 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4409 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HMS", timezone
);
4413 /* Just ignore spaces and tabs */
4416 /* Unknown character */
4417 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4419 /* Jump out on DTMF */
4428 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
)
4431 int res
=0, offset
, sndoffset
;
4432 char sndfile
[256], nextmsg
[256];
4435 format
= "ABdY 'digits/at' IMp";
4437 ast_localtime(&time
,&tm
,timezone
);
4439 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4440 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4441 switch (format
[offset
]) {
4442 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4444 /* Literal name of a sound file */
4446 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4447 sndfile
[sndoffset
] = format
[offset
];
4448 sndfile
[sndoffset
] = '\0';
4449 res
= wait_file(chan
,ints
,sndfile
,lang
);
4453 /* Sunday - Saturday */
4454 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4455 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4460 /* January - December */
4461 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4462 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4465 /* First - Twelfth */
4466 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4467 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4471 /* First - Thirtyfirst */
4472 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, NULL
);
4476 if (tm
.tm_year
> 99) {
4477 res
= wait_file(chan
,ints
, "digits/2",lang
);
4479 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4481 if (tm
.tm_year
> 100) {
4483 /* This works until the end of 2020 */
4484 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4485 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4489 if (tm
.tm_year
< 1) {
4490 /* I'm not going to handle 1900 and prior */
4491 /* We'll just be silent on the year, instead of bombing out. */
4493 res
= wait_file(chan
,ints
, "digits/19",lang
);
4495 if (tm
.tm_year
<= 9) {
4497 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4499 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4500 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4502 } else if (tm
.tm_year
<= 20) {
4504 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4505 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4509 ten
= tm
.tm_year
/ 10;
4510 one
= tm
.tm_year
% 10;
4511 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4512 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4515 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4516 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4527 if (tm
.tm_hour
== 0)
4528 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4529 else if (tm
.tm_hour
> 12)
4530 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4532 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4533 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4538 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4540 res
= wait_file(chan
,ints
, "digits/nl-uur",lang
);
4545 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4550 if (tm
.tm_hour
> 11)
4551 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4553 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4554 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4557 /* Shorthand for "Today", "Yesterday", or ABdY */
4558 /* XXX As emphasized elsewhere, this should the native way in your
4559 * language to say the date, with changes in what you say, depending
4560 * upon how recent the date is. XXX */
4564 time_t beg_today
, tt
;
4566 gettimeofday(&now
,NULL
);
4568 ast_localtime(&tt
,&tmnow
,timezone
);
4569 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4570 /* In any case, it saves not having to do ast_mktime() */
4571 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4572 if (beg_today
< time
) {
4574 res
= wait_file(chan
,ints
, "digits/today",lang
);
4575 } else if (beg_today
- 86400 < time
) {
4577 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4579 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4584 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4588 time_t beg_today
, tt
;
4590 gettimeofday(&now
,NULL
);
4592 ast_localtime(&tt
,&tmnow
,timezone
);
4593 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4594 /* In any case, it saves not having to do ast_mktime() */
4595 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4596 if (beg_today
< time
) {
4598 } else if ((beg_today
- 86400) < time
) {
4600 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4601 } else if (beg_today
- 86400 * 6 < time
) {
4602 /* Within the last week */
4603 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "A", timezone
);
4605 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4610 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HM", timezone
);
4614 if (tm
.tm_sec
== 0) {
4615 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4616 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4617 } else if (tm
.tm_sec
< 10) {
4618 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4620 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4621 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4623 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
4624 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4625 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4628 ten
= (tm
.tm_sec
/ 10) * 10;
4629 one
= (tm
.tm_sec
% 10);
4630 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
4631 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4633 /* Fifty, not fifty-zero */
4635 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4636 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4642 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HMS", timezone
);
4646 /* Just ignore spaces and tabs */
4649 /* Unknown character */
4650 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4652 /* Jump out on DTMF */
4661 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
)
4664 int res
=0, offset
, sndoffset
;
4665 char sndfile
[256], nextmsg
[256];
4667 ast_localtime(&thetime
, &tm
, timezone
);
4669 for (offset
= 0 ; format
[offset
] != '\0' ; offset
++) {
4671 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4672 switch (format
[offset
]) {
4673 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4675 /* Literal name of a sound file */
4677 for (sndoffset
= 0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4678 sndfile
[sndoffset
] = format
[offset
];
4679 sndfile
[sndoffset
] = '\0';
4680 res
= wait_file(chan
, ints
, sndfile
, lang
);
4684 /* Sunday - Saturday */
4685 snprintf(nextmsg
, sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4686 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4691 /* January - December */
4692 snprintf(nextmsg
, sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4693 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4696 /* Month enumerated */
4697 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, NULL
);
4701 /* First - Thirtyfirst */
4702 remainder
= tm
.tm_mday
;
4703 if (tm
.tm_mday
> 30) {
4704 res
= wait_file(chan
, ints
, "digits/h-30", lang
);
4707 if (tm
.tm_mday
> 20 && tm
.tm_mday
< 30) {
4708 res
= wait_file(chan
, ints
, "digits/h-20", lang
);
4712 snprintf(nextmsg
, sizeof(nextmsg
), "digits/h-%d", remainder
);
4713 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4718 if (tm
.tm_year
> 100) {
4719 res
= wait_file(chan
, ints
, "digits/2", lang
);
4721 res
= wait_file(chan
, ints
, "digits/1000.2",lang
);
4722 if (tm
.tm_year
> 100) {
4724 res
= ast_say_enumeration(chan
, tm
.tm_year
- 100, ints
, lang
, NULL
);
4726 } else if (tm
.tm_year
== 100) {
4727 res
= wait_file(chan
, ints
, "digits/h-2000", lang
);
4729 if (tm
.tm_year
< 1) {
4730 /* I'm not going to handle 1900 and prior */
4731 /* We'll just be silent on the year, instead of bombing out. */
4734 res
= wait_file(chan
, ints
, "digits/1000", lang
);
4736 wait_file(chan
, ints
, "digits/900", lang
);
4737 res
= ast_say_enumeration(chan
, tm
.tm_year
, ints
, lang
, NULL
);
4742 wait_file(chan
, ints
, "digits/year", lang
);
4747 if (tm
.tm_hour
== 0)
4748 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-12");
4749 else if (tm
.tm_hour
> 12)
4750 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
- 12);
4752 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4754 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4759 if (tm
.tm_hour
!= 0) {
4760 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4761 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4763 res
= wait_file(chan
, ints
, "digits/t-24", lang
);
4768 if (tm
.tm_min
== 0) {
4769 if (format
[offset
] == 'M') {
4770 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
4772 res
= wait_file(chan
, ints
, "digits/100", lang
);
4775 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4780 if (tm
.tm_hour
> 11)
4781 snprintf(nextmsg
, sizeof(nextmsg
), "digits/p-m");
4783 snprintf(nextmsg
, sizeof(nextmsg
), "digits/a-m");
4784 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4787 /* Shorthand for "Today", "Yesterday", or AdBY */
4789 time_t tv_sec
= time(NULL
);
4793 ast_localtime(&tv_sec
,&tmnow
, timezone
);
4794 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4795 /* In any case, it saves not having to do ast_mktime() */
4796 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4797 if (beg_today
< thetime
) {
4799 res
= wait_file(chan
, ints
, "digits/today", lang
);
4800 } else if (beg_today
- 86400 < thetime
) {
4802 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4804 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4809 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4811 time_t tv_sec
= time(NULL
);
4815 ast_localtime(&tv_sec
, &tmnow
, timezone
);
4816 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4817 /* In any case, it saves not having to do ast_mktime() */
4818 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4819 if (beg_today
< thetime
) {
4821 } else if ((beg_today
- 86400) < thetime
) {
4823 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4824 } else if (beg_today
- 86400 * 6 < thetime
) {
4825 /* Within the last week */
4826 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "A", timezone
);
4828 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4833 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HM", timezone
);
4837 res
= wait_file(chan
, ints
, "digits/and", lang
);
4839 if (tm
.tm_sec
== 1) {
4840 res
= wait_file(chan
, ints
, "digits/1z", lang
);
4842 res
= wait_file(chan
, ints
, "digits/second-a", lang
);
4844 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4847 ten
= tm
.tm_sec
/ 10;
4848 one
= tm
.tm_sec
% 10;
4850 if (one
> 1 && one
< 5 && ten
!= 1)
4851 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
4853 res
= wait_file(chan
,ints
, "digits/second",lang
);
4859 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HMS", timezone
);
4863 /* Just ignore spaces and tabs */
4866 /* Unknown character */
4867 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4869 /* Jump out on DTMF */
4876 /* Portuguese syntax */
4877 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
)
4880 int res
=0, offset
, sndoffset
;
4881 char sndfile
[256], nextmsg
[256];
4884 format
= "Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/at' IMp";
4886 ast_localtime(&time
,&tm
,timezone
);
4888 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4889 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4890 switch (format
[offset
]) {
4891 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4893 /* Literal name of a sound file */
4895 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4896 sndfile
[sndoffset
] = format
[offset
];
4897 sndfile
[sndoffset
] = '\0';
4898 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
4899 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4903 /* Sunday - Saturday */
4904 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4905 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4910 /* January - December */
4911 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4912 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4915 /* First - Twelfth */
4916 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4917 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4921 /* First - Thirtyfirst */
4922 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4926 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
4931 if (tm
.tm_hour
== 0) {
4932 if (format
[offset
] == 'I')
4933 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
4935 res
= wait_file(chan
, ints
, "digits/pt-meianoite", lang
);
4937 else if (tm
.tm_hour
== 12) {
4938 if (format
[offset
] == 'I')
4939 res
= wait_file(chan
, ints
, "digits/pt-ao", lang
);
4941 res
= wait_file(chan
, ints
, "digits/pt-meiodia", lang
);
4944 if (format
[offset
] == 'I') {
4945 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
4946 if ((tm
.tm_hour
% 12) != 1)
4948 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
4951 res
= ast_say_number(chan
, (tm
.tm_hour
% 12), ints
, lang
, "f");
4957 res
= ast_say_number(chan
, -tm
.tm_hour
, ints
, lang
, NULL
);
4959 if (tm
.tm_hour
!= 0) {
4960 int remainder
= tm
.tm_hour
;
4961 if (tm
.tm_hour
> 20) {
4962 res
= wait_file(chan
,ints
, "digits/20",lang
);
4966 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
4967 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4974 if (tm
.tm_min
== 0) {
4975 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
4976 if (tm
.tm_hour
!= 1)
4978 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
); } else {
4979 res
= wait_file(chan
,ints
,"digits/pt-e",lang
);
4981 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4987 if (tm
.tm_hour
> 12)
4988 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
4989 else if (tm
.tm_hour
&& tm
.tm_hour
< 12)
4990 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
4993 /* Shorthand for "Today", "Yesterday", or ABdY */
4994 /* XXX As emphasized elsewhere, this should the native way in your
4995 * language to say the date, with changes in what you say, depending
4996 * upon how recent the date is. XXX */
5000 time_t beg_today
, tt
;
5002 gettimeofday(&now
,NULL
);
5004 ast_localtime(&tt
,&tmnow
,timezone
);
5005 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5006 /* In any case, it saves not having to do ast_mktime() */
5007 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5008 if (beg_today
< time
) {
5010 res
= wait_file(chan
,ints
, "digits/today",lang
);
5011 } else if (beg_today
- 86400 < time
) {
5013 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5015 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5020 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5021 /* XXX As emphasized elsewhere, this should the native way in your
5022 * language to say the date, with changes in what you say, depending
5023 * upon how recent the date is. XXX */
5027 time_t beg_today
, tt
;
5029 gettimeofday(&now
,NULL
);
5031 ast_localtime(&tt
,&tmnow
,timezone
);
5032 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5033 /* In any case, it saves not having to do ast_mktime() */
5034 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5035 if (beg_today
< time
) {
5037 } else if ((beg_today
- 86400) < time
) {
5039 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5040 } else if (beg_today
- 86400 * 6 < time
) {
5041 /* Within the last week */
5042 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "A", timezone
);
5044 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5049 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "H 'digits/pt-e' M", timezone
);
5053 if (tm
.tm_sec
== 0) {
5054 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5055 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5056 } else if (tm
.tm_sec
< 10) {
5057 res
= wait_file(chan
,ints
, "digits/oh",lang
);
5059 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5060 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5062 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
5063 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5064 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5067 ten
= (tm
.tm_sec
/ 10) * 10;
5068 one
= (tm
.tm_sec
% 10);
5069 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
5070 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5072 /* Fifty, not fifty-zero */
5074 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
5075 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5081 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "HMS", timezone
);
5085 /* Just ignore spaces and tabs */
5088 /* Unknown character */
5089 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5091 /* Jump out on DTMF */
5099 /* Taiwanese / Chinese syntax */
5100 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
)
5103 int res
=0, offset
, sndoffset
;
5104 char sndfile
[256], nextmsg
[256];
5107 format
= "YBdA 'digits/at' HM";
5109 ast_localtime(&time
,&tm
,timezone
);
5111 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
5112 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
5113 switch (format
[offset
]) {
5114 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5116 /* Literal name of a sound file */
5118 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
5119 sndfile
[sndoffset
] = format
[offset
];
5120 sndfile
[sndoffset
] = '\0';
5121 res
= wait_file(chan
,ints
,sndfile
,lang
);
5125 /* Sunday - Saturday */
5126 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
5127 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5132 /* January - December */
5133 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
5134 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5137 /* First - Twelfth */
5138 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
5139 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5143 /* First - Thirtyfirst */
5144 if (!(tm
.tm_mday
% 10) || (tm
.tm_mday
< 10)) {
5145 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
5146 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5148 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%dh", tm
.tm_mday
- (tm
.tm_mday
% 10));
5149 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5151 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
% 10);
5152 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5158 if (tm
.tm_year
> 99) {
5159 res
= wait_file(chan
,ints
, "digits/2",lang
);
5161 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
5163 if (tm
.tm_year
> 100) {
5165 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) / 10);
5166 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5168 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) % 10);
5169 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5174 res
= wait_file(chan
,ints
, "digits/year",lang
);
5177 if (tm
.tm_year
< 1) {
5178 /* I'm not going to handle 1900 and prior */
5179 /* We'll just be silent on the year, instead of bombing out. */
5181 res
= wait_file(chan
,ints
, "digits/1",lang
);
5183 res
= wait_file(chan
,ints
, "digits/9",lang
);
5186 if (tm
.tm_year
<= 9) {
5188 res
= wait_file(chan
,ints
, "digits/0",lang
);
5190 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
5191 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5195 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
/ 10);
5196 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
);
5205 res
= wait_file(chan
,ints
, "digits/year",lang
);
5212 if (tm
.tm_hour
== 0)
5213 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
5214 else if (tm
.tm_hour
> 12)
5215 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
5217 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5218 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5220 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5226 if (!(tm
.tm_hour
% 10) || tm
.tm_hour
< 10) {
5227 if (tm
.tm_hour
< 10) {
5228 res
= wait_file(chan
, ints
, "digits/0", lang
);
5230 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5231 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5233 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- (tm
.tm_hour
% 10));
5234 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5236 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
% 10);
5237 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5241 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5246 if (!(tm
.tm_min
% 10) || tm
.tm_min
< 10) {
5247 if (tm
.tm_min
< 10) {
5248 res
= wait_file(chan
, ints
, "digits/0", lang
);
5250 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
5251 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5253 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
- (tm
.tm_min
% 10));
5254 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5256 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
% 10);
5257 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5261 res
= wait_file(chan
,ints
, "digits/minute",lang
);
5267 if (tm
.tm_hour
> 11)
5268 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
5270 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
5271 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5274 /* Shorthand for "Today", "Yesterday", or ABdY */
5275 /* XXX As emphasized elsewhere, this should the native way in your
5276 * language to say the date, with changes in what you say, depending
5277 * upon how recent the date is. XXX */
5281 time_t beg_today
, tt
;
5283 gettimeofday(&now
,NULL
);
5285 ast_localtime(&tt
,&tmnow
,timezone
);
5286 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5287 /* In any case, it saves not having to do ast_mktime() */
5288 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5289 if (beg_today
< time
) {
5291 res
= wait_file(chan
,ints
, "digits/today",lang
);
5292 } else if (beg_today
- 86400 < time
) {
5294 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5296 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5301 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5302 /* XXX As emphasized elsewhere, this should the native way in your
5303 * language to say the date, with changes in what you say, depending
5304 * upon how recent the date is. XXX */
5308 time_t beg_today
, tt
;
5310 gettimeofday(&now
,NULL
);
5312 ast_localtime(&tt
,&tmnow
,timezone
);
5313 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5314 /* In any case, it saves not having to do ast_mktime() */
5315 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5316 if (beg_today
< time
) {
5318 } else if ((beg_today
- 86400) < time
) {
5320 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5321 } else if (beg_today
- 86400 * 6 < time
) {
5322 /* Within the last week */
5323 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "A", timezone
);
5325 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5330 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "HM", timezone
);
5334 if (!(tm
.tm_sec
% 10) || tm
.tm_sec
< 10) {
5335 if (tm
.tm_sec
< 10) {
5336 res
= wait_file(chan
, ints
, "digits/0", lang
);
5338 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5339 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5341 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
- (tm
.tm_sec
% 10));
5342 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5344 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
% 10);
5345 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5349 res
= wait_file(chan
,ints
, "digits/second",lang
);
5353 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "HMS", timezone
);
5357 /* Just ignore spaces and tabs */
5360 /* Unknown character */
5361 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5363 /* Jump out on DTMF */
5371 static int say_time(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5373 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5374 return(ast_say_time_en(chan
, t
, ints
, lang
));
5375 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5376 return(ast_say_time_de(chan
, t
, ints
, lang
));
5377 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5378 return(ast_say_time_fr(chan
, t
, ints
, lang
));
5379 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5380 return(ast_say_time_nl(chan
, t
, ints
, lang
));
5381 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5382 return(ast_say_time_pt(chan
, t
, ints
, lang
));
5383 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5384 return(ast_say_time_tw(chan
, t
, ints
, lang
));
5385 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5386 return(ast_say_time_gr(chan
, t
, ints
, lang
));
5389 /* Default to English */
5390 return(ast_say_time_en(chan
, t
, ints
, lang
));
5393 /* English syntax */
5394 int ast_say_time_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5399 localtime_r(&t
,&tm
);
5403 else if (hour
== 12)
5405 else if (hour
> 12) {
5410 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5412 if (tm
.tm_min
> 9) {
5414 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5415 } else if (tm
.tm_min
) {
5417 res
= ast_streamfile(chan
, "digits/oh", lang
);
5419 res
= ast_waitstream(chan
, ints
);
5421 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5424 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5426 res
= ast_waitstream(chan
, ints
);
5430 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5433 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5436 res
= ast_waitstream(chan
, ints
);
5441 int ast_say_time_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5445 localtime_r(&t
,&tm
);
5447 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "n");
5449 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5451 res
= ast_waitstream(chan
, ints
);
5454 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
5459 int ast_say_time_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5463 localtime_r(&t
,&tm
);
5465 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5467 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5470 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5476 int ast_say_time_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5480 localtime_r(&t
,&tm
);
5482 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
5484 res
= ast_streamfile(chan
, "digits/nl-uur", lang
);
5486 res
= ast_waitstream(chan
, ints
);
5489 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, NULL
);
5493 /* Portuguese syntax */
5494 int ast_say_time_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5499 localtime_r(&t
,&tm
);
5502 res
= ast_say_number(chan
, hour
, ints
, lang
, "f");
5505 res
= wait_file(chan
, ints
, "digits/pt-e", lang
);
5507 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5510 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
5511 if (tm
.tm_hour
!= 1)
5513 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5516 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5520 /* Taiwanese / Chinese syntax */
5521 int ast_say_time_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5526 localtime_r(&t
,&tm
);
5530 else if (hour
== 12)
5532 else if (hour
> 12) {
5538 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5541 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5544 res
= ast_waitstream(chan
, ints
);
5546 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5548 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5550 res
= ast_waitstream(chan
, ints
);
5552 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5554 res
= ast_streamfile(chan
, "digits/minute", lang
);
5556 res
= ast_waitstream(chan
, ints
);
5560 static int say_datetime(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5562 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5563 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5564 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5565 return(ast_say_datetime_de(chan
, t
, ints
, lang
));
5566 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5567 return(ast_say_datetime_fr(chan
, t
, ints
, lang
));
5568 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5569 return(ast_say_datetime_nl(chan
, t
, ints
, lang
));
5570 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5571 return(ast_say_datetime_pt(chan
, t
, ints
, lang
));
5572 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5573 return(ast_say_datetime_tw(chan
, t
, ints
, lang
));
5574 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5575 return(ast_say_datetime_gr(chan
, t
, ints
, lang
));
5578 /* Default to English */
5579 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5582 /* English syntax */
5583 int ast_say_datetime_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5589 localtime_r(&t
,&tm
);
5591 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5592 res
= ast_streamfile(chan
, fn
, lang
);
5594 res
= ast_waitstream(chan
, ints
);
5597 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5598 res
= ast_streamfile(chan
, fn
, lang
);
5600 res
= ast_waitstream(chan
, ints
);
5603 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5608 else if (hour
== 12)
5610 else if (hour
> 12) {
5615 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5617 if (tm
.tm_min
> 9) {
5619 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5620 } else if (tm
.tm_min
) {
5622 res
= ast_streamfile(chan
, "digits/oh", lang
);
5624 res
= ast_waitstream(chan
, ints
);
5626 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5629 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5631 res
= ast_waitstream(chan
, ints
);
5635 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5638 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5641 res
= ast_waitstream(chan
, ints
);
5643 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5648 int ast_say_datetime_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5652 localtime_r(&t
,&tm
);
5653 res
= ast_say_date(chan
, t
, ints
, lang
);
5655 ast_say_time(chan
, t
, ints
, lang
);
5661 int ast_say_datetime_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5666 localtime_r(&t
,&tm
);
5669 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5672 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5673 res
= ast_streamfile(chan
, fn
, lang
);
5675 res
= ast_waitstream(chan
, ints
);
5678 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5679 res
= ast_streamfile(chan
, fn
, lang
);
5681 res
= ast_waitstream(chan
, ints
);
5685 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5687 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5688 if (tm
.tm_min
> 0) {
5690 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5693 res
= ast_waitstream(chan
, ints
);
5695 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5700 int ast_say_datetime_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5704 localtime_r(&t
,&tm
);
5705 res
= ast_say_date(chan
, t
, ints
, lang
);
5707 res
= ast_streamfile(chan
, "digits/nl-om", lang
);
5709 res
= ast_waitstream(chan
, ints
);
5712 ast_say_time(chan
, t
, ints
, lang
);
5716 /* Portuguese syntax */
5717 int ast_say_datetime_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5723 localtime_r(&t
,&tm
);
5725 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5726 res
= ast_streamfile(chan
, fn
, lang
);
5728 res
= ast_waitstream(chan
, ints
);
5731 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5732 res
= ast_streamfile(chan
, fn
, lang
);
5734 res
= ast_waitstream(chan
, ints
);
5737 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5742 else if (hour
== 12)
5744 else if (hour
> 12) {
5749 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5751 if (tm
.tm_min
> 9) {
5753 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5754 } else if (tm
.tm_min
) {
5756 res
= ast_streamfile(chan
, "digits/oh", lang
);
5758 res
= ast_waitstream(chan
, ints
);
5760 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5763 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5765 res
= ast_waitstream(chan
, ints
);
5769 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5772 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5775 res
= ast_waitstream(chan
, ints
);
5777 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5781 /* Taiwanese / Chinese syntax */
5782 int ast_say_datetime_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5788 localtime_r(&t
,&tm
);
5790 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5792 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5793 res
= ast_streamfile(chan
, fn
, lang
);
5795 res
= ast_waitstream(chan
, ints
);
5798 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5800 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5801 res
= ast_streamfile(chan
, fn
, lang
);
5803 res
= ast_waitstream(chan
, ints
);
5809 else if (hour
== 12)
5811 else if (hour
> 12) {
5817 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5820 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5823 res
= ast_waitstream(chan
, ints
);
5825 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5827 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5829 res
= ast_waitstream(chan
, ints
);
5831 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5833 res
= ast_streamfile(chan
, "digits/minute", lang
);
5835 res
= ast_waitstream(chan
, ints
);
5839 static int say_datetime_from_now(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5841 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5842 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
5843 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5844 return(ast_say_datetime_from_now_fr(chan
, t
, ints
, lang
));
5845 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5846 return(ast_say_datetime_from_now_pt(chan
, t
, ints
, lang
));
5849 /* Default to English */
5850 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
5853 /* English syntax */
5854 int ast_say_datetime_from_now_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5865 localtime_r(&t
,&tm
);
5866 localtime_r(&nowt
,&now
);
5867 daydiff
= now
.tm_yday
- tm
.tm_yday
;
5868 if ((daydiff
< 0) || (daydiff
> 6)) {
5869 /* Day of month and month */
5871 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5872 res
= ast_streamfile(chan
, fn
, lang
);
5874 res
= ast_waitstream(chan
, ints
);
5877 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5879 } else if (daydiff
) {
5880 /* Just what day of the week */
5882 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5883 res
= ast_streamfile(chan
, fn
, lang
);
5885 res
= ast_waitstream(chan
, ints
);
5887 } /* Otherwise, it was today */
5889 res
= ast_say_time(chan
, t
, ints
, lang
);
5894 int ast_say_datetime_from_now_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5905 localtime_r(&t
,&tm
);
5906 localtime_r(&nowt
,&now
);
5907 daydiff
= now
.tm_yday
- tm
.tm_yday
;
5908 if ((daydiff
< 0) || (daydiff
> 6)) {
5909 /* Day of month and month */
5911 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5912 res
= ast_streamfile(chan
, fn
, lang
);
5914 res
= ast_waitstream(chan
, ints
);
5917 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5919 } else if (daydiff
) {
5920 /* Just what day of the week */
5922 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5923 res
= ast_streamfile(chan
, fn
, lang
);
5925 res
= ast_waitstream(chan
, ints
);
5927 } /* Otherwise, it was today */
5929 res
= ast_say_time(chan
, t
, ints
, lang
);
5933 /* Portuguese syntax */
5934 int ast_say_datetime_from_now_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5945 localtime_r(&t
,&tm
);
5946 localtime_r(&nowt
,&now
);
5947 daydiff
= now
.tm_yday
- tm
.tm_yday
;
5948 if ((daydiff
< 0) || (daydiff
> 6)) {
5949 /* Day of month and month */
5951 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5953 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
5954 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5956 res
= wait_file(chan
, ints
, fn
, lang
);
5958 } else if (daydiff
) {
5959 /* Just what day of the week */
5960 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5962 res
= wait_file(chan
, ints
, fn
, lang
);
5963 } /* Otherwise, it was today */
5964 snprintf(fn
, sizeof(fn
), "digits/pt-ah");
5966 res
= wait_file(chan
, ints
, fn
, lang
);
5967 if (tm
.tm_hour
!= 1)
5969 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5971 res
= ast_say_time(chan
, t
, ints
, lang
);
5976 /*********************************** GREEK SUPPORT ***************************************/
5980 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
5982 static int gr_say_number_female(int num
, struct ast_channel
*chan
, const char *ints
, const char *lang
){
5988 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
5990 snprintf(fn
, sizeof(fn
), "digits/female-%d", num
);
5991 res
= wait_file(chan
, ints
, fn
, lang
);
5992 } else if (num
< 13) {
5993 res
= ast_say_number(chan
, num
, ints
, lang
, (char *) NULL
);
5994 } else if (num
<100 ) {
5995 tmp
= (num
/10) * 10;
5997 snprintf(fn
, sizeof(fn
), "digits/%d", tmp
);
5998 res
= ast_streamfile(chan
, fn
, lang
);
6000 res
= ast_waitstream(chan
, ints
);
6002 gr_say_number_female(left
, chan
, ints
, lang
);
6013 * A list of the files that you need to create
6014 -> digits/xilia = "xilia"
6015 -> digits/myrio = "ekatomyrio"
6016 -> digits/thousands = "xiliades"
6017 -> digits/millions = "ektatomyria"
6018 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6019 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6021 Here we must note that we use digits/tens/100 to utter "ekato"
6022 and digits/hundred-100 to utter "ekaton"
6023 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6024 "terakosia". Here again we use hundreds/1000 for "xilia"
6025 and digits/thousnds for "xiliades"
6028 static int ast_say_number_full_gr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
,int audiofd
, int ctrlfd
)
6036 snprintf(fn
, sizeof(fn
), "digits/0");
6037 res
= ast_streamfile(chan
, fn
, chan
->language
);
6039 return ast_waitstream(chan
, ints
);
6042 while(!res
&& num
) {
6045 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
6047 } else if (num
<= 100) {
6048 /* 13 < num <= 100 */
6049 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
6050 num
-= ((num
/ 10) * 10);
6051 } else if (num
< 200) {
6052 /* 100 < num < 200 */
6053 snprintf(fn
, sizeof(fn
), "digits/hundred-100");
6054 num
-= ((num
/ 100) * 100);
6055 }else if (num
< 1000) {
6056 /* 200 < num < 1000 */
6057 snprintf(fn
, sizeof(fn
), "digits/hundred-%d", (num
/100)*100);
6058 num
-= ((num
/ 100) * 100);
6059 }else if (num
< 2000){
6060 snprintf(fn
, sizeof(fn
), "digits/xilia");
6061 num
-= ((num
/ 1000) * 1000);
6065 if (num
< 1000000) {
6066 res
= ast_say_number_full_gr(chan
, (num
/ 1000), ints
, chan
->language
, audiofd
, ctrlfd
);
6070 snprintf(fn
, sizeof(fn
), "digits/thousands");
6072 if (num
< 1000000000) { /* 1,000,000,000 */
6073 res
= ast_say_number_full_gr(chan
, (num
/ 1000000), ints
, chan
->language
,audiofd
, ctrlfd
);
6076 num
= num
% 1000000;
6077 snprintf(fn
, sizeof(fn
), "digits/millions");
6079 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
6085 if(!ast_streamfile(chan
, fn
, language
)) {
6086 if ((audiofd
> -1) && (ctrlfd
> -1))
6087 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
6089 res
= ast_waitstream(chan
, ints
);
6091 ast_stopstream(chan
);
6099 * The format is weekday - day - month -year
6101 * A list of the files that you need to create
6102 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6103 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6104 Attention the months are in
6109 static int ast_say_date_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6117 ast_localtime(&t
,&tm
,NULL
);
6118 /* W E E K - D A Y */
6120 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6121 res
= ast_streamfile(chan
, fn
, lang
);
6123 res
= ast_waitstream(chan
, ints
);
6127 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6131 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6132 res
= ast_streamfile(chan
, fn
, lang
);
6134 res
= ast_waitstream(chan
, ints
);
6138 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
6144 /* A list of the files that you need to create
6145 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6146 * digits/kai : "KAI"
6148 * digits/p-m : "meta meshmbrias"
6149 * digits/a-m : "pro meshmbrias"
6152 static int ast_say_time_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6159 localtime_r(&t
,&tm
);
6164 else if (hour
== 12)
6166 else if (hour
> 12) {
6171 res
= gr_say_number_female(hour
, chan
, ints
, lang
);
6174 res
= ast_streamfile(chan
, "digits/kai", lang
);
6176 res
= ast_waitstream(chan
, ints
);
6178 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
6181 res
= ast_streamfile(chan
, "digits/hwra", lang
);
6183 res
= ast_waitstream(chan
, ints
);
6187 res
= ast_streamfile(chan
, "digits/p-m", lang
);
6190 res
= ast_streamfile(chan
, "digits/a-m", lang
);
6193 res
= ast_waitstream(chan
, ints
);
6199 static int ast_say_datetime_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6204 localtime_r(&t
,&tm
);
6207 /* W E E K - D A Y */
6209 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6210 res
= ast_streamfile(chan
, fn
, lang
);
6212 res
= ast_waitstream(chan
, ints
);
6216 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6220 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6221 res
= ast_streamfile(chan
, fn
, lang
);
6223 res
= ast_waitstream(chan
, ints
);
6226 res
= ast_say_time_gr(chan
, t
, ints
, lang
);
6230 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
)
6234 int res
=0, offset
, sndoffset
;
6235 char sndfile
[256], nextmsg
[256];
6238 format
= "AdBY 'digits/at' IMp";
6240 ast_localtime(&time
,&tm
,timezone
);
6242 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
6243 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
6244 switch (format
[offset
]) {
6245 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6247 /* Literal name of a sound file */
6249 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
6250 sndfile
[sndoffset
] = format
[offset
];
6251 sndfile
[sndoffset
] = '\0';
6252 res
= wait_file(chan
,ints
,sndfile
,lang
);
6256 /* Sunday - Saturday */
6257 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
6258 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6263 /* January - December */
6264 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
6265 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6269 /* first - thirtyfirst */
6270 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6275 ast_say_number_full_gr(chan
, 1900+tm
.tm_year
, ints
, chan
->language
, -1, -1);
6280 if (tm
.tm_hour
== 0)
6281 gr_say_number_female(12, chan
, ints
, lang
);
6282 else if (tm
.tm_hour
> 12)
6283 gr_say_number_female(tm
.tm_hour
- 12, chan
, ints
, lang
);
6285 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6290 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6296 res
= ast_streamfile(chan
, "digits/kai", lang
);
6298 res
= ast_waitstream(chan
, ints
);
6300 res
= ast_say_number_full_gr(chan
, tm
.tm_min
, ints
, lang
, -1, -1);
6303 res
= ast_streamfile(chan
, "digits/oclock", lang
);
6305 res
= ast_waitstream(chan
, ints
);
6311 if (tm
.tm_hour
> 11)
6312 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
6314 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
6315 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6318 /* Shorthand for "Today", "Yesterday", or ABdY */
6319 /* XXX As emphasized elsewhere, this should the native way in your
6320 * language to say the date, with changes in what you say, depending
6321 * upon how recent the date is. XXX */
6325 time_t beg_today
, tt
;
6327 gettimeofday(&now
,NULL
);
6329 ast_localtime(&tt
,&tmnow
,timezone
);
6330 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6331 /* In any case, it saves not having to do ast_mktime() */
6332 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6333 if (beg_today
< time
) {
6335 res
= wait_file(chan
,ints
, "digits/today",lang
);
6336 } else if (beg_today
- 86400 < time
) {
6338 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6340 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6345 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6346 /* XXX As emphasized elsewhere, this should the native way in your
6347 * language to say the date, with changes in what you say, depending
6348 * upon how recent the date is. XXX */
6352 time_t beg_today
, tt
;
6354 gettimeofday(&now
,NULL
);
6356 ast_localtime(&tt
,&tmnow
,timezone
);
6357 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6358 /* In any case, it saves not having to do ast_mktime() */
6359 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6360 if (beg_today
< time
) {
6362 } else if ((beg_today
- 86400) < time
) {
6364 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6365 } else if (beg_today
- 86400 * 6 < time
) {
6366 /* Within the last week */
6367 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "A", timezone
);
6369 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6374 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HM", timezone
);
6378 snprintf(nextmsg
,sizeof(nextmsg
), "digits/kai");
6379 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6381 res
= ast_say_number_full_gr(chan
, tm
.tm_sec
, ints
, lang
, -1, -1);
6383 snprintf(nextmsg
,sizeof(nextmsg
), "digits/seconds");
6384 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6387 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HMS", timezone
);
6391 /* Just ignore spaces and tabs */
6394 /* Unknown character */
6395 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
6397 /* Jump out on DTMF */
6406 * remap the 'say' functions to use those in this file
6408 static void __attribute__((constructor
)) __say_init(void)
6410 ast_say_number_full
= say_number_full
;
6411 ast_say_enumeration_full
= say_enumeration_full
;
6412 ast_say_digit_str_full
= say_digit_str_full
;
6413 ast_say_character_str_full
= say_character_str_full
;
6414 ast_say_phonetic_str_full
= say_phonetic_str_full
;
6415 ast_say_datetime
= say_datetime
;
6416 ast_say_time
= say_time
;
6417 ast_say_date
= say_date
;
6418 ast_say_datetime_from_now
= say_datetime_from_now
;
6419 ast_say_date_with_format
= say_date_with_format
;