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>
28 * \note 2007-02-08 : Support for Georgian added by Alexander Shaduri <ashaduri@gmail.com>,
29 * Next Generation Networks (NGN).
34 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
36 #include <sys/types.h>
39 #include <netinet/in.h>
46 #include <iso/limits_iso.h>
49 #include "asterisk/file.h"
50 #include "asterisk/channel.h"
51 #include "asterisk/logger.h"
52 #include "asterisk/options.h"
53 #include "asterisk/say.h"
54 #include "asterisk/lock.h"
55 #include "asterisk/localtime.h"
56 #include "asterisk/utils.h"
58 /* Forward declaration */
59 static int wait_file(struct ast_channel
*chan
, const char *ints
, const char *file
, const char *lang
);
62 static int say_character_str_full(struct ast_channel
*chan
, const char *str
, const char *ints
, const char *lang
, int audiofd
, int ctrlfd
)
70 while (str
[num
] && !res
) {
80 fn
= "letters/exclaimation-point";
86 fn
= "letters/dollar";
95 fn
= "letters/equals";
101 fn
= "letters/slash";
104 fn
= "letters/space";
116 strcpy(fnbuf
, "digits/X");
122 if ('A' <= ltr
&& ltr
<= 'Z') ltr
+= 'a' - 'A'; /* file names are all lower-case */
123 strcpy(fnbuf
, "letters/X");
127 if (fn
&& ast_fileexists(fn
, NULL
, lang
) > 0) {
128 res
= ast_streamfile(chan
, fn
, lang
);
130 if ((audiofd
> -1) && (ctrlfd
> -1))
131 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
133 res
= ast_waitstream(chan
, ints
);
135 ast_stopstream(chan
);
143 static int say_phonetic_str_full(struct ast_channel
*chan
, const char *str
, const char *ints
, const char *lang
, int audiofd
, int ctrlfd
)
151 while (str
[num
] && !res
) {
161 fn
= "letters/exclaimation-point";
167 fn
= "letters/dollar";
176 fn
= "letters/equals";
182 fn
= "letters/slash";
185 fn
= "letters/space";
196 strcpy(fnbuf
, "digits/X");
200 default: /* '9' falls here... */
202 if ('A' <= ltr
&& ltr
<= 'Z') ltr
+= 'a' - 'A'; /* file names are all lower-case */
203 strcpy(fnbuf
, "phonetic/X_p");
207 if (fn
&& ast_fileexists(fn
, NULL
, lang
) > 0) {
208 res
= ast_streamfile(chan
, fn
, lang
);
210 if ((audiofd
> -1) && (ctrlfd
> -1))
211 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
213 res
= ast_waitstream(chan
, ints
);
215 ast_stopstream(chan
);
223 static int say_digit_str_full(struct ast_channel
*chan
, const char *str
, const char *ints
, const char *lang
, int audiofd
, int ctrlfd
)
230 while (str
[num
] && !res
) {
252 strcpy(fnbuf
, "digits/X");
257 if (fn
&& ast_fileexists(fn
, NULL
, lang
) > 0) {
258 res
= ast_streamfile(chan
, fn
, lang
);
260 if ((audiofd
> -1) && (ctrlfd
> -1))
261 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
263 res
= ast_waitstream(chan
, ints
);
265 ast_stopstream(chan
);
273 /* Forward declarations */
274 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
275 \note Not really language codes.
276 For these language codes, Asterisk will change the syntax when
277 saying numbers (and in some cases dates and voicemail messages
281 \arg \b en - English (US)
282 \arg \b en_GB - English (British)
283 \arg \b es - Spanish, Mexican
288 \arg \b no - Norwegian
290 \arg \b pt - Portuguese
291 \arg \b pt_BR - Portuguese (Brazil)
293 \arg \b tw - Taiwanese / Chinese
295 \arg \b ge - Georgian
298 For Some languages the numbers differ for gender and plural.
299 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
300 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
301 use the option argument 'p' for plural enumerations like in German
303 Date/Time functions currently have less languages supported than saynumber().
305 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
307 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
310 Portuguese sound files needed for Time/Date functions:
321 Spanish sound files needed for Time/Date functions:
326 Italian sound files needed for Time/Date functions:
332 /* Forward declarations of language specific variants of ast_say_number_full */
333 static int ast_say_number_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
334 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
);
335 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
);
336 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
);
337 static int ast_say_number_full_en_GB(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
338 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
);
339 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
);
340 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
);
341 static int ast_say_number_full_it(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
342 static int ast_say_number_full_nl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
343 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
);
344 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
);
345 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
);
346 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
);
347 static int ast_say_number_full_tw(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
348 static int ast_say_number_full_gr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
349 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
);
350 static int ast_say_number_full_ge(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
352 /* Forward declarations of language specific variants of ast_say_enumeration_full */
353 static int ast_say_enumeration_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
354 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
);
355 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
);
357 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
358 static int ast_say_date_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
359 static int ast_say_date_da(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
360 static int ast_say_date_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
361 static int ast_say_date_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
362 static int ast_say_date_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
363 static int ast_say_date_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
364 static int ast_say_date_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
365 static int ast_say_date_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
367 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
);
368 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
);
369 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
);
370 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
);
371 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
);
372 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
);
373 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
);
374 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
);
375 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
);
376 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
);
377 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
);
378 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
);
380 static int ast_say_time_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
381 static int ast_say_time_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
382 static int ast_say_time_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
383 static int ast_say_time_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
384 static int ast_say_time_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
385 static int ast_say_time_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
386 static int ast_say_time_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
387 static int ast_say_time_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
388 static int ast_say_time_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
390 static int ast_say_datetime_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
391 static int ast_say_datetime_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
392 static int ast_say_datetime_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
393 static int ast_say_datetime_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
394 static int ast_say_datetime_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
395 static int ast_say_datetime_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
396 static int ast_say_datetime_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
397 static int ast_say_datetime_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
398 static int ast_say_datetime_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
400 static int ast_say_datetime_from_now_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
401 static int ast_say_datetime_from_now_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
402 static int ast_say_datetime_from_now_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
403 static int ast_say_datetime_from_now_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
405 static int wait_file(struct ast_channel
*chan
, const char *ints
, const char *file
, const char *lang
)
408 if ((res
= ast_streamfile(chan
, file
, lang
)))
409 ast_log(LOG_WARNING
, "Unable to play message %s\n", file
);
411 res
= ast_waitstream(chan
, ints
);
415 /*! \brief ast_say_number_full: call language-specific functions */
416 /* Called from AGI */
417 static int say_number_full(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
419 if (!strcasecmp(language
,"en") ) { /* English syntax */
420 return(ast_say_number_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
421 } else if (!strcasecmp(language
, "cz") ) { /* Czech syntax */
422 return(ast_say_number_full_cz(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
423 } else if (!strcasecmp(language
, "da") ) { /* Danish syntax */
424 return(ast_say_number_full_da(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
425 } else if (!strcasecmp(language
, "de") ) { /* German syntax */
426 return(ast_say_number_full_de(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
427 } else if (!strcasecmp(language
, "en_GB") ) { /* British syntax */
428 return(ast_say_number_full_en_GB(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
429 } else if (!strcasecmp(language
, "no") ) { /* Norwegian syntax */
430 return(ast_say_number_full_no(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
431 } else if (!strcasecmp(language
, "es") || !strcasecmp(language
, "mx")) { /* Spanish syntax */
432 return(ast_say_number_full_es(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
433 } else if (!strcasecmp(language
, "fr") ) { /* French syntax */
434 return(ast_say_number_full_fr(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
435 } else if (!strcasecmp(language
, "he") ) { /* Hebrew syntax */
436 return(ast_say_number_full_he(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
437 } else if (!strcasecmp(language
, "it") ) { /* Italian syntax */
438 return(ast_say_number_full_it(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
439 } else if (!strcasecmp(language
, "nl") ) { /* Dutch syntax */
440 return(ast_say_number_full_nl(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
441 } else if (!strcasecmp(language
, "pl") ) { /* Polish syntax */
442 return(ast_say_number_full_pl(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
443 } else if (!strcasecmp(language
, "pt") || !strcasecmp(language
, "pt_BR")) { /* Portuguese syntax */
444 return(ast_say_number_full_pt(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
445 } else if (!strcasecmp(language
, "se") ) { /* Swedish syntax */
446 return(ast_say_number_full_se(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
447 } else if (!strcasecmp(language
, "tw") || !strcasecmp(language
, "zh") ) { /* Taiwanese / Chinese syntax */
448 return(ast_say_number_full_tw(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
449 } else if (!strcasecmp(language
, "gr") ) { /* Greek syntax */
450 return(ast_say_number_full_gr(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
451 } else if (!strcasecmp(language
, "ru") ) { /* Russian syntax */
452 return(ast_say_number_full_ru(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
453 } else if (!strcasecmp(language
, "ge") ) { /* Georgian syntax */
454 return(ast_say_number_full_ge(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
457 /* Default to english */
458 return(ast_say_number_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
461 /*! \brief ast_say_number_full_en: English syntax */
462 /* This is the default syntax, if no other syntax defined in this file is used */
463 static int ast_say_number_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
469 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
471 while (!res
&& (num
|| playh
)) {
473 snprintf(fn
, sizeof(fn
), "digits/minus");
474 if ( num
> INT_MIN
) {
480 snprintf(fn
, sizeof(fn
), "digits/hundred");
482 } else if (num
< 20) {
483 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
485 } else if (num
< 100) {
486 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
487 num
-= ((num
/ 10) * 10);
490 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
492 num
-= ((num
/ 100) * 100);
494 if (num
< 1000000) { /* 1,000,000 */
495 res
= ast_say_number_full_en(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
499 snprintf(fn
, sizeof(fn
), "digits/thousand");
501 if (num
< 1000000000) { /* 1,000,000,000 */
502 res
= ast_say_number_full_en(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
506 snprintf(fn
, sizeof(fn
), "digits/million");
508 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
515 if (!ast_streamfile(chan
, fn
, language
)) {
516 if ((audiofd
> -1) && (ctrlfd
> -1))
517 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
519 res
= ast_waitstream(chan
, ints
);
521 ast_stopstream(chan
);
527 static int exp10_int(int power
)
530 for (x
=0;x
<power
;x
++)
535 /*! \brief ast_say_number_full_cz: Czech syntax */
537 * 1m,2m - gender male
538 * 1w,2w - gender female
542 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
544 * for each number 10^(3n + 3) exist 3 files represented as:
545 * 1 tousand = jeden tisic = 1_E3
546 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
547 * 5,6,... tousands = pet,sest,... tisic = 5_E3
553 * tousand, milion are gender male, so 1 and 2 is 1m 2m
554 * miliard is gender female, so 1 and 2 is 1w 2w
556 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
)
566 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
571 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
573 while (!res
&& (num
|| playh
)) {
575 snprintf(fn
, sizeof(fn
), "digits/minus");
576 if ( num
> INT_MIN
) {
581 } else if (num
< 3 ) {
582 snprintf(fn
, sizeof(fn
), "digits/%d%c",num
,options
[0]);
585 } else if (num
< 20) {
586 snprintf(fn
, sizeof(fn
), "digits/%d",num
);
589 } else if (num
< 100) {
590 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
591 num
-= ((num
/ 10) * 10);
592 } else if (num
< 1000) {
593 hundered
= num
/ 100;
594 if ( hundered
== 1 ) {
595 snprintf(fn
, sizeof(fn
), "digits/1sto");
596 } else if ( hundered
== 2 ) {
597 snprintf(fn
, sizeof(fn
), "digits/2ste");
599 res
= ast_say_number_full_cz(chan
,hundered
,ints
,language
,options
,audiofd
,ctrlfd
);
602 if (hundered
== 3 || hundered
== 4) {
603 snprintf(fn
, sizeof(fn
), "digits/sta");
604 } else if ( hundered
> 4 ) {
605 snprintf(fn
, sizeof(fn
), "digits/set");
608 num
-= (hundered
* 100);
609 } else { /* num > 1000 */
610 length
= (int)log10(num
)+1;
611 while ( (length
% 3 ) != 1 ) {
614 left
= num
/ (exp10_int(length
-1));
617 case 9: options
= "w"; /* 1,000,000,000 gender female */
619 default : options
= "m"; /* others are male */
622 if ( left
> 1 ) { /* we dont say "one thousand" but only thousand */
623 res
= ast_say_number_full_cz(chan
,left
,ints
,language
,options
,audiofd
,ctrlfd
);
627 if ( left
>= 5 ) { /* >= 5 have the same declesion */
628 snprintf(fn
, sizeof(fn
), "digits/5_E%d",length
-1);
629 } else if ( left
>= 2 && left
<= 4 ) {
630 snprintf(fn
, sizeof(fn
), "digits/2-4_E%d",length
-1);
631 } else { /* left == 1 */
632 snprintf(fn
, sizeof(fn
), "digits/1_E%d",length
-1);
634 num
-= left
* (exp10_int(length
-1));
637 if (!ast_streamfile(chan
, fn
, language
)) {
638 if ((audiofd
> -1) && (ctrlfd
> -1)) {
639 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
641 res
= ast_waitstream(chan
, ints
);
644 ast_stopstream(chan
);
650 /*! \brief ast_say_number_full_da: Danish syntax */
652 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
654 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
)
659 int cn
= 1; /* +1 = commune; -1 = neuter */
662 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
664 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
666 while (!res
&& (num
|| playh
|| playa
)) {
667 /* The grammar for Danish numbers is the same as for English except
669 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
670 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
671 * "one-and twenty" and 68 is "eight-and sixty".
672 * - "million" is different in singular and plural form
673 * - numbers > 1000 with zero as the third digit from last have an
674 * "and" before the last two digits, i.e. 2034 is "two thousand and
675 * four-and thirty" and 1000012 is "one million and twelve".
678 snprintf(fn
, sizeof(fn
), "digits/minus");
679 if ( num
> INT_MIN
) {
685 snprintf(fn
, sizeof(fn
), "digits/hundred");
688 snprintf(fn
, sizeof(fn
), "digits/and");
690 } else if (num
== 1 && cn
== -1) {
691 snprintf(fn
, sizeof(fn
), "digits/1N");
693 } else if (num
< 20) {
694 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
696 } else if (num
< 100) {
699 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
702 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
707 int hundreds
= num
/ 100;
709 snprintf(fn
, sizeof(fn
), "digits/1N");
711 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
714 num
-= 100 * hundreds
;
720 res
= ast_say_number_full_da(chan
, num
/ 1000, ints
, language
, "n", audiofd
, ctrlfd
);
724 snprintf(fn
, sizeof(fn
), "digits/thousand");
726 if (num
< 1000000000) {
727 int millions
= num
/ 1000000;
728 res
= ast_say_number_full_da(chan
, millions
, ints
, language
, "c", audiofd
, ctrlfd
);
732 snprintf(fn
, sizeof(fn
), "digits/million");
734 snprintf(fn
, sizeof(fn
), "digits/millions");
737 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
741 if (num
&& num
< 100)
746 if (!ast_streamfile(chan
, fn
, language
)) {
747 if ((audiofd
> -1) && (ctrlfd
> -1))
748 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
750 res
= ast_waitstream(chan
, ints
);
752 ast_stopstream(chan
);
758 /*! \brief ast_say_number_full_de: German syntax */
760 In addition to English, the following sounds are required:
762 "1-and" through "9-and"
765 NB "1" is recorded as 'eins'
767 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
)
770 int mf
= 1; /* +1 = male and neuter; -1 = female */
774 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
776 if (options
&& (!strncasecmp(options
, "f",1)))
779 while (!res
&& num
) {
780 /* The grammar for German numbers is the same as for English except
782 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
783 * "one-and twenty" and 68 is "eight-and sixty".
784 * - "one" varies according to gender
785 * - 100 is 'hundert', however all other instances are 'ein hundert'
786 * - 1000 is 'tausend', however all other instances are 'ein tausend'
787 * - 1000000 is always 'eine million'
788 * - "million" is different in singular and plural form
791 snprintf(fn
, sizeof(fn
), "digits/minus");
792 if ( num
> INT_MIN
) {
797 } else if (num
< 100 && t
) {
798 snprintf(fn
, sizeof(fn
), "digits/and");
800 } else if (num
== 1 && mf
== -1) {
801 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
803 } else if (num
< 20) {
804 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
806 } else if (num
< 100) {
809 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
812 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
815 } else if (num
== 100 && t
== 0) {
816 snprintf(fn
, sizeof(fn
), "digits/hundred");
818 } else if (num
< 1000) {
819 int hundreds
= num
/ 100;
822 snprintf(fn
, sizeof(fn
), "digits/1N");
824 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
826 snprintf(fna
, sizeof(fna
), "digits/hundred");
828 } else if (num
== 1000 && t
== 0) {
829 snprintf(fn
, sizeof(fn
), "digits/thousand");
831 } else if (num
< 1000000) {
832 int thousands
= num
/ 1000;
835 if (thousands
== 1) {
836 snprintf(fn
, sizeof(fn
), "digits/1N");
837 snprintf(fna
, sizeof(fna
), "digits/thousand");
839 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
842 snprintf(fn
, sizeof(fn
), "digits/thousand");
844 } else if (num
< 1000000000) {
845 int millions
= num
/ 1000000;
849 snprintf(fn
, sizeof(fn
), "digits/1F");
850 snprintf(fna
, sizeof(fna
), "digits/million");
852 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
855 snprintf(fn
, sizeof(fn
), "digits/millions");
857 } else if (num
<= INT_MAX
) {
858 int billions
= num
/ 1000000000;
859 num
= num
% 1000000000;
862 snprintf(fn
, sizeof(fn
), "digits/1F");
863 snprintf(fna
, sizeof(fna
), "digits/milliard");
865 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
869 snprintf(fn
, sizeof(fn
), "digits/milliards");
872 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
876 if (!ast_streamfile(chan
, fn
, language
)) {
877 if ((audiofd
> -1) && (ctrlfd
> -1))
878 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
880 res
= ast_waitstream(chan
, ints
);
882 ast_stopstream(chan
);
884 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
885 if ((audiofd
> -1) && (ctrlfd
> -1))
886 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
888 res
= ast_waitstream(chan
, ints
);
890 ast_stopstream(chan
);
898 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
900 In addition to American English, the following sounds are required: "and"
902 static int ast_say_number_full_en_GB(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
909 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
911 while (!res
&& (num
|| playh
|| playa
)) {
913 snprintf(fn
, sizeof(fn
), "digits/minus");
914 if ( num
> INT_MIN
) {
920 snprintf(fn
, sizeof(fn
), "digits/hundred");
923 snprintf(fn
, sizeof(fn
), "digits/and");
925 } else if (num
< 20) {
926 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
928 } else if (num
< 100) {
929 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
930 num
-= ((num
/ 10) * 10);
931 } else if (num
< 1000) {
932 int hundreds
= num
/ 100;
933 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
936 num
-= 100 * hundreds
;
939 } else if (num
< 1000000) {
940 res
= ast_say_number_full_en_GB(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
943 snprintf(fn
, sizeof(fn
), "digits/thousand");
945 if (num
&& num
< 100)
947 } else if (num
< 1000000000) {
948 int millions
= num
/ 1000000;
949 res
= ast_say_number_full_en_GB(chan
, millions
, ints
, language
, audiofd
, ctrlfd
);
952 snprintf(fn
, sizeof(fn
), "digits/million");
954 if (num
&& num
< 100)
957 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
962 if (!ast_streamfile(chan
, fn
, language
)) {
963 if ((audiofd
> -1) && (ctrlfd
> -1))
964 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
966 res
= ast_waitstream(chan
, ints
);
968 ast_stopstream(chan
);
974 /*! \brief ast_say_number_full_es: Spanish syntax */
976 Requires a few new audios:
977 1F.gsm: feminine 'una'
978 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
980 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
)
984 int mf
= 0; /* +1 = male; -1 = female */
987 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
990 if (!strncasecmp(options
, "f",1))
992 else if (!strncasecmp(options
, "m", 1))
996 while (!res
&& num
) {
998 snprintf(fn
, sizeof(fn
), "digits/minus");
999 if ( num
> INT_MIN
) {
1005 snprintf(fn
, sizeof(fn
), "digits/and");
1007 } else if (num
== 1) {
1009 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1011 snprintf(fn
, sizeof(fn
), "digits/%dM", num
);
1013 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1015 } else if (num
< 31) {
1016 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1018 } else if (num
< 100) {
1019 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1020 num
-= ((num
/10)*10);
1023 } else if (num
== 100) {
1024 snprintf(fn
, sizeof(fn
), "digits/100");
1026 } else if (num
< 200) {
1027 snprintf(fn
, sizeof(fn
), "digits/100-and");
1031 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100)*100);
1032 num
-= ((num
/100)*100);
1033 } else if (num
< 2000) {
1035 snprintf(fn
, sizeof(fn
), "digits/thousand");
1037 if (num
< 1000000) {
1038 res
= ast_say_number_full_es(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1042 snprintf(fn
, sizeof(fn
), "digits/thousand");
1044 if (num
< 2147483640) {
1045 if ((num
/1000000) == 1) {
1046 res
= ast_say_number_full_es(chan
, num
/ 1000000, ints
, language
, "M", audiofd
, ctrlfd
);
1049 snprintf(fn
, sizeof(fn
), "digits/million");
1051 res
= ast_say_number_full_es(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1054 snprintf(fn
, sizeof(fn
), "digits/millions");
1056 num
= num
% 1000000;
1058 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1066 if (!ast_streamfile(chan
, fn
, language
)) {
1067 if ((audiofd
> -1) && (ctrlfd
> -1))
1068 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1070 res
= ast_waitstream(chan
, ints
);
1072 ast_stopstream(chan
);
1080 /*! \brief ast_say_number_full_fr: French syntax */
1081 /* Extra sounds needed:
1084 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
)
1089 int mf
= 1; /* +1 = male; -1 = female */
1092 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1094 if (options
&& !strncasecmp(options
, "f",1))
1097 while (!res
&& (num
|| playh
|| playa
)) {
1099 snprintf(fn
, sizeof(fn
), "digits/minus");
1100 if ( num
> INT_MIN
) {
1106 snprintf(fn
, sizeof(fn
), "digits/hundred");
1109 snprintf(fn
, sizeof(fn
), "digits/et");
1111 } else if (num
== 1) {
1113 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1115 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1117 } else if (num
< 21) {
1118 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1120 } else if (num
< 70) {
1121 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1122 if ((num
% 10) == 1) playa
++;
1124 } else if (num
< 80) {
1125 snprintf(fn
, sizeof(fn
), "digits/60");
1126 if ((num
% 10) == 1) playa
++;
1128 } else if (num
< 100) {
1129 snprintf(fn
, sizeof(fn
), "digits/80");
1131 } else if (num
< 200) {
1132 snprintf(fn
, sizeof(fn
), "digits/hundred");
1134 } else if (num
< 1000) {
1135 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1138 } else if (num
< 2000) {
1139 snprintf(fn
, sizeof(fn
), "digits/thousand");
1141 } else if (num
< 1000000) {
1142 res
= ast_say_number_full_fr(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1145 snprintf(fn
, sizeof(fn
), "digits/thousand");
1147 } else if (num
< 1000000000) {
1148 res
= ast_say_number_full_fr(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1151 snprintf(fn
, sizeof(fn
), "digits/million");
1152 num
= num
% 1000000;
1154 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1158 if (!ast_streamfile(chan
, fn
, language
)) {
1159 if ((audiofd
> -1) && (ctrlfd
> -1))
1160 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1162 res
= ast_waitstream(chan
, ints
);
1164 ast_stopstream(chan
);
1172 /*! \brief ast_say_number_full_he: Hebrew syntax */
1173 /* Extra sounds needed:
1177 2hundred: 2 hundreds
1178 2thousands: 2 thousand
1179 thousands: plural of 'thousand'
1180 3sF 'Smichut forms (female)
1187 3s 'Smichut' forms (male)
1204 TODO: 've' should sometimed be 'hu':
1205 * before 'shtaym' (2, F)
1206 * before 'shnaym' (2, M)
1207 * before 'shlosha' (3, M)
1208 * before 'shmone' (8, M)
1209 * before 'shlosim' (30)
1210 * before 'shmonim' (80)
1216 #define SAY_NUM_BUF_SIZE 256
1217 static int ast_say_number_full_he(struct ast_channel
*chan
, int num
,
1218 const char *ints
, const char *language
, const char *options
,
1219 int audiofd
, int ctrlfd
)
1222 int state
= 0; /* no need to save anything */
1223 int mf
= 1; /* +1 = Masculin; -1 = Feminin */
1224 char fn
[SAY_NUM_BUF_SIZE
] = "";
1225 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: started. "
1226 "num: %d, options=\"%s\"\n",
1230 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1232 if (options
&& !strncasecmp(options
, "f",1))
1235 /* Do we have work to do? */
1236 while (!res
&& (num
|| (state
>0) )) {
1237 /* first type of work: play a second sound. In this loop
1238 * we can only play one sound file at a time. Thus playing
1239 * a second one requires repeating the loop just for the
1240 * second file. The variable 'state' remembers where we were.
1241 * state==0 is the normal mode and it means that we continue
1242 * to check if the number num has yet anything left.
1244 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: num: %d, "
1245 "state=%d, options=\"%s\", mf=%d\n",
1246 num
, state
, options
, mf
1249 snprintf(fn
, sizeof(fn
), "digits/hundred");
1251 } else if (state
==2) {
1252 snprintf(fn
, sizeof(fn
), "digits/ve");
1254 } else if (state
==3) {
1255 snprintf(fn
, sizeof(fn
), "digits/thousands");
1257 } else if (num
<21) {
1259 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1261 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1263 } else if (num
< 100) {
1264 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1267 } else if (num
< 200) {
1268 snprintf(fn
, sizeof(fn
), "digits/1hundred");
1271 } else if (num
< 300) {
1272 snprintf(fn
, sizeof(fn
), "digits/2hundred");
1275 } else if (num
< 1000) {
1276 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1279 } else if (num
< 2000) {
1280 snprintf(fn
, sizeof(fn
), "digits/thousand");
1282 } else if (num
< 3000) {
1283 snprintf(fn
, sizeof(fn
), "digits/2thousand");
1286 } else if (num
< 20000) {
1287 snprintf(fn
, sizeof(fn
), "digits/%ds",(num
/1000));
1290 } else if (num
< 1000000) {
1291 res
= ast_say_number_full_he(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1294 snprintf(fn
, sizeof(fn
), "digits/thousand");
1296 } else if (num
< 1000000000) {
1297 res
= ast_say_number_full_he(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1300 snprintf(fn
, sizeof(fn
), "digits/million");
1301 num
= num
% 1000000;
1303 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1307 if (!ast_streamfile(chan
, fn
, language
)) {
1308 if ((audiofd
> -1) && (ctrlfd
> -1))
1309 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1311 res
= ast_waitstream(chan
, ints
);
1313 ast_stopstream(chan
);
1319 /*! \brief ast_say_number_full_it: Italian */
1320 static int ast_say_number_full_it(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1328 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1333 Like english, numbers up to 20 are a single 'word', and others
1334 compound, but with exceptions.
1335 For example 21 is not twenty-one, but there is a single word in 'it'.
1336 Idem for 28 (ie when a the 2nd part of a compund number
1337 starts with a vowel)
1339 There are exceptions also for hundred, thousand and million.
1340 In english 100 = one hundred, 200 is two hundred.
1341 In italian 100 = cento , like to say hundred (without one),
1342 200 and more are like english.
1344 Same applies for thousand:
1345 1000 is one thousand in en, 2000 is two thousand.
1346 In it we have 1000 = mille , 2000 = 2 mila
1348 For million(s) we use the plural, if more than one
1349 Also, one million is abbreviated in it, like on-million,
1350 or 'un milione', not 'uno milione'.
1351 So the right file is provided.
1354 while (!res
&& (num
|| playh
)) {
1356 snprintf(fn
, sizeof(fn
), "digits/minus");
1357 if ( num
> INT_MIN
) {
1363 snprintf(fn
, sizeof(fn
), "digits/hundred");
1365 } else if (num
< 20) {
1366 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1368 } else if (num
== 21) {
1369 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1371 } else if (num
== 28) {
1372 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1374 } else if (num
== 31) {
1375 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1377 } else if (num
== 38) {
1378 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1380 } else if (num
== 41) {
1381 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1383 } else if (num
== 48) {
1384 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1386 } else if (num
== 51) {
1387 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1389 } else if (num
== 58) {
1390 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1392 } else if (num
== 61) {
1393 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1395 } else if (num
== 68) {
1396 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1398 } else if (num
== 71) {
1399 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1401 } else if (num
== 78) {
1402 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1404 } else if (num
== 81) {
1405 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1407 } else if (num
== 88) {
1408 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1410 } else if (num
== 91) {
1411 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1413 } else if (num
== 98) {
1414 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1416 } else if (num
< 100) {
1417 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1418 num
-= ((num
/ 10) * 10);
1421 if ((num
/ 100) > 1) {
1422 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1425 snprintf(fn
, sizeof(fn
), "digits/hundred");
1427 num
-= ((num
/ 100) * 100);
1429 if (num
< 1000000) { /* 1,000,000 */
1431 res
= ast_say_number_full_it(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1436 if ((tempnum
/ 1000) < 2)
1437 snprintf(fn
, sizeof(fn
), "digits/thousand");
1438 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1439 snprintf(fn
, sizeof(fn
), "digits/thousands");
1441 if (num
< 1000000000) { /* 1,000,000,000 */
1442 if ((num
/ 1000000) > 1)
1443 res
= ast_say_number_full_it(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1447 num
= num
% 1000000;
1448 if ((tempnum
/ 1000000) < 2)
1449 snprintf(fn
, sizeof(fn
), "digits/million");
1451 snprintf(fn
, sizeof(fn
), "digits/millions");
1453 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1460 if (!ast_streamfile(chan
, fn
, language
)) {
1461 if ((audiofd
> -1) && (ctrlfd
> -1))
1462 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1464 res
= ast_waitstream(chan
, ints
);
1466 ast_stopstream(chan
);
1472 /*! \brief ast_say_number_full_nl: dutch syntax */
1473 /* New files: digits/nl-en
1475 static int ast_say_number_full_nl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1482 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1483 while (!res
&& (num
|| playh
)) {
1485 snprintf(fn
, sizeof(fn
), "digits/minus");
1486 if ( num
> INT_MIN
) {
1492 snprintf(fn
, sizeof(fn
), "digits/hundred");
1494 } else if (num
< 20) {
1495 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1497 } else if (num
< 100) {
1500 res
= ast_say_number_full_nl(chan
, units
, ints
, language
, audiofd
, ctrlfd
);
1504 snprintf(fn
, sizeof(fn
), "digits/nl-en");
1506 snprintf(fn
, sizeof(fn
), "digits/%d", num
- units
);
1509 } else if (num
< 200) {
1510 /* hundred, not one-hundred */
1511 ast_copy_string(fn
, "digits/hundred", sizeof(fn
));
1512 num
-= ((num
/ 100) * 100);
1513 } else if (num
< 1000) {
1514 snprintf(fn
, sizeof(fn
), "digits/%d", num
/ 100);
1516 num
-= ((num
/ 100) * 100);
1519 /* thousand, not one-thousand */
1521 ast_copy_string(fn
, "digits/thousand", sizeof(fn
));
1522 } else if (num
< 10000) { /* 1,100 to 9,9999 */
1523 res
= ast_say_number_full_nl(chan
, num
/ 100, ints
, language
, audiofd
, ctrlfd
);
1527 ast_copy_string(fn
, "digits/hundred", sizeof(fn
));
1529 if (num
< 1000000) { /* 1,000,000 */
1530 res
= ast_say_number_full_nl(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1534 snprintf(fn
, sizeof(fn
), "digits/thousand");
1536 if (num
< 1000000000) { /* 1,000,000,000 */
1537 res
= ast_say_number_full_nl(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1540 num
= num
% 1000000;
1541 snprintf(fn
, sizeof(fn
), "digits/million");
1543 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1551 if (!ast_streamfile(chan
, fn
, language
)) {
1552 if ((audiofd
> -1) && (ctrlfd
> -1))
1553 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1555 res
= ast_waitstream(chan
, ints
);
1557 ast_stopstream(chan
);
1563 /*! \brief ast_say_number_full_no: Norwegian syntax */
1565 In addition to American English, the following sounds are required: "and", "1N"
1567 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
)
1572 int cn
= 1; /* +1 = commune; -1 = neuter */
1576 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1578 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
1580 while (!res
&& (num
|| playh
|| playa
)) {
1581 /* The grammar for Norwegian numbers is the same as for English except
1582 * for the following:
1583 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1584 * "and" before the last two digits, i.e. 2034 is "two thousand and
1585 * thirty-four" and 1000012 is "one million and twelve".
1588 snprintf(fn
, sizeof(fn
), "digits/minus");
1589 if ( num
> INT_MIN
) {
1595 snprintf(fn
, sizeof(fn
), "digits/hundred");
1598 snprintf(fn
, sizeof(fn
), "digits/and");
1600 } else if (num
== 1 && cn
== -1) {
1601 snprintf(fn
, sizeof(fn
), "digits/1N");
1603 } else if (num
< 20) {
1604 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1606 } else if (num
< 100) {
1607 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1608 num
-= ((num
/ 10) * 10);
1609 } else if (num
< 1000) {
1610 int hundreds
= num
/ 100;
1612 snprintf(fn
, sizeof(fn
), "digits/1N");
1614 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
1617 num
-= 100 * hundreds
;
1620 } else if (num
< 1000000) {
1621 res
= ast_say_number_full_no(chan
, num
/ 1000, ints
, language
, "n", audiofd
, ctrlfd
);
1624 snprintf(fn
, sizeof(fn
), "digits/thousand");
1626 if (num
&& num
< 100)
1628 } else if (num
< 1000000000) {
1629 int millions
= num
/ 1000000;
1630 res
= ast_say_number_full_no(chan
, millions
, ints
, language
, "c", audiofd
, ctrlfd
);
1633 snprintf(fn
, sizeof(fn
), "digits/million");
1634 num
= num
% 1000000;
1635 if (num
&& num
< 100)
1638 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1643 if (!ast_streamfile(chan
, fn
, language
)) {
1644 if ((audiofd
> -1) && (ctrlfd
> -1))
1645 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1647 res
= ast_waitstream(chan
, ints
);
1649 ast_stopstream(chan
);
1656 char *separator_dziesiatek
;
1660 char *dziesiatki
[10];
1665 static char *pl_rzad_na_tekst(odmiana
*odm
, int i
, int rzad
)
1671 return odm
->rzedy
[rzad
- 1][0];
1672 if ((i
> 21 || i
< 11) && i
%10 > 1 && i
%10 < 5)
1673 return odm
->rzedy
[rzad
- 1][1];
1675 return odm
->rzedy
[rzad
- 1][2];
1678 static char* pl_append(char* buffer
, char* str
)
1680 strcpy(buffer
, str
);
1681 buffer
+= strlen(str
);
1685 static void pl_odtworz_plik(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, char *fn
)
1687 char file_name
[255] = "digits/";
1688 strcat(file_name
, fn
);
1689 ast_log(LOG_DEBUG
, "Trying to play: %s\n", file_name
);
1690 if (!ast_streamfile(chan
, file_name
, language
)) {
1691 if ((audiofd
> -1) && (ctrlfd
> -1))
1692 ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1694 ast_waitstream(chan
, ints
);
1696 ast_stopstream(chan
);
1699 static void powiedz(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, odmiana
*odm
, int rzad
, int i
)
1701 /* Initialise variables to allow compilation on Debian-stable, etc */
1711 if (i
== 0 && rzad
> 0) {
1715 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[0]);
1719 m1000E6
= i
% 1000000000;
1720 i1000E6
= i
/ 1000000000;
1722 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+3, i1000E6
);
1724 m1000E3
= m1000E6
% 1000000;
1725 i1000E3
= m1000E6
/ 1000000;
1727 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+2, i1000E3
);
1729 m1000
= m1000E3
% 1000;
1730 i1000
= m1000E3
/ 1000;
1732 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+1, i1000
);
1738 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->setki
[i100
]);
1740 if ( m100
> 0 && m100
<=9 ) {
1742 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
]);
1744 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[m100
]);
1745 } else if (m100
% 10 == 0) {
1746 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1747 } else if (m100
<= 19 ) {
1748 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->nastki
[m100
% 10]);
1749 } else if (m100
!= 0) {
1750 if (odm
->separator_dziesiatek
[0]==' ') {
1751 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1752 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
% 10]);
1756 b
= pl_append(b
, odm
->dziesiatki
[m100
/ 10]);
1757 b
= pl_append(b
, odm
->separator_dziesiatek
);
1758 b
= pl_append(b
, odm
->cyfry2
[m100
% 10]);
1759 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, buf
);
1764 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, pl_rzad_na_tekst(odm
, i
, rzad
));
1768 /* ast_say_number_full_pl: Polish syntax */
1769 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
)
1779 1000000000.2 miliardy
1780 1000000000.5 miliardow
1844 70m siedemdziesieciu
1856 90m dziewiedziesieciu
1858 and combinations of eg.: 20_1, 30m_3m, etc...
1862 char *zenski_cyfry
[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1864 char *zenski_cyfry2
[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1866 char *meski_cyfry
[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1868 char *meski_cyfry2
[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1870 char *meski_setki
[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1872 char *meski_dziesiatki
[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1874 char *meski_nastki
[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1876 char *nijaki_cyfry
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1878 char *nijaki_cyfry2
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1880 char *nijaki_setki
[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1882 char *nijaki_dziesiatki
[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1884 char *nijaki_nastki
[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1886 char *rzedy
[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1888 /* Initialise variables to allow compilation on Debian-stable, etc */
1891 static odmiana
*odmiana_nieosobowa
= NULL
;
1892 static odmiana
*odmiana_meska
= NULL
;
1893 static odmiana
*odmiana_zenska
= NULL
;
1895 if (odmiana_nieosobowa
== NULL
) {
1896 odmiana_nieosobowa
= (odmiana
*) malloc(sizeof(odmiana
));
1898 odmiana_nieosobowa
->separator_dziesiatek
= " ";
1900 memcpy(odmiana_nieosobowa
->cyfry
, nijaki_cyfry
, sizeof(odmiana_nieosobowa
->cyfry
));
1901 memcpy(odmiana_nieosobowa
->cyfry2
, nijaki_cyfry2
, sizeof(odmiana_nieosobowa
->cyfry
));
1902 memcpy(odmiana_nieosobowa
->setki
, nijaki_setki
, sizeof(odmiana_nieosobowa
->setki
));
1903 memcpy(odmiana_nieosobowa
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_nieosobowa
->dziesiatki
));
1904 memcpy(odmiana_nieosobowa
->nastki
, nijaki_nastki
, sizeof(odmiana_nieosobowa
->nastki
));
1905 memcpy(odmiana_nieosobowa
->rzedy
, rzedy
, sizeof(odmiana_nieosobowa
->rzedy
));
1908 if (odmiana_zenska
== NULL
) {
1909 odmiana_zenska
= (odmiana
*) malloc(sizeof(odmiana
));
1911 odmiana_zenska
->separator_dziesiatek
= " ";
1913 memcpy(odmiana_zenska
->cyfry
, zenski_cyfry
, sizeof(odmiana_zenska
->cyfry
));
1914 memcpy(odmiana_zenska
->cyfry2
, zenski_cyfry2
, sizeof(odmiana_zenska
->cyfry
));
1915 memcpy(odmiana_zenska
->setki
, nijaki_setki
, sizeof(odmiana_zenska
->setki
));
1916 memcpy(odmiana_zenska
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_zenska
->dziesiatki
));
1917 memcpy(odmiana_zenska
->nastki
, nijaki_nastki
, sizeof(odmiana_zenska
->nastki
));
1918 memcpy(odmiana_zenska
->rzedy
, rzedy
, sizeof(odmiana_zenska
->rzedy
));
1921 if (odmiana_meska
== NULL
) {
1922 odmiana_meska
= (odmiana
*) malloc(sizeof(odmiana
));
1924 odmiana_meska
->separator_dziesiatek
= " ";
1926 memcpy(odmiana_meska
->cyfry
, meski_cyfry
, sizeof(odmiana_meska
->cyfry
));
1927 memcpy(odmiana_meska
->cyfry2
, meski_cyfry2
, sizeof(odmiana_meska
->cyfry
));
1928 memcpy(odmiana_meska
->setki
, meski_setki
, sizeof(odmiana_meska
->setki
));
1929 memcpy(odmiana_meska
->dziesiatki
, meski_dziesiatki
, sizeof(odmiana_meska
->dziesiatki
));
1930 memcpy(odmiana_meska
->nastki
, meski_nastki
, sizeof(odmiana_meska
->nastki
));
1931 memcpy(odmiana_meska
->rzedy
, rzedy
, sizeof(odmiana_meska
->rzedy
));
1935 if (strncasecmp(options
, "f", 1) == 0)
1937 else if (strncasecmp(options
, "m", 1) == 0)
1940 o
= odmiana_nieosobowa
;
1942 o
= odmiana_nieosobowa
;
1944 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, o
, 0, num
);
1948 /* ast_say_number_full_pt: Portuguese syntax */
1949 /* Extra sounds needed: */
1950 /* For feminin all sound files end with F */
1951 /* 100E for 100+ something */
1952 /* 1000000S for plural */
1953 /* pt-e for 'and' */
1954 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
)
1958 int mf
= 1; /* +1 = male; -1 = female */
1962 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1964 if (options
&& !strncasecmp(options
, "f",1))
1967 while (!res
&& num
) {
1969 snprintf(fn
, sizeof(fn
), "digits/minus");
1970 if ( num
> INT_MIN
) {
1975 } else if (num
< 20) {
1976 if ((num
== 1 || num
== 2) && (mf
< 0))
1977 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1979 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1981 } else if (num
< 100) {
1982 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 10) * 10);
1986 } else if (num
< 1000) {
1988 snprintf(fn
, sizeof(fn
), "digits/100");
1990 snprintf(fn
, sizeof(fn
), "digits/100E");
1992 if (mf
< 0 && num
> 199)
1993 snprintf(fn
, sizeof(fn
), "digits/%dF", (num
/ 100) * 100);
1995 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100) * 100);
2000 } else if (num
< 1000000) {
2002 res
= ast_say_number_full_pt(chan
, (num
/ 1000) * mf
, ints
, language
, options
, audiofd
, ctrlfd
);
2006 snprintf(fn
, sizeof(fn
), "digits/1000");
2007 if ((num
% 1000) && ((num
% 1000) < 100 || !(num
% 100)))
2010 } else if (num
< 1000000000) {
2011 res
= ast_say_number_full_pt(chan
, (num
/ 1000000), ints
, language
, options
, audiofd
, ctrlfd
);
2015 snprintf(fn
, sizeof(fn
), "digits/1000000");
2017 snprintf(fn
, sizeof(fn
), "digits/1000000S");
2019 if ((num
% 1000000) &&
2021 ((!((num
/ 1000) % 1000) && ((num
% 1000) < 100 || !(num
% 100))) ||
2022 /* no hundreds and below */
2023 (!(num
% 1000) && (((num
/ 1000) % 1000) < 100 || !((num
/ 1000) % 100))) ) )
2025 num
= num
% 1000000;
2027 /* number is too big */
2028 ast_log(LOG_WARNING
, "Number '%d' is too big to say.", num
);
2032 if (!ast_streamfile(chan
, fn
, language
)) {
2033 if ((audiofd
> -1) && (ctrlfd
> -1))
2034 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2036 res
= ast_waitstream(chan
, ints
);
2038 ast_stopstream(chan
);
2040 if (!res
&& playh
) {
2041 res
= wait_file(chan
, ints
, "digits/pt-e", language
);
2042 ast_stopstream(chan
);
2049 /*! \brief ast_say_number_full_se: Swedish syntax */
2050 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
)
2055 int cn
= 1; /* +1 = commune; -1 = neuter */
2057 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2058 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
2060 while (!res
&& (num
|| playh
)) {
2062 snprintf(fn
, sizeof(fn
), "digits/minus");
2063 if ( num
> INT_MIN
) {
2069 snprintf(fn
, sizeof(fn
), "digits/hundred");
2071 } else if (num
< 20) {
2072 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2074 } else if (num
< 100) {
2075 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2076 num
-= ((num
/ 10) * 10);
2077 } else if (num
== 1 && cn
== -1) { /* En eller ett? */
2078 snprintf(fn
, sizeof(fn
), "digits/1N");
2082 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2084 num
-= ((num
/ 100) * 100);
2086 if (num
< 1000000) { /* 1,000,000 */
2087 res
= ast_say_number_full_se(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
2092 snprintf(fn
, sizeof(fn
), "digits/thousand");
2094 if (num
< 1000000000) { /* 1,000,000,000 */
2095 res
= ast_say_number_full_se(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
2099 num
= num
% 1000000;
2100 snprintf(fn
, sizeof(fn
), "digits/million");
2102 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2109 if (!ast_streamfile(chan
, fn
, language
)) {
2110 if ((audiofd
> -1) && (ctrlfd
> -1))
2111 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2113 res
= ast_waitstream(chan
, ints
);
2114 ast_stopstream(chan
);
2121 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2122 static int ast_say_number_full_tw(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2128 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2130 while (!res
&& (num
|| playh
)) {
2132 snprintf(fn
, sizeof(fn
), "digits/minus");
2133 if ( num
> INT_MIN
) {
2139 snprintf(fn
, sizeof(fn
), "digits/hundred");
2141 } else if (num
< 10) {
2142 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2144 } else if (num
< 100) {
2145 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2146 num
-= ((num
/ 10) * 10);
2149 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2151 num
-= ((num
/ 100) * 100);
2153 if (num
< 1000000) { /* 1,000,000 */
2154 res
= ast_say_number_full_tw(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
2158 snprintf(fn
, sizeof(fn
), "digits/thousand");
2160 if (num
< 1000000000) { /* 1,000,000,000 */
2161 res
= ast_say_number_full_tw(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
2164 num
= num
% 1000000;
2165 snprintf(fn
, sizeof(fn
), "digits/million");
2167 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2174 if (!ast_streamfile(chan
, fn
, language
)) {
2175 if ((audiofd
> -1) && (ctrlfd
> -1))
2176 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2178 res
= ast_waitstream(chan
, ints
);
2180 ast_stopstream(chan
);
2187 /*! \brief determine last digits for thousands/millions (ru) */
2188 static int get_lastdigits_ru(int num
) {
2191 } else if (num
< 100) {
2192 return get_lastdigits_ru(num
% 10);
2193 } else if (num
< 1000) {
2194 return get_lastdigits_ru(num
% 100);
2196 return 0; /* number too big */
2200 /*! \brief ast_say_number_full_ru: Russian syntax */
2201 /*! \brief additional files:
2202 n00.gsm (one hundred, two hundred, ...)
2205 thousands-i.gsm (tisyachi)
2206 million-a.gsm (milliona)
2212 where 'n' from 1 to 9
2214 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
)
2220 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2222 while (!res
&& (num
)) {
2224 snprintf(fn
, sizeof(fn
), "digits/minus");
2225 if ( num
> INT_MIN
) {
2230 } else if (num
< 20) {
2231 if (options
&& strlen(options
) == 1 && num
< 3) {
2232 snprintf(fn
, sizeof(fn
), "digits/%d%s", num
, options
);
2234 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2237 } else if (num
< 100) {
2238 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 10));
2240 } else if (num
< 1000){
2241 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 100));
2243 } else if (num
< 1000000) { /* 1,000,000 */
2244 lastdigits
= get_lastdigits_ru(num
/ 1000);
2246 if (lastdigits
< 3) {
2247 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, "f", audiofd
, ctrlfd
);
2249 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2253 if (lastdigits
== 1) {
2254 snprintf(fn
, sizeof(fn
), "digits/thousand");
2255 } else if (lastdigits
> 1 && lastdigits
< 5) {
2256 snprintf(fn
, sizeof(fn
), "digits/thousands-i");
2258 snprintf(fn
, sizeof(fn
), "digits/thousands");
2261 } else if (num
< 1000000000) { /* 1,000,000,000 */
2262 lastdigits
= get_lastdigits_ru(num
/ 1000000);
2264 res
= ast_say_number_full_ru(chan
, num
/ 1000000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2267 if (lastdigits
== 1) {
2268 snprintf(fn
, sizeof(fn
), "digits/million");
2269 } else if (lastdigits
> 1 && lastdigits
< 5) {
2270 snprintf(fn
, sizeof(fn
), "digits/million-a");
2272 snprintf(fn
, sizeof(fn
), "digits/millions");
2276 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2280 if (!ast_streamfile(chan
, fn
, language
)) {
2281 if ((audiofd
> -1) && (ctrlfd
> -1))
2282 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2284 res
= ast_waitstream(chan
, ints
);
2286 ast_stopstream(chan
);
2293 /*! \brief ast_say_enumeration_full: call language-specific functions */
2294 /* Called from AGI */
2295 static int say_enumeration_full(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2297 if (!strcasecmp(language
,"en") ) { /* English syntax */
2298 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2299 } else if (!strcasecmp(language
, "da") ) { /* Danish syntax */
2300 return(ast_say_enumeration_full_da(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2301 } else if (!strcasecmp(language
, "de") ) { /* German syntax */
2302 return(ast_say_enumeration_full_de(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2305 /* Default to english */
2306 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2309 /*! \brief ast_say_enumeration_full_en: English syntax */
2310 /* This is the default syntax, if no other syntax defined in this file is used */
2311 static int ast_say_enumeration_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2316 while (!res
&& num
) {
2318 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2319 if ( num
> INT_MIN
) {
2324 } else if (num
< 20) {
2325 snprintf(fn
, sizeof(fn
), "digits/h-%d", num
);
2327 } else if (num
< 100) {
2328 int tens
= num
/ 10;
2331 snprintf(fn
, sizeof(fn
), "digits/h-%d", (tens
* 10));
2333 snprintf(fn
, sizeof(fn
), "digits/%d", (tens
* 10));
2335 } else if (num
< 1000) {
2336 int hundreds
= num
/ 100;
2338 if (hundreds
> 1 || t
== 1) {
2339 res
= ast_say_number_full_en(chan
, hundreds
, ints
, language
, audiofd
, ctrlfd
);
2344 snprintf(fn
, sizeof(fn
), "digits/hundred");
2346 snprintf(fn
, sizeof(fn
), "digits/h-hundred");
2348 } else if (num
< 1000000) {
2349 int thousands
= num
/ 1000;
2351 if (thousands
> 1 || t
== 1) {
2352 res
= ast_say_number_full_en(chan
, thousands
, ints
, language
, audiofd
, ctrlfd
);
2357 snprintf(fn
, sizeof(fn
), "digits/thousand");
2359 snprintf(fn
, sizeof(fn
), "digits/h-thousand");
2362 } else if (num
< 1000000000) {
2363 int millions
= num
/ 1000000;
2364 num
= num
% 1000000;
2366 res
= ast_say_number_full_en(chan
, millions
, ints
, language
, audiofd
, ctrlfd
);
2370 snprintf(fn
, sizeof(fn
), "digits/million");
2372 snprintf(fn
, sizeof(fn
), "digits/h-million");
2374 } else if (num
< INT_MAX
) {
2375 int billions
= num
/ 1000000000;
2376 num
= num
% 1000000000;
2378 res
= ast_say_number_full_en(chan
, billions
, ints
, language
, audiofd
, ctrlfd
);
2382 snprintf(fn
, sizeof(fn
), "digits/billion");
2384 snprintf(fn
, sizeof(fn
), "digits/h-billion");
2386 } else if (num
== INT_MAX
) {
2387 snprintf(fn
, sizeof(fn
), "digits/h-last");
2390 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2395 if (!ast_streamfile(chan
, fn
, language
)) {
2396 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2397 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2399 res
= ast_waitstream(chan
, ints
);
2402 ast_stopstream(chan
);
2408 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2409 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
)
2411 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2413 char fn
[256] = "", fna
[256] = "";
2416 if (options
&& !strncasecmp(options
, "f",1)) {
2418 } else if (options
&& !strncasecmp(options
, "n",1)) {
2425 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2427 while (!res
&& num
) {
2429 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2430 if ( num
> INT_MIN
) {
2435 } else if (num
< 100 && t
) {
2436 snprintf(fn
, sizeof(fn
), "digits/and");
2438 } else if (num
< 20) {
2439 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2441 } else if (num
< 100) {
2442 int ones
= num
% 10;
2444 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2447 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2450 } else if (num
== 100 && t
== 0) {
2451 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2453 } else if (num
< 1000) {
2454 int hundreds
= num
/ 100;
2456 if (hundreds
== 1) {
2457 snprintf(fn
, sizeof(fn
), "digits/1N");
2459 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2462 snprintf(fna
, sizeof(fna
), "digits/hundred");
2464 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2467 } else if (num
< 1000000) {
2468 int thousands
= num
/ 1000;
2470 if (thousands
== 1) {
2472 snprintf(fn
, sizeof(fn
), "digits/1N");
2473 snprintf(fna
, sizeof(fna
), "digits/thousand");
2476 snprintf(fn
, sizeof(fn
), "digits/1N");
2477 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2479 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2483 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2488 snprintf(fn
, sizeof(fn
), "digits/thousand");
2490 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2494 } else if (num
< 1000000000) {
2495 int millions
= num
/ 1000000;
2496 num
= num
% 1000000;
2497 if (millions
== 1) {
2499 snprintf(fn
, sizeof(fn
), "digits/1F");
2500 snprintf(fna
, sizeof(fna
), "digits/million");
2502 snprintf(fn
, sizeof(fn
), "digits/1N");
2503 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2506 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2511 snprintf(fn
, sizeof(fn
), "digits/millions");
2513 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2517 } else if (num
< INT_MAX
) {
2518 int billions
= num
/ 1000000000;
2519 num
= num
% 1000000000;
2520 if (billions
== 1) {
2522 snprintf(fn
, sizeof(fn
), "digits/1F");
2523 snprintf(fna
, sizeof(fna
), "digits/milliard");
2525 snprintf(fn
, sizeof(fn
), "digits/1N");
2526 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2529 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2533 snprintf(fn
, sizeof(fna
), "digits/milliards");
2535 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2539 } else if (num
== INT_MAX
) {
2540 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2543 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2548 if (!ast_streamfile(chan
, fn
, language
)) {
2549 if ((audiofd
> -1) && (ctrlfd
> -1))
2550 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2552 res
= ast_waitstream(chan
, ints
);
2554 ast_stopstream(chan
);
2556 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2557 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2558 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2560 res
= ast_waitstream(chan
, ints
);
2563 ast_stopstream(chan
);
2571 /*! \brief ast_say_enumeration_full_de: German syntax */
2572 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
)
2574 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2576 char fn
[256] = "", fna
[256] = "";
2579 if (options
&& !strncasecmp(options
, "f",1)) {
2581 } else if (options
&& !strncasecmp(options
, "n",1)) {
2588 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2590 while (!res
&& num
) {
2592 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2593 if ( num
> INT_MIN
) {
2598 } else if (num
< 100 && t
) {
2599 snprintf(fn
, sizeof(fn
), "digits/and");
2601 } else if (num
< 20) {
2602 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2604 } else if (num
< 100) {
2605 int ones
= num
% 10;
2607 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2610 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2613 } else if (num
== 100 && t
== 0) {
2614 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2616 } else if (num
< 1000) {
2617 int hundreds
= num
/ 100;
2619 if (hundreds
== 1) {
2620 snprintf(fn
, sizeof(fn
), "digits/1N");
2622 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2625 snprintf(fna
, sizeof(fna
), "digits/hundred");
2627 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2630 } else if (num
< 1000000) {
2631 int thousands
= num
/ 1000;
2633 if (thousands
== 1) {
2635 snprintf(fn
, sizeof(fn
), "digits/1N");
2636 snprintf(fna
, sizeof(fna
), "digits/thousand");
2639 snprintf(fn
, sizeof(fn
), "digits/1N");
2640 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2642 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2646 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2651 snprintf(fn
, sizeof(fn
), "digits/thousand");
2653 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2657 } else if (num
< 1000000000) {
2658 int millions
= num
/ 1000000;
2659 num
= num
% 1000000;
2660 if (millions
== 1) {
2662 snprintf(fn
, sizeof(fn
), "digits/1F");
2663 snprintf(fna
, sizeof(fna
), "digits/million");
2665 snprintf(fn
, sizeof(fn
), "digits/1N");
2666 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2669 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2674 snprintf(fn
, sizeof(fn
), "digits/millions");
2676 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2680 } else if (num
< INT_MAX
) {
2681 int billions
= num
/ 1000000000;
2682 num
= num
% 1000000000;
2683 if (billions
== 1) {
2685 snprintf(fn
, sizeof(fn
), "digits/1F");
2686 snprintf(fna
, sizeof(fna
), "digits/milliard");
2688 snprintf(fn
, sizeof(fn
), "digits/1N");
2689 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2692 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2696 snprintf(fn
, sizeof(fna
), "digits/milliards");
2698 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2702 } else if (num
== INT_MAX
) {
2703 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2706 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2711 if (!ast_streamfile(chan
, fn
, language
)) {
2712 if ((audiofd
> -1) && (ctrlfd
> -1))
2713 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2715 res
= ast_waitstream(chan
, ints
);
2717 ast_stopstream(chan
);
2719 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2720 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2721 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2723 res
= ast_waitstream(chan
, ints
);
2726 ast_stopstream(chan
);
2734 static int say_date(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2736 if (!strcasecmp(lang
, "en") ) { /* English syntax */
2737 return(ast_say_date_en(chan
, t
, ints
, lang
));
2738 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
2739 return(ast_say_date_da(chan
, t
, ints
, lang
));
2740 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
2741 return(ast_say_date_de(chan
, t
, ints
, lang
));
2742 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
2743 return(ast_say_date_fr(chan
, t
, ints
, lang
));
2744 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
2745 return(ast_say_date_nl(chan
, t
, ints
, lang
));
2746 } else if (!strcasecmp(lang
, "pt") || !strcasecmp(lang
, "pt_BR")) { /* Portuguese syntax */
2747 return(ast_say_date_pt(chan
, t
, ints
, lang
));
2748 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
2749 return(ast_say_date_gr(chan
, t
, ints
, lang
));
2750 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
2751 return(ast_say_date_ge(chan
, t
, ints
, lang
));
2754 /* Default to English */
2755 return(ast_say_date_en(chan
, t
, ints
, lang
));
2758 /* English syntax */
2759 int ast_say_date_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2764 ast_localtime(&t
,&tm
,NULL
);
2766 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2767 res
= ast_streamfile(chan
, fn
, lang
);
2769 res
= ast_waitstream(chan
, ints
);
2772 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2773 res
= ast_streamfile(chan
, fn
, lang
);
2775 res
= ast_waitstream(chan
, ints
);
2778 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2780 res
= ast_waitstream(chan
, ints
);
2782 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2787 int ast_say_date_da(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2792 ast_localtime(&t
,&tm
,NULL
);
2794 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2795 res
= ast_streamfile(chan
, fn
, lang
);
2797 res
= ast_waitstream(chan
, ints
);
2800 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2802 res
= ast_waitstream(chan
, ints
);
2804 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2805 res
= ast_streamfile(chan
, fn
, lang
);
2807 res
= ast_waitstream(chan
, ints
);
2811 int year
= tm
.tm_year
+ 1900;
2812 if (year
> 1999) { /* year 2000 and later */
2813 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2816 /* I'm not going to handle 1100 and prior */
2817 /* We'll just be silent on the year, instead of bombing out. */
2819 /* year 1100 to 1999. will anybody need this?!? */
2820 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2821 res
= wait_file(chan
, ints
, fn
, lang
);
2823 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2824 if (!res
&& year
% 100 != 0) {
2825 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
2835 int ast_say_date_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2840 ast_localtime(&t
,&tm
,NULL
);
2842 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2843 res
= ast_streamfile(chan
, fn
, lang
);
2845 res
= ast_waitstream(chan
, ints
);
2848 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2850 res
= ast_waitstream(chan
, ints
);
2852 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2853 res
= ast_streamfile(chan
, fn
, lang
);
2855 res
= ast_waitstream(chan
, ints
);
2859 int year
= tm
.tm_year
+ 1900;
2860 if (year
> 1999) { /* year 2000 and later */
2861 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2864 /* I'm not going to handle 1100 and prior */
2865 /* We'll just be silent on the year, instead of bombing out. */
2867 /* year 1100 to 1999. will anybody need this?!? */
2868 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2869 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2870 res
= wait_file(chan
, ints
, fn
, lang
);
2872 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2873 if (!res
&& year
% 100 != 0) {
2874 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
2884 int ast_say_date_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2889 ast_localtime(&t
,&tm
,NULL
);
2891 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2892 res
= ast_streamfile(chan
, fn
, lang
);
2894 res
= ast_waitstream(chan
, ints
);
2897 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2899 res
= ast_waitstream(chan
, ints
);
2901 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2902 res
= ast_streamfile(chan
, fn
, lang
);
2904 res
= ast_waitstream(chan
, ints
);
2907 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2912 int ast_say_date_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2917 ast_localtime(&t
,&tm
,NULL
);
2919 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2920 res
= ast_streamfile(chan
, fn
, lang
);
2922 res
= ast_waitstream(chan
, ints
);
2925 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2927 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2928 res
= ast_streamfile(chan
, fn
, lang
);
2930 res
= ast_waitstream(chan
, ints
);
2933 res
= ast_waitstream(chan
, ints
);
2935 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2939 /* Portuguese syntax */
2940 int ast_say_date_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2946 ast_localtime(&t
, &tm
, NULL
);
2947 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2949 res
= wait_file(chan
, ints
, fn
, lang
);
2951 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
2953 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
2954 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2956 res
= wait_file(chan
, ints
, fn
, lang
);
2958 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
2960 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2965 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
)
2967 if (!strcasecmp(lang
, "en") ) { /* English syntax */
2968 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
2969 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
2970 return(ast_say_date_with_format_da(chan
, time
, ints
, lang
, format
, timezone
));
2971 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
2972 return(ast_say_date_with_format_de(chan
, time
, ints
, lang
, format
, timezone
));
2973 } else if (!strcasecmp(lang
, "es") || !strcasecmp(lang
, "mx")) { /* Spanish syntax */
2974 return(ast_say_date_with_format_es(chan
, time
, ints
, lang
, format
, timezone
));
2975 } else if (!strcasecmp(lang
, "he")) { /* Hebrew syntax */
2976 return(ast_say_date_with_format_he(chan
, time
, ints
, lang
, format
, timezone
));
2977 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
2978 return(ast_say_date_with_format_fr(chan
, time
, ints
, lang
, format
, timezone
));
2979 } else if (!strcasecmp(lang
, "it") ) { /* Italian syntax */
2980 return(ast_say_date_with_format_it(chan
, time
, ints
, lang
, format
, timezone
));
2981 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
2982 return(ast_say_date_with_format_nl(chan
, time
, ints
, lang
, format
, timezone
));
2983 } else if (!strcasecmp(lang
, "pl") ) { /* Polish syntax */
2984 return(ast_say_date_with_format_pl(chan
, time
, ints
, lang
, format
, timezone
));
2985 } else if (!strcasecmp(lang
, "pt") || !strcasecmp(lang
, "pt_BR")) { /* Portuguese syntax */
2986 return(ast_say_date_with_format_pt(chan
, time
, ints
, lang
, format
, timezone
));
2987 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
2988 return(ast_say_date_with_format_tw(chan
, time
, ints
, lang
, format
, timezone
));
2989 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
2990 return(ast_say_date_with_format_gr(chan
, time
, ints
, lang
, format
, timezone
));
2993 /* Default to English */
2994 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
2997 /* English syntax */
2998 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
)
3001 int res
=0, offset
, sndoffset
;
3002 char sndfile
[256], nextmsg
[256];
3005 format
= "ABdY 'digits/at' IMp";
3007 ast_localtime(&time
,&tm
,timezone
);
3009 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3010 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3011 switch (format
[offset
]) {
3012 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3014 /* Literal name of a sound file */
3016 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3017 sndfile
[sndoffset
] = format
[offset
];
3018 sndfile
[sndoffset
] = '\0';
3019 res
= wait_file(chan
,ints
,sndfile
,lang
);
3023 /* Sunday - Saturday */
3024 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3025 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3030 /* January - December */
3031 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3032 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3035 /* Month enumerated */
3036 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, (char *) NULL
);
3040 /* First - Thirtyfirst */
3041 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
3045 if (tm
.tm_year
> 99) {
3046 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
3047 } else if (tm
.tm_year
< 1) {
3048 /* I'm not going to handle 1900 and prior */
3049 /* We'll just be silent on the year, instead of bombing out. */
3051 res
= wait_file(chan
, ints
, "digits/19", lang
);
3053 if (tm
.tm_year
<= 9) {
3055 res
= wait_file(chan
,ints
, "digits/oh", lang
);
3058 res
|= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char *) NULL
);
3065 if (tm
.tm_hour
== 0)
3066 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3067 else if (tm
.tm_hour
> 12)
3068 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3070 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3071 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3076 if (format
[offset
] == 'H') {
3078 if (tm
.tm_hour
< 10) {
3079 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3083 if (tm
.tm_hour
== 0) {
3084 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3088 if (tm
.tm_hour
!= 0) {
3089 int remainder
= tm
.tm_hour
;
3090 if (tm
.tm_hour
> 20) {
3091 res
= wait_file(chan
,ints
, "digits/20",lang
);
3095 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
3096 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3104 if (tm
.tm_min
== 0) {
3105 if (format
[offset
] == 'M') {
3106 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
3108 res
= wait_file(chan
, ints
, "digits/hundred", lang
);
3110 } else if (tm
.tm_min
< 10) {
3111 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3113 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
3114 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3117 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
3123 if (tm
.tm_hour
> 11)
3124 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3126 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3127 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3130 /* Shorthand for "Today", "Yesterday", or ABdY */
3131 /* XXX As emphasized elsewhere, this should the native way in your
3132 * language to say the date, with changes in what you say, depending
3133 * upon how recent the date is. XXX */
3137 time_t beg_today
, tt
;
3139 gettimeofday(&now
,NULL
);
3141 ast_localtime(&tt
,&tmnow
,timezone
);
3142 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3143 /* In any case, it saves not having to do ast_mktime() */
3144 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3145 if (beg_today
< time
) {
3147 res
= wait_file(chan
,ints
, "digits/today",lang
);
3148 } else if (beg_today
- 86400 < time
) {
3150 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3151 } else if (beg_today
- 86400 * 6 < time
) {
3152 /* Within the last week */
3153 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3154 } else if (beg_today
- 2628000 < time
) {
3155 /* Less than a month ago - "Sunday, October third" */
3156 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3157 } else if (beg_today
- 15768000 < time
) {
3158 /* Less than 6 months ago - "August seventh" */
3159 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3161 /* More than 6 months ago - "April nineteenth two thousand three" */
3162 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3167 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3168 /* XXX As emphasized elsewhere, this should the native way in your
3169 * language to say the date, with changes in what you say, depending
3170 * upon how recent the date is. XXX */
3174 time_t beg_today
, tt
;
3176 gettimeofday(&now
,NULL
);
3178 ast_localtime(&tt
,&tmnow
,timezone
);
3179 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3180 /* In any case, it saves not having to do ast_mktime() */
3181 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3182 if (beg_today
< time
) {
3184 } else if ((beg_today
- 86400) < time
) {
3186 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3187 } else if (beg_today
- 86400 * 6 < time
) {
3188 /* Within the last week */
3189 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3190 } else if (beg_today
- 2628000 < time
) {
3191 /* Less than a month ago - "Sunday, October third" */
3192 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3193 } else if (beg_today
- 15768000 < time
) {
3194 /* Less than 6 months ago - "August seventh" */
3195 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3197 /* More than 6 months ago - "April nineteenth two thousand three" */
3198 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3203 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HM", timezone
);
3207 if (tm
.tm_sec
== 0) {
3208 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3209 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3210 } else if (tm
.tm_sec
< 10) {
3211 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3213 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3214 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3217 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, (char *) NULL
);
3221 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HMS", timezone
);
3225 /* Just ignore spaces and tabs */
3228 /* Unknown character */
3229 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3231 /* Jump out on DTMF */
3240 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
)
3243 int res
=0, offset
, sndoffset
;
3244 char sndfile
[256], nextmsg
[256];
3247 format
= "A dBY HMS";
3249 ast_localtime(&time
,&tm
,timezone
);
3251 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3252 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3253 switch (format
[offset
]) {
3254 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3256 /* Literal name of a sound file */
3258 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3259 sndfile
[sndoffset
] = format
[offset
];
3260 sndfile
[sndoffset
] = '\0';
3261 res
= wait_file(chan
,ints
,sndfile
,lang
);
3265 /* Sunday - Saturday */
3266 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3267 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3272 /* January - December */
3273 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3274 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3277 /* Month enumerated */
3278 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3282 /* First - Thirtyfirst */
3283 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3288 int year
= tm
.tm_year
+ 1900;
3289 if (year
> 1999) { /* year 2000 and later */
3290 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3293 /* I'm not going to handle 1100 and prior */
3294 /* We'll just be silent on the year, instead of bombing out. */
3296 /* year 1100 to 1999. will anybody need this?!? */
3297 /* say 1967 as 'nineteen hundred seven and sixty' */
3298 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3299 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3301 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3302 if (!res
&& year
% 100 != 0) {
3303 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3313 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3314 if (tm
.tm_hour
== 0)
3315 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3316 else if (tm
.tm_hour
> 12)
3317 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3319 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3321 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3325 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3326 if (tm
.tm_hour
< 10 && tm
.tm_hour
> 0) {
3327 res
= wait_file(chan
,ints
, "digits/0",lang
);
3332 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3336 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3337 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3339 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3340 if (tm
.tm_min
== 1) {
3341 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3343 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3350 if (tm
.tm_hour
> 11)
3351 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3353 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3354 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3357 /* Shorthand for "Today", "Yesterday", or AdBY */
3358 /* XXX As emphasized elsewhere, this should the native way in your
3359 * language to say the date, with changes in what you say, depending
3360 * upon how recent the date is. XXX */
3364 time_t beg_today
, tt
;
3366 gettimeofday(&now
,NULL
);
3368 ast_localtime(&tt
,&tmnow
,timezone
);
3369 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3370 /* In any case, it saves not having to do ast_mktime() */
3371 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3372 if (beg_today
< time
) {
3374 res
= wait_file(chan
,ints
, "digits/today",lang
);
3375 } else if (beg_today
- 86400 < time
) {
3377 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3379 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3384 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3385 /* XXX As emphasized elsewhere, this should the native way in your
3386 * language to say the date, with changes in what you say, depending
3387 * upon how recent the date is. XXX */
3391 time_t beg_today
, tt
;
3393 gettimeofday(&now
,NULL
);
3395 ast_localtime(&tt
,&tmnow
,timezone
);
3396 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3397 /* In any case, it saves not having to do ast_mktime() */
3398 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3399 if (beg_today
< time
) {
3401 } else if ((beg_today
- 86400) < time
) {
3403 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3404 } else if (beg_today
- 86400 * 6 < time
) {
3405 /* Within the last week */
3406 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "A", timezone
);
3408 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3413 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HM", timezone
);
3417 res
= wait_file(chan
,ints
, "digits/and",lang
);
3419 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3421 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3426 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HMS", timezone
);
3430 /* Just ignore spaces and tabs */
3433 /* Unknown character */
3434 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3436 /* Jump out on DTMF */
3445 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
)
3448 int res
=0, offset
, sndoffset
;
3449 char sndfile
[256], nextmsg
[256];
3452 format
= "A dBY HMS";
3454 ast_localtime(&time
,&tm
,timezone
);
3456 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3457 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3458 switch (format
[offset
]) {
3459 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3461 /* Literal name of a sound file */
3463 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3464 sndfile
[sndoffset
] = format
[offset
];
3465 sndfile
[sndoffset
] = '\0';
3466 res
= wait_file(chan
,ints
,sndfile
,lang
);
3470 /* Sunday - Saturday */
3471 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3472 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3477 /* January - December */
3478 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3479 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3482 /* Month enumerated */
3483 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3487 /* First - Thirtyfirst */
3488 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3493 int year
= tm
.tm_year
+ 1900;
3494 if (year
> 1999) { /* year 2000 and later */
3495 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3498 /* I'm not going to handle 1100 and prior */
3499 /* We'll just be silent on the year, instead of bombing out. */
3501 /* year 1100 to 1999. will anybody need this?!? */
3502 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3503 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3504 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3506 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3507 if (!res
&& year
% 100 != 0) {
3508 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3518 if (tm
.tm_hour
== 0)
3519 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3520 else if (tm
.tm_hour
> 12)
3521 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3523 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3524 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3526 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3532 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3534 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3539 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3540 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3542 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3543 if (tm
.tm_min
== 1) {
3544 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3546 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3553 if (tm
.tm_hour
> 11)
3554 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3556 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3557 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3560 /* Shorthand for "Today", "Yesterday", or AdBY */
3561 /* XXX As emphasized elsewhere, this should the native way in your
3562 * language to say the date, with changes in what you say, depending
3563 * upon how recent the date is. XXX */
3567 time_t beg_today
, tt
;
3569 gettimeofday(&now
,NULL
);
3571 ast_localtime(&tt
,&tmnow
,timezone
);
3572 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3573 /* In any case, it saves not having to do ast_mktime() */
3574 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3575 if (beg_today
< time
) {
3577 res
= wait_file(chan
,ints
, "digits/today",lang
);
3578 } else if (beg_today
- 86400 < time
) {
3580 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3582 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3587 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3588 /* XXX As emphasized elsewhere, this should the native way in your
3589 * language to say the date, with changes in what you say, depending
3590 * upon how recent the date is. XXX */
3594 time_t beg_today
, tt
;
3596 gettimeofday(&now
,NULL
);
3598 ast_localtime(&tt
,&tmnow
,timezone
);
3599 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3600 /* In any case, it saves not having to do ast_mktime() */
3601 beg_today
= now
.tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3602 if (beg_today
< time
) {
3604 } else if ((beg_today
- 86400) < time
) {
3606 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3607 } else if (beg_today
- 86400 * 6 < time
) {
3608 /* Within the last week */
3609 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "A", timezone
);
3611 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3616 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HM", timezone
);
3620 res
= wait_file(chan
,ints
, "digits/and",lang
);
3622 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3624 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3629 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HMS", timezone
);
3633 /* Just ignore spaces and tabs */
3636 /* Unknown character */
3637 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3639 /* Jump out on DTMF */
3647 /* TODO: this probably is not the correct format for doxygen remarks */
3649 /** ast_say_date_with_format_he Say formmated date in Hebrew
3651 * \ref ast_say_date_with_format_en for the details of the options
3653 * Changes from the English version:
3655 * * don't replicate in here the logic of ast_say_number_full_he
3657 * * year is always 4-digit (because it's simpler)
3659 * * added c, x, and X. Mainly for my tests
3661 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3664 * * A "ha" is missing in the standard date format, before the 'd'.
3665 * * The numbers of 3000--19000 are not handled well
3667 #define IL_DATE_STR "AdBY"
3668 #define IL_TIME_STR "IMp"
3669 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3670 int ast_say_date_with_format_he(struct ast_channel
*chan
, time_t time
,
3671 const char *ints
, const char *lang
, const char *format
,
3672 const char *timezone
)
3674 /* TODO: This whole function is cut&paste from
3675 * ast_say_date_with_format_en . Is that considered acceptable?
3678 int res
=0, offset
, sndoffset
;
3679 char sndfile
[256], nextmsg
[256];
3682 format
= IL_DATE_STR_FULL
;
3684 ast_localtime(&time
,&tm
,timezone
);
3686 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3687 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3688 switch (format
[offset
]) {
3689 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3691 /* Literal name of a sound file */
3693 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3694 sndfile
[sndoffset
] = format
[offset
];
3695 sndfile
[sndoffset
] = '\0';
3696 res
= wait_file(chan
,ints
,sndfile
,lang
);
3700 /* Sunday - Saturday */
3701 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3702 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3707 /* January - December */
3708 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3709 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3712 case 'e': /* Day of the month */
3713 /* I'm not sure exactly what the parameters
3714 * audiofd and ctrlfd to
3715 * ast_say_number_full_he mean, but it seems
3716 * safe to pass -1 there.
3718 * At least in one of the pathes :-(
3720 res
= ast_say_number_full_he(chan
, tm
.tm_mday
,
3721 ints
, lang
, "m", -1, -1
3724 case 'Y': /* Year */
3725 res
= ast_say_number_full_he(chan
, tm
.tm_year
+1900,
3726 ints
, lang
, "f", -1, -1
3730 case 'l': /* 12-Hour */
3732 int hour
= tm
.tm_hour
;
3734 if (hour
== 0) hour
=12;
3736 res
= ast_say_number_full_he(chan
, hour
,
3737 ints
, lang
, "f", -1, -1
3742 case 'k': /* 24-Hour */
3743 /* With 'H' there is an 'oh' after a single-
3745 if ((format
[offset
] == 'H') &&
3746 (tm
.tm_hour
<10)&&(tm
.tm_hour
>0)
3747 ) { /* e.g. oh-eight */
3748 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3751 res
= ast_say_number_full_he(chan
, tm
.tm_hour
,
3752 ints
, lang
, "f", -1, -1
3755 case 'M': /* Minute */
3756 res
= ast_say_number_full_he(chan
, tm
.tm_min
,
3757 ints
, lang
,"f", -1, -1
3763 if (tm
.tm_hour
> 11)
3764 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3766 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3767 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3770 /* Shorthand for "Today", "Yesterday", or "date" */
3772 /* Shorthand for "" (today), "Yesterday", A
3773 * (weekday), or "date" */
3774 /* XXX As emphasized elsewhere, this should the native way in your
3775 * language to say the date, with changes in what you say, depending
3776 * upon how recent the date is. XXX */
3780 time_t beg_today
, tt
;
3781 char todo
= format
[offset
]; /* The letter to format*/
3783 gettimeofday(&now
,NULL
);
3785 ast_localtime(&tt
,&tmnow
,timezone
);
3786 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3787 /* In any case, it saves not having to do ast_mktime() */
3788 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3789 if (beg_today
< time
) {
3792 res
= wait_file(chan
,
3797 } else if (beg_today
- 86400 < time
) {
3799 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3800 } else if ((todo
!= 'Q') &&
3801 (beg_today
- 86400 * 6 < time
))
3803 /* Within the last week */
3804 res
= ast_say_date_with_format_he(chan
,
3808 res
= ast_say_date_with_format_he(chan
,
3810 IL_DATE_STR
, timezone
);
3815 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HM", timezone
);
3817 case 'S': /* Seconds */
3818 res
= ast_say_number_full_he(chan
, tm
.tm_sec
,
3819 ints
, lang
, "f", -1, -1
3823 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HMS", timezone
);
3825 /* c, x, and X seem useful for testing. Not sure
3826 * if thiey're good for the general public */
3828 res
= ast_say_date_with_format_he(chan
, time
,
3829 ints
, lang
, IL_DATE_STR_FULL
, timezone
);
3832 res
= ast_say_date_with_format_he(chan
, time
,
3833 ints
, lang
, IL_DATE_STR
, timezone
);
3835 case 'X': /* Currently not locale-dependent...*/
3836 res
= ast_say_date_with_format_he(chan
, time
,
3837 ints
, lang
, IL_TIME_STR
, timezone
);
3841 /* Just ignore spaces and tabs */
3844 /* Unknown character */
3845 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3847 /* Jump out on DTMF */
3856 /* Spanish syntax */
3857 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
)
3860 int res
=0, offset
, sndoffset
;
3861 char sndfile
[256], nextmsg
[256];
3864 format
= "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
3866 ast_localtime(&time
,&tm
,timezone
);
3868 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3869 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3870 switch (format
[offset
]) {
3871 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3873 /* Literal name of a sound file */
3875 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3876 sndfile
[sndoffset
] = format
[offset
];
3877 sndfile
[sndoffset
] = '\0';
3878 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
3879 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3883 /* Sunday - Saturday */
3884 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3885 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3890 /* January - December */
3891 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3892 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3895 /* First - Twelfth */
3896 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
3897 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3901 /* First - Thirtyfirst */
3902 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
3906 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
3911 if (tm
.tm_hour
== 0)
3912 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3913 else if (tm
.tm_hour
> 12)
3914 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3916 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3917 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3922 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, NULL
);
3926 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
3931 if (tm
.tm_hour
> 18)
3932 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
3933 else if (tm
.tm_hour
> 12)
3934 res
= wait_file(chan
, ints
, "digits/afternoon", lang
);
3935 else if (tm
.tm_hour
)
3936 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
3939 /* Shorthand for "Today", "Yesterday", or ABdY */
3940 /* XXX As emphasized elsewhere, this should the native way in your
3941 * language to say the date, with changes in what you say, depending
3942 * upon how recent the date is. XXX */
3946 time_t beg_today
, tt
;
3948 gettimeofday(&now
,NULL
);
3950 ast_localtime(&tt
,&tmnow
,timezone
);
3951 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3952 /* In any case, it saves not having to do ast_mktime() */
3953 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3954 if (beg_today
< time
) {
3956 res
= wait_file(chan
,ints
, "digits/today",lang
);
3957 } else if (beg_today
- 86400 < time
) {
3959 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3961 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
3966 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3967 /* XXX As emphasized elsewhere, this should the native way in your
3968 * language to say the date, with changes in what you say, depending
3969 * upon how recent the date is. XXX */
3973 time_t beg_today
, tt
;
3975 gettimeofday(&now
,NULL
);
3977 ast_localtime(&tt
,&tmnow
,timezone
);
3978 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3979 /* In any case, it saves not having to do ast_mktime() */
3980 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3981 if (beg_today
< time
) {
3983 res
= wait_file(chan
,ints
, "digits/today",lang
);
3984 } else if ((beg_today
- 86400) < time
) {
3986 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3987 } else if (beg_today
- 86400 * 6 < time
) {
3988 /* Within the last week */
3989 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "A", timezone
);
3991 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
3996 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "H 'digits/y' M", timezone
);
4000 if (tm
.tm_sec
== 0) {
4001 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4002 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4003 } else if (tm
.tm_sec
< 10) {
4004 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4006 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4007 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4009 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
4010 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4011 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4014 ten
= (tm
.tm_sec
/ 10) * 10;
4015 one
= (tm
.tm_sec
% 10);
4016 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
4017 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4019 /* Fifty, not fifty-zero */
4021 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4022 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4028 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "HMS", timezone
);
4032 /* Just ignore spaces and tabs */
4035 /* Unknown character */
4036 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4038 /* Jump out on DTMF */
4049 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
)
4052 int res
=0, offset
, sndoffset
;
4053 char sndfile
[256], nextmsg
[256];
4056 format
= "AdBY 'digits/at' IMp";
4058 ast_localtime(&time
,&tm
,timezone
);
4060 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4061 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4062 switch (format
[offset
]) {
4063 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4065 /* Literal name of a sound file */
4067 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4068 sndfile
[sndoffset
] = format
[offset
];
4069 sndfile
[sndoffset
] = '\0';
4070 res
= wait_file(chan
,ints
,sndfile
,lang
);
4074 /* Sunday - Saturday */
4075 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4076 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4081 /* January - December */
4082 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4083 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4086 /* First - Twelfth */
4087 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4088 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4093 if (tm
.tm_mday
== 1) {
4094 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4095 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4097 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
4102 if (tm
.tm_year
> 99) {
4103 res
= wait_file(chan
,ints
, "digits/2",lang
);
4105 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4107 if (tm
.tm_year
> 100) {
4109 res
= ast_say_number(chan
, tm
.tm_year
- 100, ints
, lang
, (char * ) NULL
);
4113 if (tm
.tm_year
< 1) {
4114 /* I'm not going to handle 1900 and prior */
4115 /* We'll just be silent on the year, instead of bombing out. */
4117 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4119 wait_file(chan
,ints
, "digits/9",lang
);
4120 wait_file(chan
,ints
, "digits/hundred",lang
);
4121 res
= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char * ) NULL
);
4129 if (tm
.tm_hour
== 0)
4130 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4131 else if (tm
.tm_hour
> 12)
4132 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4134 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4135 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4137 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4142 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char * ) NULL
);
4144 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4148 if (tm
.tm_min
== 0) {
4151 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char * ) NULL
);
4156 if (tm
.tm_hour
> 11)
4157 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4159 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4160 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4163 /* Shorthand for "Today", "Yesterday", or AdBY */
4164 /* XXX As emphasized elsewhere, this should the native way in your
4165 * language to say the date, with changes in what you say, depending
4166 * upon how recent the date is. XXX */
4170 time_t beg_today
, tt
;
4172 gettimeofday(&now
,NULL
);
4174 ast_localtime(&tt
,&tmnow
,timezone
);
4175 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4176 /* In any case, it saves not having to do ast_mktime() */
4177 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4178 if (beg_today
< time
) {
4180 res
= wait_file(chan
,ints
, "digits/today",lang
);
4181 } else if (beg_today
- 86400 < time
) {
4183 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4185 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4190 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4191 /* XXX As emphasized elsewhere, this should the native way in your
4192 * language to say the date, with changes in what you say, depending
4193 * upon how recent the date is. XXX */
4197 time_t beg_today
, tt
;
4199 gettimeofday(&now
,NULL
);
4201 ast_localtime(&tt
,&tmnow
,timezone
);
4202 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4203 /* In any case, it saves not having to do ast_mktime() */
4204 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4205 if (beg_today
< time
) {
4207 } else if ((beg_today
- 86400) < time
) {
4209 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4210 } else if (beg_today
- 86400 * 6 < time
) {
4211 /* Within the last week */
4212 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "A", timezone
);
4214 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4219 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HM", timezone
);
4223 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, (char * ) NULL
);
4225 res
= wait_file(chan
,ints
, "digits/second",lang
);
4229 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HMS", timezone
);
4233 /* Just ignore spaces and tabs */
4236 /* Unknown character */
4237 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4239 /* Jump out on DTMF */
4247 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
)
4250 int res
=0, offset
, sndoffset
;
4251 char sndfile
[256], nextmsg
[256];
4254 format
= "AdB 'digits/at' IMp";
4256 ast_localtime(&time
,&tm
,timezone
);
4258 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4259 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4260 switch (format
[offset
]) {
4261 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4263 /* Literal name of a sound file */
4265 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4266 sndfile
[sndoffset
] = format
[offset
];
4267 sndfile
[sndoffset
] = '\0';
4268 res
= wait_file(chan
,ints
,sndfile
,lang
);
4272 /* Sunday - Saturday */
4273 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4274 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4279 /* January - December */
4280 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4281 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4284 /* First - Twelfth */
4285 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4286 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4290 /* First day of the month is spelled as ordinal */
4291 if (tm
.tm_mday
== 1) {
4292 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4293 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4296 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4302 if (tm
.tm_year
> 99) {
4303 res
= wait_file(chan
,ints
, "digits/ore-2000",lang
);
4304 if (tm
.tm_year
> 100) {
4306 /* This works until the end of 2021 */
4307 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4308 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4312 if (tm
.tm_year
< 1) {
4313 /* I'm not going to handle 1900 and prior */
4314 /* We'll just be silent on the year, instead of bombing out. */
4316 res
= wait_file(chan
,ints
, "digits/ore-1900",lang
);
4317 if ((!res
) && (tm
.tm_year
!= 0)) {
4318 if (tm
.tm_year
<= 21) {
4320 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4321 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4323 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4325 ten
= tm
.tm_year
/ 10;
4326 one
= tm
.tm_year
% 10;
4327 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4328 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4331 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4332 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4343 if (tm
.tm_hour
== 0)
4344 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4345 else if (tm
.tm_hour
> 12)
4346 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4348 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4349 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4354 if (tm
.tm_hour
== 0) {
4355 res
= wait_file(chan
,ints
, "digits/ore-mezzanotte",lang
);
4356 } else if (tm
.tm_hour
== 1) {
4357 res
= wait_file(chan
,ints
, "digits/ore-una",lang
);
4359 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4364 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4369 if (tm
.tm_hour
> 11)
4370 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4372 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4373 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4376 /* Shorthand for "Today", "Yesterday", or ABdY */
4377 /* XXX As emphasized elsewhere, this should the native way in your
4378 * language to say the date, with changes in what you say, depending
4379 * upon how recent the date is. XXX */
4383 time_t beg_today
, tt
;
4385 gettimeofday(&now
,NULL
);
4387 ast_localtime(&tt
,&tmnow
,timezone
);
4388 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4389 /* In any case, it saves not having to do ast_mktime() */
4390 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4391 if (beg_today
< time
) {
4393 res
= wait_file(chan
,ints
, "digits/today",lang
);
4394 } else if (beg_today
- 86400 < time
) {
4396 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4398 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4403 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4407 time_t beg_today
, tt
;
4409 gettimeofday(&now
,NULL
);
4411 ast_localtime(&tt
,&tmnow
,timezone
);
4412 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4413 /* In any case, it saves not having to do ast_mktime() */
4414 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4415 if (beg_today
< time
) {
4417 } else if ((beg_today
- 86400) < time
) {
4419 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4420 } else if (beg_today
- 86400 * 6 < time
) {
4421 /* Within the last week */
4422 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "A", timezone
);
4424 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4429 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HM", timezone
);
4433 if (tm
.tm_sec
== 0) {
4434 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4435 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4436 } else if (tm
.tm_sec
< 10) {
4437 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4439 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4440 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4442 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
4443 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4444 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4447 ten
= (tm
.tm_sec
/ 10) * 10;
4448 one
= (tm
.tm_sec
% 10);
4449 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
4450 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4452 /* Fifty, not fifty-zero */
4454 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4455 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4461 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HMS", timezone
);
4465 /* Just ignore spaces and tabs */
4468 /* Unknown character */
4469 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4471 /* Jump out on DTMF */
4480 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
)
4483 int res
=0, offset
, sndoffset
;
4484 char sndfile
[256], nextmsg
[256];
4487 format
= "ABdY 'digits/at' IMp";
4489 ast_localtime(&time
,&tm
,timezone
);
4491 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4492 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4493 switch (format
[offset
]) {
4494 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4496 /* Literal name of a sound file */
4498 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4499 sndfile
[sndoffset
] = format
[offset
];
4500 sndfile
[sndoffset
] = '\0';
4501 res
= wait_file(chan
,ints
,sndfile
,lang
);
4505 /* Sunday - Saturday */
4506 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4507 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4512 /* January - December */
4513 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4514 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4517 /* First - Twelfth */
4518 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4519 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4523 /* First - Thirtyfirst */
4524 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, NULL
);
4528 if (tm
.tm_year
> 99) {
4529 res
= wait_file(chan
,ints
, "digits/2",lang
);
4531 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4533 if (tm
.tm_year
> 100) {
4535 /* This works until the end of 2020 */
4536 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4537 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4541 if (tm
.tm_year
< 1) {
4542 /* I'm not going to handle 1900 and prior */
4543 /* We'll just be silent on the year, instead of bombing out. */
4545 res
= wait_file(chan
,ints
, "digits/19",lang
);
4547 if (tm
.tm_year
<= 9) {
4549 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4551 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4552 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4554 } else if (tm
.tm_year
<= 20) {
4556 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4557 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4561 ten
= tm
.tm_year
/ 10;
4562 one
= tm
.tm_year
% 10;
4563 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4564 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4567 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4568 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4579 if (tm
.tm_hour
== 0)
4580 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4581 else if (tm
.tm_hour
> 12)
4582 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4584 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4585 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4590 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4592 res
= wait_file(chan
,ints
, "digits/nl-uur",lang
);
4597 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4602 if (tm
.tm_hour
> 11)
4603 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4605 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4606 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4609 /* Shorthand for "Today", "Yesterday", or ABdY */
4610 /* XXX As emphasized elsewhere, this should the native way in your
4611 * language to say the date, with changes in what you say, depending
4612 * upon how recent the date is. XXX */
4616 time_t beg_today
, tt
;
4618 gettimeofday(&now
,NULL
);
4620 ast_localtime(&tt
,&tmnow
,timezone
);
4621 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4622 /* In any case, it saves not having to do ast_mktime() */
4623 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4624 if (beg_today
< time
) {
4626 res
= wait_file(chan
,ints
, "digits/today",lang
);
4627 } else if (beg_today
- 86400 < time
) {
4629 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4631 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4636 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4640 time_t beg_today
, tt
;
4642 gettimeofday(&now
,NULL
);
4644 ast_localtime(&tt
,&tmnow
,timezone
);
4645 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4646 /* In any case, it saves not having to do ast_mktime() */
4647 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4648 if (beg_today
< time
) {
4650 } else if ((beg_today
- 86400) < time
) {
4652 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4653 } else if (beg_today
- 86400 * 6 < time
) {
4654 /* Within the last week */
4655 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "A", timezone
);
4657 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4662 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HM", timezone
);
4666 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, (char *) NULL
);
4669 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HMS", timezone
);
4673 /* Just ignore spaces and tabs */
4676 /* Unknown character */
4677 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4679 /* Jump out on DTMF */
4688 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
)
4691 int res
=0, offset
, sndoffset
;
4692 char sndfile
[256], nextmsg
[256];
4694 ast_localtime(&thetime
, &tm
, timezone
);
4696 for (offset
= 0 ; format
[offset
] != '\0' ; offset
++) {
4698 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4699 switch (format
[offset
]) {
4700 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4702 /* Literal name of a sound file */
4704 for (sndoffset
= 0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4705 sndfile
[sndoffset
] = format
[offset
];
4706 sndfile
[sndoffset
] = '\0';
4707 res
= wait_file(chan
, ints
, sndfile
, lang
);
4711 /* Sunday - Saturday */
4712 snprintf(nextmsg
, sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4713 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4718 /* January - December */
4719 snprintf(nextmsg
, sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4720 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4723 /* Month enumerated */
4724 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, NULL
);
4728 /* First - Thirtyfirst */
4729 remainder
= tm
.tm_mday
;
4730 if (tm
.tm_mday
> 30) {
4731 res
= wait_file(chan
, ints
, "digits/h-30", lang
);
4734 if (tm
.tm_mday
> 20 && tm
.tm_mday
< 30) {
4735 res
= wait_file(chan
, ints
, "digits/h-20", lang
);
4739 snprintf(nextmsg
, sizeof(nextmsg
), "digits/h-%d", remainder
);
4740 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4745 if (tm
.tm_year
> 100) {
4746 res
= wait_file(chan
, ints
, "digits/2", lang
);
4748 res
= wait_file(chan
, ints
, "digits/1000.2",lang
);
4749 if (tm
.tm_year
> 100) {
4751 res
= ast_say_enumeration(chan
, tm
.tm_year
- 100, ints
, lang
, NULL
);
4753 } else if (tm
.tm_year
== 100) {
4754 res
= wait_file(chan
, ints
, "digits/h-2000", lang
);
4756 if (tm
.tm_year
< 1) {
4757 /* I'm not going to handle 1900 and prior */
4758 /* We'll just be silent on the year, instead of bombing out. */
4761 res
= wait_file(chan
, ints
, "digits/1000", lang
);
4763 wait_file(chan
, ints
, "digits/900", lang
);
4764 res
= ast_say_enumeration(chan
, tm
.tm_year
, ints
, lang
, NULL
);
4769 wait_file(chan
, ints
, "digits/year", lang
);
4774 if (tm
.tm_hour
== 0)
4775 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-12");
4776 else if (tm
.tm_hour
> 12)
4777 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
- 12);
4779 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4781 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4786 if (tm
.tm_hour
!= 0) {
4787 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4788 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4790 res
= wait_file(chan
, ints
, "digits/t-24", lang
);
4795 if (tm
.tm_min
== 0) {
4796 if (format
[offset
] == 'M') {
4797 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
4799 res
= wait_file(chan
, ints
, "digits/100", lang
);
4802 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4807 if (tm
.tm_hour
> 11)
4808 snprintf(nextmsg
, sizeof(nextmsg
), "digits/p-m");
4810 snprintf(nextmsg
, sizeof(nextmsg
), "digits/a-m");
4811 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4814 /* Shorthand for "Today", "Yesterday", or AdBY */
4816 time_t tv_sec
= time(NULL
);
4820 ast_localtime(&tv_sec
,&tmnow
, timezone
);
4821 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4822 /* In any case, it saves not having to do ast_mktime() */
4823 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4824 if (beg_today
< thetime
) {
4826 res
= wait_file(chan
, ints
, "digits/today", lang
);
4827 } else if (beg_today
- 86400 < thetime
) {
4829 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4831 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4836 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4838 time_t tv_sec
= time(NULL
);
4842 ast_localtime(&tv_sec
, &tmnow
, timezone
);
4843 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4844 /* In any case, it saves not having to do ast_mktime() */
4845 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4846 if (beg_today
< thetime
) {
4848 } else if ((beg_today
- 86400) < thetime
) {
4850 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4851 } else if (beg_today
- 86400 * 6 < thetime
) {
4852 /* Within the last week */
4853 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "A", timezone
);
4855 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4860 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HM", timezone
);
4864 res
= wait_file(chan
, ints
, "digits/and", lang
);
4866 if (tm
.tm_sec
== 1) {
4867 res
= wait_file(chan
, ints
, "digits/1z", lang
);
4869 res
= wait_file(chan
, ints
, "digits/second-a", lang
);
4871 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4874 ten
= tm
.tm_sec
/ 10;
4875 one
= tm
.tm_sec
% 10;
4877 if (one
> 1 && one
< 5 && ten
!= 1)
4878 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
4880 res
= wait_file(chan
,ints
, "digits/second",lang
);
4886 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HMS", timezone
);
4890 /* Just ignore spaces and tabs */
4893 /* Unknown character */
4894 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4896 /* Jump out on DTMF */
4903 /* Portuguese syntax */
4904 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
)
4907 int res
=0, offset
, sndoffset
;
4908 char sndfile
[256], nextmsg
[256];
4911 format
= "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
4913 ast_localtime(&time
,&tm
,timezone
);
4915 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4916 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4917 switch (format
[offset
]) {
4918 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4920 /* Literal name of a sound file */
4922 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4923 sndfile
[sndoffset
] = format
[offset
];
4924 sndfile
[sndoffset
] = '\0';
4925 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
4926 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4930 /* Sunday - Saturday */
4931 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4932 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4937 /* January - December */
4938 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4939 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4942 /* First - Twelfth */
4943 if (!strcasecmp(lang
, "pt_BR")) {
4944 res
= ast_say_number(chan
, tm
.tm_mon
+1, ints
, lang
, (char *) NULL
);
4946 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4947 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4952 /* First - Thirtyfirst */
4953 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4957 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
4962 if (!strcasecmp(lang
, "pt_BR")) {
4963 if (tm
.tm_hour
== 0) {
4964 if (format
[offset
] == 'I')
4965 res
= wait_file(chan
, ints
, "digits/pt-a", lang
);
4967 res
= wait_file(chan
, ints
, "digits/pt-meianoite", lang
);
4968 } else if (tm
.tm_hour
== 12) {
4969 if (format
[offset
] == 'I')
4970 res
= wait_file(chan
, ints
, "digits/pt-ao", lang
);
4972 res
= wait_file(chan
, ints
, "digits/pt-meiodia", lang
);
4974 if (format
[offset
] == 'I') {
4975 if ((tm
.tm_hour
% 12) != 1)
4976 res
= wait_file(chan
, ints
, "digits/pt-as", lang
);
4978 res
= wait_file(chan
, ints
, "digits/pt-a", lang
);
4981 res
= ast_say_number(chan
, (tm
.tm_hour
% 12), ints
, lang
, "f");
4984 if (tm
.tm_hour
== 0) {
4985 if (format
[offset
] == 'I')
4986 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
4988 res
= wait_file(chan
, ints
, "digits/pt-meianoite", lang
);
4990 else if (tm
.tm_hour
== 12) {
4991 if (format
[offset
] == 'I')
4992 res
= wait_file(chan
, ints
, "digits/pt-ao", lang
);
4994 res
= wait_file(chan
, ints
, "digits/pt-meiodia", lang
);
4997 if (format
[offset
] == 'I') {
4998 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
4999 if ((tm
.tm_hour
% 12) != 1)
5001 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5004 res
= ast_say_number(chan
, (tm
.tm_hour
% 12), ints
, lang
, "f");
5011 if (!strcasecmp(lang
, "pt_BR")) {
5012 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5013 if ((!res
) && (format
[offset
] == 'H')) {
5014 if (tm
.tm_hour
> 1) {
5015 res
= wait_file(chan
,ints
,"digits/hours",lang
);
5017 res
= wait_file(chan
,ints
,"digits/hour",lang
);
5021 res
= ast_say_number(chan
, -tm
.tm_hour
, ints
, lang
, NULL
);
5023 if (tm
.tm_hour
!= 0) {
5024 int remainder
= tm
.tm_hour
;
5025 if (tm
.tm_hour
> 20) {
5026 res
= wait_file(chan
,ints
, "digits/20",lang
);
5030 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
5031 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5039 if (!strcasecmp(lang
, "pt_BR")) {
5040 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, NULL
);
5042 if (tm
.tm_min
> 1) {
5043 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
5045 res
= wait_file(chan
,ints
,"digits/minute",lang
);
5049 if (tm
.tm_min
== 0) {
5050 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
5051 if (tm
.tm_hour
!= 1)
5053 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5055 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5062 if (!strcasecmp(lang
, "pt_BR")) {
5063 if ((tm
.tm_hour
!= 0) && (tm
.tm_hour
!= 12)) {
5064 res
= wait_file(chan
, ints
, "digits/pt-da", lang
);
5066 if ((tm
.tm_hour
>= 0) && (tm
.tm_hour
< 12))
5067 res
= wait_file(chan
, ints
, "digits/morning", lang
);
5068 else if ((tm
.tm_hour
>= 12) && (tm
.tm_hour
< 18))
5069 res
= wait_file(chan
, ints
, "digits/afternoon", lang
);
5070 else res
= wait_file(chan
, ints
, "digits/night", lang
);
5074 if (tm
.tm_hour
> 12)
5075 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
5076 else if (tm
.tm_hour
&& tm
.tm_hour
< 12)
5077 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
5081 /* Shorthand for "Today", "Yesterday", or ABdY */
5082 /* XXX As emphasized elsewhere, this should the native way in your
5083 * language to say the date, with changes in what you say, depending
5084 * upon how recent the date is. XXX */
5088 time_t beg_today
, tt
;
5090 gettimeofday(&now
,NULL
);
5092 ast_localtime(&tt
,&tmnow
,timezone
);
5093 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5094 /* In any case, it saves not having to do ast_mktime() */
5095 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5096 if (beg_today
< time
) {
5098 res
= wait_file(chan
,ints
, "digits/today",lang
);
5099 } else if (beg_today
- 86400 < time
) {
5101 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5103 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5108 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5109 /* XXX As emphasized elsewhere, this should the native way in your
5110 * language to say the date, with changes in what you say, depending
5111 * upon how recent the date is. XXX */
5115 time_t beg_today
, tt
;
5117 gettimeofday(&now
,NULL
);
5119 ast_localtime(&tt
,&tmnow
,timezone
);
5120 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5121 /* In any case, it saves not having to do ast_mktime() */
5122 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5123 if (beg_today
< time
) {
5125 } else if ((beg_today
- 86400) < time
) {
5127 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5128 } else if (beg_today
- 86400 * 6 < time
) {
5129 /* Within the last week */
5130 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "A", timezone
);
5132 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5137 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "H 'digits/pt-e' M", timezone
);
5141 if (!strcasecmp(lang
, "pt_BR")) {
5142 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, NULL
);
5144 if (tm
.tm_sec
> 1) {
5145 res
= wait_file(chan
,ints
,"digits/seconds",lang
);
5147 res
= wait_file(chan
,ints
,"digits/second",lang
);
5149 } else if (tm
.tm_sec
< 10) {
5150 res
= wait_file(chan
,ints
, "digits/oh",lang
);
5152 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5153 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5155 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
5156 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5157 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5160 ten
= (tm
.tm_sec
/ 10) * 10;
5161 one
= (tm
.tm_sec
% 10);
5162 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
5163 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5165 /* Fifty, not fifty-zero */
5167 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
5168 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5175 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "HMS", timezone
);
5179 /* Just ignore spaces and tabs */
5182 /* Unknown character */
5183 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5185 /* Jump out on DTMF */
5193 /* Taiwanese / Chinese syntax */
5194 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
)
5197 int res
=0, offset
, sndoffset
;
5198 char sndfile
[256], nextmsg
[256];
5203 ast_localtime(&time
,&tm
,timezone
);
5205 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
5206 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
5207 switch (format
[offset
]) {
5208 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5210 /* Literal name of a sound file */
5212 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
5213 sndfile
[sndoffset
] = format
[offset
];
5214 sndfile
[sndoffset
] = '\0';
5215 res
= wait_file(chan
,ints
,sndfile
,lang
);
5219 /* Sunday - Saturday */
5220 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
5221 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5227 /* January - December */
5228 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
5229 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5233 /* First - Thirtyfirst */
5234 if (!(tm
.tm_mday
% 10) || (tm
.tm_mday
< 10)) {
5235 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_mday
);
5236 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5238 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_mday
- (tm
.tm_mday
% 10));
5239 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5241 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_mday
% 10);
5242 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5245 if (!res
) res
= wait_file(chan
,ints
,"digits/day",lang
);
5249 if (tm
.tm_year
> 99) {
5250 res
= wait_file(chan
,ints
, "digits/2",lang
);
5252 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
5254 if (tm
.tm_year
> 100) {
5256 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) / 10);
5257 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5259 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) % 10);
5260 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5265 res
= wait_file(chan
,ints
, "digits/year",lang
);
5268 if (tm
.tm_year
< 1) {
5269 /* I'm not going to handle 1900 and prior */
5270 /* We'll just be silent on the year, instead of bombing out. */
5272 res
= wait_file(chan
,ints
, "digits/1",lang
);
5274 res
= wait_file(chan
,ints
, "digits/9",lang
);
5277 if (tm
.tm_year
<= 9) {
5279 res
= wait_file(chan
,ints
, "digits/0",lang
);
5281 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
5282 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5286 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
/ 10);
5287 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5289 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
% 10);
5290 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5296 res
= wait_file(chan
,ints
, "digits/year",lang
);
5303 if (tm
.tm_hour
== 0)
5304 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
5305 else if (tm
.tm_hour
> 12)
5306 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
5308 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5309 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5311 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5315 if (tm
.tm_hour
< 10) {
5316 res
= wait_file(chan
, ints
, "digits/0", lang
);
5320 if (!(tm
.tm_hour
% 10) || tm
.tm_hour
< 10) {
5321 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5322 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5324 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- (tm
.tm_hour
% 10));
5325 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5327 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
% 10);
5328 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5332 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5337 if (!(tm
.tm_min
% 10) || tm
.tm_min
< 10) {
5338 if (tm
.tm_min
< 10) {
5339 res
= wait_file(chan
, ints
, "digits/0", lang
);
5341 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
5342 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5344 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
- (tm
.tm_min
% 10));
5345 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5347 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
% 10);
5348 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5352 res
= wait_file(chan
,ints
, "digits/minute",lang
);
5358 if (tm
.tm_hour
> 11)
5359 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
5361 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
5362 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5365 /* Shorthand for "Today", "Yesterday", or ABdY */
5366 /* XXX As emphasized elsewhere, this should the native way in your
5367 * language to say the date, with changes in what you say, depending
5368 * upon how recent the date is. XXX */
5372 time_t beg_today
, tt
;
5374 gettimeofday(&now
,NULL
);
5376 ast_localtime(&tt
,&tmnow
,timezone
);
5377 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5378 /* In any case, it saves not having to do ast_mktime() */
5379 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5380 if (beg_today
< time
) {
5382 res
= wait_file(chan
,ints
, "digits/today",lang
);
5383 } else if (beg_today
- 86400 < time
) {
5385 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5387 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5392 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5393 /* XXX As emphasized elsewhere, this should the native way in your
5394 * language to say the date, with changes in what you say, depending
5395 * upon how recent the date is. XXX */
5399 time_t beg_today
, tt
;
5401 gettimeofday(&now
,NULL
);
5403 ast_localtime(&tt
,&tmnow
,timezone
);
5404 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5405 /* In any case, it saves not having to do ast_mktime() */
5406 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5407 if (beg_today
< time
) {
5409 } else if ((beg_today
- 86400) < time
) {
5411 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5412 } else if (beg_today
- 86400 * 6 < time
) {
5413 /* Within the last week */
5414 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "A", timezone
);
5416 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5421 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "kM", timezone
);
5425 if (!(tm
.tm_sec
% 10) || tm
.tm_sec
< 10) {
5426 if (tm
.tm_sec
< 10) {
5427 res
= wait_file(chan
, ints
, "digits/0", lang
);
5429 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5430 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5432 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
- (tm
.tm_sec
% 10));
5433 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5435 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
% 10);
5436 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5440 res
= wait_file(chan
,ints
, "digits/second",lang
);
5444 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "HMS", timezone
);
5448 /* Just ignore spaces and tabs */
5451 /* Unknown character */
5452 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5454 /* Jump out on DTMF */
5462 static int say_time(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5464 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5465 return(ast_say_time_en(chan
, t
, ints
, lang
));
5466 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5467 return(ast_say_time_de(chan
, t
, ints
, lang
));
5468 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5469 return(ast_say_time_fr(chan
, t
, ints
, lang
));
5470 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5471 return(ast_say_time_nl(chan
, t
, ints
, lang
));
5472 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5473 return(ast_say_time_pt(chan
, t
, ints
, lang
));
5474 } else if (!strcasecmp(lang
, "pt_BR") ) { /* Brazilian Portuguese syntax */
5475 return(ast_say_time_pt_BR(chan
, t
, ints
, lang
));
5476 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5477 return(ast_say_time_tw(chan
, t
, ints
, lang
));
5478 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5479 return(ast_say_time_gr(chan
, t
, ints
, lang
));
5480 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
5481 return(ast_say_time_ge(chan
, t
, ints
, lang
));
5484 /* Default to English */
5485 return(ast_say_time_en(chan
, t
, ints
, lang
));
5488 /* English syntax */
5489 int ast_say_time_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5495 ast_localtime(&t
, &tm
, NULL
);
5499 else if (hour
== 12)
5501 else if (hour
> 12) {
5506 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5508 if (tm
.tm_min
> 9) {
5510 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5511 } else if (tm
.tm_min
) {
5513 res
= ast_streamfile(chan
, "digits/oh", lang
);
5515 res
= ast_waitstream(chan
, ints
);
5517 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5520 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5522 res
= ast_waitstream(chan
, ints
);
5526 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5529 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5532 res
= ast_waitstream(chan
, ints
);
5537 int ast_say_time_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5542 ast_localtime(&t
, &tm
, NULL
);
5544 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "n");
5546 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5548 res
= ast_waitstream(chan
, ints
);
5551 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
5556 int ast_say_time_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5561 ast_localtime(&t
, &tm
, NULL
);
5563 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5565 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5568 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5574 int ast_say_time_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5579 ast_localtime(&t
, &tm
, NULL
);
5581 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
5583 res
= ast_streamfile(chan
, "digits/nl-uur", lang
);
5585 res
= ast_waitstream(chan
, ints
);
5588 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, NULL
);
5592 /* Portuguese syntax */
5593 int ast_say_time_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5599 ast_localtime(&t
, &tm
, NULL
);
5602 res
= ast_say_number(chan
, hour
, ints
, lang
, "f");
5605 res
= wait_file(chan
, ints
, "digits/pt-e", lang
);
5607 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5610 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
5611 if (tm
.tm_hour
!= 1)
5613 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5616 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5620 /* Brazilian Portuguese syntax */
5621 int ast_say_time_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5626 ast_localtime(&t
, &tm
, NULL
);
5628 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5631 res
= wait_file(chan
, ints
, "digits/hours", lang
);
5633 res
= wait_file(chan
, ints
, "digits/hour", lang
);
5635 if ((!res
) && (tm
.tm_min
)) {
5636 res
= wait_file(chan
, ints
, "digits/pt-e", lang
);
5638 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5641 res
= wait_file(chan
, ints
, "digits/minutes", lang
);
5643 res
= wait_file(chan
, ints
, "digits/minute", lang
);
5649 /* Taiwanese / Chinese syntax */
5650 int ast_say_time_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5656 ast_localtime(&t
, &tm
, NULL
);
5660 else if (hour
== 12)
5662 else if (hour
> 12) {
5668 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5671 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5674 res
= ast_waitstream(chan
, ints
);
5676 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5678 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5680 res
= ast_waitstream(chan
, ints
);
5682 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5684 res
= ast_streamfile(chan
, "digits/minute", lang
);
5686 res
= ast_waitstream(chan
, ints
);
5690 static int say_datetime(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5692 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5693 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5694 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5695 return(ast_say_datetime_de(chan
, t
, ints
, lang
));
5696 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5697 return(ast_say_datetime_fr(chan
, t
, ints
, lang
));
5698 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5699 return(ast_say_datetime_nl(chan
, t
, ints
, lang
));
5700 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5701 return(ast_say_datetime_pt(chan
, t
, ints
, lang
));
5702 } else if (!strcasecmp(lang
, "pt_BR") ) { /* Brazilian Portuguese syntax */
5703 return(ast_say_datetime_pt_BR(chan
, t
, ints
, lang
));
5704 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5705 return(ast_say_datetime_tw(chan
, t
, ints
, lang
));
5706 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5707 return(ast_say_datetime_gr(chan
, t
, ints
, lang
));
5708 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
5709 return(ast_say_datetime_ge(chan
, t
, ints
, lang
));
5712 /* Default to English */
5713 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5716 /* English syntax */
5717 int ast_say_datetime_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5724 ast_localtime(&t
, &tm
, NULL
);
5726 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5727 res
= ast_streamfile(chan
, fn
, lang
);
5729 res
= ast_waitstream(chan
, ints
);
5732 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5733 res
= ast_streamfile(chan
, fn
, lang
);
5735 res
= ast_waitstream(chan
, ints
);
5738 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5743 else if (hour
== 12)
5745 else if (hour
> 12) {
5750 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5752 if (tm
.tm_min
> 9) {
5754 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5755 } else if (tm
.tm_min
) {
5757 res
= ast_streamfile(chan
, "digits/oh", lang
);
5759 res
= ast_waitstream(chan
, ints
);
5761 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5764 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5766 res
= ast_waitstream(chan
, ints
);
5770 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5773 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5776 res
= ast_waitstream(chan
, ints
);
5778 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5783 int ast_say_datetime_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5788 ast_localtime(&t
, &tm
, NULL
);
5789 res
= ast_say_date(chan
, t
, ints
, lang
);
5791 ast_say_time(chan
, t
, ints
, lang
);
5797 int ast_say_datetime_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5803 ast_localtime(&t
, &tm
, NULL
);
5806 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5809 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5810 res
= ast_streamfile(chan
, fn
, lang
);
5812 res
= ast_waitstream(chan
, ints
);
5815 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5816 res
= ast_streamfile(chan
, fn
, lang
);
5818 res
= ast_waitstream(chan
, ints
);
5822 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5824 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5825 if (tm
.tm_min
> 0) {
5827 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5830 res
= ast_waitstream(chan
, ints
);
5832 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5837 int ast_say_datetime_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5842 ast_localtime(&t
, &tm
, NULL
);
5843 res
= ast_say_date(chan
, t
, ints
, lang
);
5845 res
= ast_streamfile(chan
, "digits/nl-om", lang
);
5847 res
= ast_waitstream(chan
, ints
);
5850 ast_say_time(chan
, t
, ints
, lang
);
5854 /* Portuguese syntax */
5855 int ast_say_datetime_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5862 ast_localtime(&t
, &tm
, NULL
);
5864 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5865 res
= ast_streamfile(chan
, fn
, lang
);
5867 res
= ast_waitstream(chan
, ints
);
5870 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5871 res
= ast_streamfile(chan
, fn
, lang
);
5873 res
= ast_waitstream(chan
, ints
);
5876 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5881 else if (hour
== 12)
5883 else if (hour
> 12) {
5888 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5890 if (tm
.tm_min
> 9) {
5892 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5893 } else if (tm
.tm_min
) {
5895 res
= ast_streamfile(chan
, "digits/oh", lang
);
5897 res
= ast_waitstream(chan
, ints
);
5899 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5902 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5904 res
= ast_waitstream(chan
, ints
);
5908 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5911 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5914 res
= ast_waitstream(chan
, ints
);
5916 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5920 /* Brazilian Portuguese syntax */
5921 int ast_say_datetime_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5926 ast_localtime(&t
, &tm
, NULL
);
5927 res
= ast_say_date(chan
, t
, ints
, lang
);
5929 res
= ast_say_time(chan
, t
, ints
, lang
);
5933 /* Taiwanese / Chinese syntax */
5934 int ast_say_datetime_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5941 ast_localtime(&t
, &tm
, NULL
);
5943 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5945 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5946 res
= ast_streamfile(chan
, fn
, lang
);
5948 res
= ast_waitstream(chan
, ints
);
5951 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5953 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5954 res
= ast_streamfile(chan
, fn
, lang
);
5956 res
= ast_waitstream(chan
, ints
);
5962 else if (hour
== 12)
5964 else if (hour
> 12) {
5970 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5973 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5976 res
= ast_waitstream(chan
, ints
);
5978 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5980 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5982 res
= ast_waitstream(chan
, ints
);
5984 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5986 res
= ast_streamfile(chan
, "digits/minute", lang
);
5988 res
= ast_waitstream(chan
, ints
);
5992 static int say_datetime_from_now(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5994 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5995 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
5996 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5997 return(ast_say_datetime_from_now_fr(chan
, t
, ints
, lang
));
5998 } else if (!strcasecmp(lang
, "pt") || !strcasecmp(lang
, "pt_BR")) { /* Portuguese syntax */
5999 return(ast_say_datetime_from_now_pt(chan
, t
, ints
, lang
));
6000 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
6001 return(ast_say_datetime_from_now_ge(chan
, t
, ints
, lang
));
6004 /* Default to English */
6005 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
6008 /* English syntax */
6009 int ast_say_datetime_from_now_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6020 ast_localtime(&t
, &tm
, NULL
);
6021 ast_localtime(&nowt
,&now
, NULL
);
6022 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6023 if ((daydiff
< 0) || (daydiff
> 6)) {
6024 /* Day of month and month */
6026 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6027 res
= ast_streamfile(chan
, fn
, lang
);
6029 res
= ast_waitstream(chan
, ints
);
6032 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6034 } else if (daydiff
) {
6035 /* Just what day of the week */
6037 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6038 res
= ast_streamfile(chan
, fn
, lang
);
6040 res
= ast_waitstream(chan
, ints
);
6042 } /* Otherwise, it was today */
6044 res
= ast_say_time(chan
, t
, ints
, lang
);
6049 int ast_say_datetime_from_now_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6060 ast_localtime(&t
, &tm
, NULL
);
6061 ast_localtime(&nowt
, &now
, NULL
);
6062 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6063 if ((daydiff
< 0) || (daydiff
> 6)) {
6064 /* Day of month and month */
6066 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6067 res
= ast_streamfile(chan
, fn
, lang
);
6069 res
= ast_waitstream(chan
, ints
);
6072 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6074 } else if (daydiff
) {
6075 /* Just what day of the week */
6077 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6078 res
= ast_streamfile(chan
, fn
, lang
);
6080 res
= ast_waitstream(chan
, ints
);
6082 } /* Otherwise, it was today */
6084 res
= ast_say_time(chan
, t
, ints
, lang
);
6088 /* Portuguese syntax */
6089 int ast_say_datetime_from_now_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6100 ast_localtime(&t
, &tm
, NULL
);
6101 ast_localtime(&nowt
, &now
, NULL
);
6102 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6103 if ((daydiff
< 0) || (daydiff
> 6)) {
6104 /* Day of month and month */
6106 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6108 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
6109 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6111 res
= wait_file(chan
, ints
, fn
, lang
);
6113 } else if (daydiff
) {
6114 /* Just what day of the week */
6115 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6117 res
= wait_file(chan
, ints
, fn
, lang
);
6118 } /* Otherwise, it was today */
6119 if (!strcasecmp(lang
, "pt_BR")) {
6120 if (tm
.tm_hour
> 1) {
6121 snprintf(fn
, sizeof(fn
), "digits/pt-as");
6123 snprintf(fn
, sizeof(fn
), "digits/pt-a");
6126 res
= wait_file(chan
, ints
, fn
, lang
);
6128 snprintf(fn
, sizeof(fn
), "digits/pt-ah");
6130 res
= wait_file(chan
, ints
, fn
, lang
);
6131 if (tm
.tm_hour
!= 1)
6133 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
6135 res
= ast_say_time(chan
, t
, ints
, lang
);
6141 /*********************************** GREEK SUPPORT ***************************************/
6145 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
6147 static int gr_say_number_female(int num
, struct ast_channel
*chan
, const char *ints
, const char *lang
){
6153 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
6155 snprintf(fn
, sizeof(fn
), "digits/female-%d", num
);
6156 res
= wait_file(chan
, ints
, fn
, lang
);
6157 } else if (num
< 13) {
6158 res
= ast_say_number(chan
, num
, ints
, lang
, (char *) NULL
);
6159 } else if (num
<100 ) {
6160 tmp
= (num
/10) * 10;
6162 snprintf(fn
, sizeof(fn
), "digits/%d", tmp
);
6163 res
= ast_streamfile(chan
, fn
, lang
);
6165 res
= ast_waitstream(chan
, ints
);
6167 gr_say_number_female(left
, chan
, ints
, lang
);
6178 * A list of the files that you need to create
6179 -> digits/xilia = "xilia"
6180 -> digits/myrio = "ekatomyrio"
6181 -> digits/thousands = "xiliades"
6182 -> digits/millions = "ektatomyria"
6183 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6184 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6186 Here we must note that we use digits/tens/100 to utter "ekato"
6187 and digits/hundred-100 to utter "ekaton"
6188 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6189 "terakosia". Here again we use hundreds/1000 for "xilia"
6190 and digits/thousnds for "xiliades"
6193 static int ast_say_number_full_gr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
,int audiofd
, int ctrlfd
)
6201 snprintf(fn
, sizeof(fn
), "digits/0");
6202 res
= ast_streamfile(chan
, fn
, chan
->language
);
6204 return ast_waitstream(chan
, ints
);
6207 while (!res
&& num
) {
6210 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
6212 } else if (num
<= 100) {
6213 /* 13 < num <= 100 */
6214 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
6215 num
-= ((num
/ 10) * 10);
6216 } else if (num
< 200) {
6217 /* 100 < num < 200 */
6218 snprintf(fn
, sizeof(fn
), "digits/hundred-100");
6219 num
-= ((num
/ 100) * 100);
6220 } else if (num
< 1000) {
6221 /* 200 < num < 1000 */
6222 snprintf(fn
, sizeof(fn
), "digits/hundred-%d", (num
/100)*100);
6223 num
-= ((num
/ 100) * 100);
6224 } else if (num
< 2000){
6225 snprintf(fn
, sizeof(fn
), "digits/xilia");
6226 num
-= ((num
/ 1000) * 1000);
6229 if (num
< 1000000) {
6230 res
= ast_say_number_full_gr(chan
, (num
/ 1000), ints
, chan
->language
, audiofd
, ctrlfd
);
6234 snprintf(fn
, sizeof(fn
), "digits/thousands");
6236 if (num
< 1000000000) { /* 1,000,000,000 */
6237 res
= ast_say_number_full_gr(chan
, (num
/ 1000000), ints
, chan
->language
,audiofd
, ctrlfd
);
6240 num
= num
% 1000000;
6241 snprintf(fn
, sizeof(fn
), "digits/millions");
6243 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
6249 if (!ast_streamfile(chan
, fn
, language
)) {
6250 if ((audiofd
> -1) && (ctrlfd
> -1))
6251 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
6253 res
= ast_waitstream(chan
, ints
);
6255 ast_stopstream(chan
);
6263 * The format is weekday - day - month -year
6265 * A list of the files that you need to create
6266 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6267 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6268 Attention the months are in
6273 static int ast_say_date_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6281 ast_localtime(&t
,&tm
,NULL
);
6282 /* W E E K - D A Y */
6284 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6285 res
= ast_streamfile(chan
, fn
, lang
);
6287 res
= ast_waitstream(chan
, ints
);
6291 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6295 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6296 res
= ast_streamfile(chan
, fn
, lang
);
6298 res
= ast_waitstream(chan
, ints
);
6302 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
6308 /* A list of the files that you need to create
6309 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6310 * digits/kai : "KAI"
6312 * digits/p-m : "meta meshmbrias"
6313 * digits/a-m : "pro meshmbrias"
6316 static int ast_say_time_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6323 ast_localtime(&t
, &tm
, NULL
);
6328 else if (hour
== 12)
6330 else if (hour
> 12) {
6335 res
= gr_say_number_female(hour
, chan
, ints
, lang
);
6338 res
= ast_streamfile(chan
, "digits/kai", lang
);
6340 res
= ast_waitstream(chan
, ints
);
6342 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
6345 res
= ast_streamfile(chan
, "digits/hwra", lang
);
6347 res
= ast_waitstream(chan
, ints
);
6351 res
= ast_streamfile(chan
, "digits/p-m", lang
);
6354 res
= ast_streamfile(chan
, "digits/a-m", lang
);
6357 res
= ast_waitstream(chan
, ints
);
6363 static int ast_say_datetime_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6369 ast_localtime(&t
, &tm
, NULL
);
6372 /* W E E K - D A Y */
6374 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6375 res
= ast_streamfile(chan
, fn
, lang
);
6377 res
= ast_waitstream(chan
, ints
);
6381 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6385 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6386 res
= ast_streamfile(chan
, fn
, lang
);
6388 res
= ast_waitstream(chan
, ints
);
6391 res
= ast_say_time_gr(chan
, t
, ints
, lang
);
6395 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
)
6399 int res
=0, offset
, sndoffset
;
6400 char sndfile
[256], nextmsg
[256];
6403 format
= "AdBY 'digits/at' IMp";
6405 ast_localtime(&time
,&tm
,timezone
);
6407 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
6408 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
6409 switch (format
[offset
]) {
6410 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6412 /* Literal name of a sound file */
6414 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
6415 sndfile
[sndoffset
] = format
[offset
];
6416 sndfile
[sndoffset
] = '\0';
6417 res
= wait_file(chan
,ints
,sndfile
,lang
);
6421 /* Sunday - Saturday */
6422 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
6423 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6428 /* January - December */
6429 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
6430 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6434 /* first - thirtyfirst */
6435 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6440 ast_say_number_full_gr(chan
, 1900+tm
.tm_year
, ints
, chan
->language
, -1, -1);
6445 if (tm
.tm_hour
== 0)
6446 gr_say_number_female(12, chan
, ints
, lang
);
6447 else if (tm
.tm_hour
> 12)
6448 gr_say_number_female(tm
.tm_hour
- 12, chan
, ints
, lang
);
6450 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6455 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6461 res
= ast_streamfile(chan
, "digits/kai", lang
);
6463 res
= ast_waitstream(chan
, ints
);
6465 res
= ast_say_number_full_gr(chan
, tm
.tm_min
, ints
, lang
, -1, -1);
6468 res
= ast_streamfile(chan
, "digits/oclock", lang
);
6470 res
= ast_waitstream(chan
, ints
);
6476 if (tm
.tm_hour
> 11)
6477 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
6479 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
6480 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6483 /* Shorthand for "Today", "Yesterday", or ABdY */
6484 /* XXX As emphasized elsewhere, this should the native way in your
6485 * language to say the date, with changes in what you say, depending
6486 * upon how recent the date is. XXX */
6490 time_t beg_today
, tt
;
6492 gettimeofday(&now
,NULL
);
6494 ast_localtime(&tt
,&tmnow
,timezone
);
6495 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6496 /* In any case, it saves not having to do ast_mktime() */
6497 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6498 if (beg_today
< time
) {
6500 res
= wait_file(chan
,ints
, "digits/today",lang
);
6501 } else if (beg_today
- 86400 < time
) {
6503 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6505 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6510 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6511 /* XXX As emphasized elsewhere, this should the native way in your
6512 * language to say the date, with changes in what you say, depending
6513 * upon how recent the date is. XXX */
6517 time_t beg_today
, tt
;
6519 gettimeofday(&now
,NULL
);
6521 ast_localtime(&tt
,&tmnow
,timezone
);
6522 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6523 /* In any case, it saves not having to do ast_mktime() */
6524 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6525 if (beg_today
< time
) {
6527 } else if ((beg_today
- 86400) < time
) {
6529 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6530 } else if (beg_today
- 86400 * 6 < time
) {
6531 /* Within the last week */
6532 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "A", timezone
);
6534 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6539 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HM", timezone
);
6543 snprintf(nextmsg
,sizeof(nextmsg
), "digits/kai");
6544 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6546 res
= ast_say_number_full_gr(chan
, tm
.tm_sec
, ints
, lang
, -1, -1);
6548 snprintf(nextmsg
,sizeof(nextmsg
), "digits/seconds");
6549 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6552 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HMS", timezone
);
6556 /* Just ignore spaces and tabs */
6559 /* Unknown character */
6560 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
6562 /* Jump out on DTMF */
6573 /*********************************** Georgian Support ***************************************/
6577 Convert a number into a semi-localized string. Only for Georgian.
6578 res must be of at least 256 bytes, preallocated.
6579 The output corresponds to Georgian spoken numbers, so
6580 it may be either converted to real words by applying a direct conversion
6581 table, or played just by substituting the entities with played files.
6583 Output may consist of the following tokens (separated by spaces):
6585 1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
6587 20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
6588 100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
6589 1000, 1000_. (atasi, atas).
6590 1000000, 1000000_. (milioni, milion).
6591 1000000000, 1000000000_. (miliardi, miliard).
6593 To be able to play the sounds, each of the above tokens needs
6594 a corresponding sound file. (e.g. 200_.gsm).
6596 static char* ast_translate_number_ge(int num
, char* res
, int res_len
)
6604 strncat(res
, "minus ", res_len
- strlen(res
) - 1);
6605 if ( num
> INT_MIN
) {
6613 /* directly read the numbers */
6614 if (num
<= 20 || num
== 40 || num
== 60 || num
== 80 || num
== 100) {
6615 snprintf(buf
, sizeof(buf
), "%d", num
);
6616 strncat(res
, buf
, res_len
- strlen(res
) - 1);
6621 if (num
< 40) { /* ocda... */
6622 strncat(res
, "20_ ", res_len
- strlen(res
) - 1);
6623 return ast_translate_number_ge(num
- 20, res
, res_len
);
6626 if (num
< 60) { /* ormocda... */
6627 strncat(res
, "40_ ", res_len
- strlen(res
) - 1);
6628 return ast_translate_number_ge(num
- 40, res
, res_len
);
6631 if (num
< 80) { /* samocda... */
6632 strncat(res
, "60_ ", res_len
- strlen(res
) - 1);
6633 return ast_translate_number_ge(num
- 60, res
, res_len
);
6636 if (num
< 100) { /* otxmocda... */
6637 strncat(res
, "80_ ", res_len
- strlen(res
) - 1);
6638 return ast_translate_number_ge(num
- 80, res
, res_len
);
6642 if (num
< 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
6643 remainder
= num
% 100;
6644 digit
= (num
- remainder
) / 100;
6646 if (remainder
== 0) {
6647 snprintf(buf
, sizeof(buf
), "%d", num
);
6648 strncat(res
, buf
, res_len
- strlen(res
) - 1);
6651 snprintf(buf
, sizeof(buf
), "%d_ ", digit
*100);
6652 strncat(res
, buf
, res_len
- strlen(res
) - 1);
6653 return ast_translate_number_ge(remainder
, res
, res_len
);
6659 strncat(res
, "1000", res_len
- strlen(res
) - 1);
6664 if (num
< 1000000) {
6665 remainder
= num
% 1000;
6666 digit
= (num
- remainder
) / 1000;
6668 if (remainder
== 0) {
6669 ast_translate_number_ge(digit
, res
, res_len
);
6670 strncat(res
, " 1000", res_len
- strlen(res
) - 1);
6675 strncat(res
, "1000_ ", res_len
- strlen(res
) - 1);
6676 return ast_translate_number_ge(remainder
, res
, res_len
);
6679 ast_translate_number_ge(digit
, res
, res_len
);
6680 strncat(res
, " 1000_ ", res_len
- strlen(res
) - 1);
6681 return ast_translate_number_ge(remainder
, res
, res_len
);
6686 if (num
== 1000000) {
6687 strncat(res
, "1 1000000", res_len
- strlen(res
) - 1);
6692 if (num
< 1000000000) {
6693 remainder
= num
% 1000000;
6694 digit
= (num
- remainder
) / 1000000;
6696 if (remainder
== 0) {
6697 ast_translate_number_ge(digit
, res
, res_len
);
6698 strncat(res
, " 1000000", res_len
- strlen(res
) - 1);
6702 ast_translate_number_ge(digit
, res
, res_len
);
6703 strncat(res
, " 1000000_ ", res_len
- strlen(res
) - 1);
6704 return ast_translate_number_ge(remainder
, res
, res_len
);
6709 if (num
== 1000000000) {
6710 strncat(res
, "1 1000000000", res_len
- strlen(res
) - 1);
6715 if (num
> 1000000000) {
6716 remainder
= num
% 1000000000;
6717 digit
= (num
- remainder
) / 1000000000;
6719 if (remainder
== 0) {
6720 ast_translate_number_ge(digit
, res
, res_len
);
6721 strncat(res
, " 1000000000", res_len
- strlen(res
) - 1);
6725 ast_translate_number_ge(digit
, res
, res_len
);
6726 strncat(res
, " 1000000000_ ", res_len
- strlen(res
) - 1);
6727 return ast_translate_number_ge(remainder
, res
, res_len
);
6737 /*! \brief ast_say_number_full_ge: Georgian syntax */
6738 static int ast_say_number_full_ge(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
6743 const char* remainder
= fn
;
6746 return ast_say_digits_full(chan
, 0, ints
, language
, audiofd
, ctrlfd
);
6749 ast_translate_number_ge(num
, fn
, 512);
6753 while (res
== 0 && (s
= strstr(remainder
, " "))) {
6754 size_t len
= s
- remainder
;
6755 char* new_string
= malloc(len
+ 1 + strlen("digits/"));
6757 sprintf(new_string
, "digits/");
6758 strncat(new_string
, remainder
, len
); /* we can't sprintf() it, it's not null-terminated. */
6759 /* new_string[len + strlen("digits/")] = '\0'; */
6761 if (!ast_streamfile(chan
, new_string
, language
)) {
6762 if ((audiofd
> -1) && (ctrlfd
> -1))
6763 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
6765 res
= ast_waitstream(chan
, ints
);
6767 ast_stopstream(chan
);
6771 remainder
= s
+ 1; /* position just after the found space char. */
6772 while (*remainder
== ' ') /* skip multiple spaces */
6777 /* the last chunk. */
6778 if (res
== 0 && *remainder
) {
6780 char* new_string
= malloc(strlen(remainder
) + 1 + strlen("digits/"));
6781 sprintf(new_string
, "digits/%s", remainder
);
6783 if (!ast_streamfile(chan
, new_string
, language
)) {
6784 if ((audiofd
> -1) && (ctrlfd
> -1))
6785 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
6787 res
= ast_waitstream(chan
, ints
);
6789 ast_stopstream(chan
);
6803 Georgian support for date/time requires the following files (*.gsm):
6805 mon-1, mon-2, ... (ianvari, tebervali, ...)
6806 day-1, day-2, ... (orshabati, samshabati, ...)
6814 /* Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi". */
6815 static int ast_say_date_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6820 ast_localtime(&t
,&tm
,NULL
);
6823 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
6826 snprintf(fn
, sizeof(fn
), "digits/tslis %d", tm
.tm_wday
);
6827 res
= ast_streamfile(chan
, fn
, lang
);
6829 res
= ast_waitstream(chan
, ints
);
6833 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
6835 res = ast_waitstream(chan, ints);
6840 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6841 res
= ast_streamfile(chan
, fn
, lang
);
6843 res
= ast_waitstream(chan
, ints
);
6853 /* Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
6854 static int ast_say_time_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6859 ast_localtime(&t
, &tm
, NULL
);
6861 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char*)NULL
);
6863 res
= ast_streamfile(chan
, "digits/saati_da", lang
);
6865 res
= ast_waitstream(chan
, ints
);
6870 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char*)NULL
);
6873 res
= ast_streamfile(chan
, "digits/tsuti", lang
);
6875 res
= ast_waitstream(chan
, ints
);
6884 /* Georgian syntax. Say date, then say time. */
6885 static int ast_say_datetime_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6890 ast_localtime(&t
, &tm
, NULL
);
6891 res
= ast_say_date(chan
, t
, ints
, lang
);
6893 ast_say_time(chan
, t
, ints
, lang
);
6901 /* Georgian syntax */
6902 static int ast_say_datetime_from_now_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6913 ast_localtime(&t
, &tm
, NULL
);
6914 ast_localtime(&nowt
, &now
, NULL
);
6915 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6916 if ((daydiff
< 0) || (daydiff
> 6)) {
6917 /* Day of month and month */
6919 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6921 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6922 res
= ast_streamfile(chan
, fn
, lang
);
6924 res
= ast_waitstream(chan
, ints
);
6927 } else if (daydiff
) {
6928 /* Just what day of the week */
6930 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6931 res
= ast_streamfile(chan
, fn
, lang
);
6933 res
= ast_waitstream(chan
, ints
);
6935 } /* Otherwise, it was today */
6937 res
= ast_say_time(chan
, t
, ints
, lang
);
6945 * remap the 'say' functions to use those in this file
6947 static void __attribute__((constructor
)) __say_init(void)
6949 ast_say_number_full
= say_number_full
;
6950 ast_say_enumeration_full
= say_enumeration_full
;
6951 ast_say_digit_str_full
= say_digit_str_full
;
6952 ast_say_character_str_full
= say_character_str_full
;
6953 ast_say_phonetic_str_full
= say_phonetic_str_full
;
6954 ast_say_datetime
= say_datetime
;
6955 ast_say_time
= say_time
;
6956 ast_say_date
= say_date
;
6957 ast_say_datetime_from_now
= say_datetime_from_now
;
6958 ast_say_date_with_format
= say_date_with_format
;