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 res
= ast_streamfile(chan
, fn
, lang
);
129 if ((audiofd
> -1) && (ctrlfd
> -1))
130 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
132 res
= ast_waitstream(chan
, ints
);
134 ast_stopstream(chan
);
141 static int say_phonetic_str_full(struct ast_channel
*chan
, const char *str
, const char *ints
, const char *lang
, int audiofd
, int ctrlfd
)
149 while (str
[num
] && !res
) {
159 fn
= "letters/exclaimation-point";
165 fn
= "letters/dollar";
174 fn
= "letters/equals";
180 fn
= "letters/slash";
183 fn
= "letters/space";
194 strcpy(fnbuf
, "digits/X");
198 default: /* '9' falls here... */
200 if ('A' <= ltr
&& ltr
<= 'Z') ltr
+= 'a' - 'A'; /* file names are all lower-case */
201 strcpy(fnbuf
, "phonetic/X_p");
205 res
= ast_streamfile(chan
, fn
, lang
);
207 if ((audiofd
> -1) && (ctrlfd
> -1))
208 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
210 res
= ast_waitstream(chan
, ints
);
212 ast_stopstream(chan
);
219 static int say_digit_str_full(struct ast_channel
*chan
, const char *str
, const char *ints
, const char *lang
, int audiofd
, int ctrlfd
)
226 while (str
[num
] && !res
) {
248 strcpy(fnbuf
, "digits/X");
254 res
= ast_streamfile(chan
, fn
, lang
);
256 if ((audiofd
> -1) && (ctrlfd
> -1))
257 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
259 res
= ast_waitstream(chan
, ints
);
261 ast_stopstream(chan
);
269 /* Forward declarations */
270 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
271 \note Not really language codes.
272 For these language codes, Asterisk will change the syntax when
273 saying numbers (and in some cases dates and voicemail messages
277 \arg \b en - English (US)
278 \arg \b en_GB - English (British)
279 \arg \b es - Spanish, Mexican
284 \arg \b no - Norwegian
286 \arg \b pt - Portuguese
287 \arg \b pt_BR - Portuguese (Brazil)
289 \arg \b tw - Taiwanese / Chinese
291 \arg \b ge - Georgian
294 For Some languages the numbers differ for gender and plural.
295 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
296 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
297 use the option argument 'p' for plural enumerations like in German
299 Date/Time functions currently have less languages supported than saynumber().
301 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
303 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
306 Portuguese sound files needed for Time/Date functions:
317 Spanish sound files needed for Time/Date functions:
322 Italian sound files needed for Time/Date functions:
328 /* Forward declarations of language specific variants of ast_say_number_full */
329 static int ast_say_number_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
330 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
);
331 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
);
332 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
);
333 static int ast_say_number_full_en_GB(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
334 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
);
335 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
);
336 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
);
337 static int ast_say_number_full_it(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
338 static int ast_say_number_full_nl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
339 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
);
340 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
);
341 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
);
342 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
);
343 static int ast_say_number_full_tw(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
344 static int ast_say_number_full_gr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
345 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
);
346 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
);
348 /* Forward declarations of language specific variants of ast_say_enumeration_full */
349 static int ast_say_enumeration_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
350 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
);
351 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
);
353 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
354 static int ast_say_date_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
355 static int ast_say_date_da(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
356 static int ast_say_date_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
357 static int ast_say_date_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
358 static int ast_say_date_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
359 static int ast_say_date_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
360 static int ast_say_date_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
361 static int ast_say_date_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
363 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
);
364 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
);
365 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
);
366 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
);
367 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
);
368 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
);
369 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
);
370 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
);
371 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
);
372 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
);
373 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
);
374 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
);
376 static int ast_say_time_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
377 static int ast_say_time_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
378 static int ast_say_time_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
379 static int ast_say_time_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
380 static int ast_say_time_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
381 static int ast_say_time_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
382 static int ast_say_time_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
383 static int ast_say_time_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
384 static int ast_say_time_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
386 static int ast_say_datetime_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
387 static int ast_say_datetime_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
388 static int ast_say_datetime_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
389 static int ast_say_datetime_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
390 static int ast_say_datetime_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
391 static int ast_say_datetime_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
392 static int ast_say_datetime_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
393 static int ast_say_datetime_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
394 static int ast_say_datetime_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
396 static int ast_say_datetime_from_now_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
397 static int ast_say_datetime_from_now_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
398 static int ast_say_datetime_from_now_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
399 static int ast_say_datetime_from_now_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
401 static int wait_file(struct ast_channel
*chan
, const char *ints
, const char *file
, const char *lang
)
404 if ((res
= ast_streamfile(chan
, file
, lang
)))
405 ast_log(LOG_WARNING
, "Unable to play message %s\n", file
);
407 res
= ast_waitstream(chan
, ints
);
411 /*! \brief ast_say_number_full: call language-specific functions */
412 /* Called from AGI */
413 static int say_number_full(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
415 if (!strcasecmp(language
,"en") ) { /* English syntax */
416 return(ast_say_number_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
417 } else if (!strcasecmp(language
, "cz") ) { /* Czech syntax */
418 return(ast_say_number_full_cz(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
419 } else if (!strcasecmp(language
, "da") ) { /* Danish syntax */
420 return(ast_say_number_full_da(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
421 } else if (!strcasecmp(language
, "de") ) { /* German syntax */
422 return(ast_say_number_full_de(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
423 } else if (!strcasecmp(language
, "en_GB") ) { /* British syntax */
424 return(ast_say_number_full_en_GB(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
425 } else if (!strcasecmp(language
, "no") ) { /* Norwegian syntax */
426 return(ast_say_number_full_no(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
427 } else if (!strcasecmp(language
, "es") || !strcasecmp(language
, "mx")) { /* Spanish syntax */
428 return(ast_say_number_full_es(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
429 } else if (!strcasecmp(language
, "fr") ) { /* French syntax */
430 return(ast_say_number_full_fr(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
431 } else if (!strcasecmp(language
, "he") ) { /* Hebrew syntax */
432 return(ast_say_number_full_he(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
433 } else if (!strcasecmp(language
, "it") ) { /* Italian syntax */
434 return(ast_say_number_full_it(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
435 } else if (!strcasecmp(language
, "nl") ) { /* Dutch syntax */
436 return(ast_say_number_full_nl(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
437 } else if (!strcasecmp(language
, "pl") ) { /* Polish syntax */
438 return(ast_say_number_full_pl(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
439 } else if (!strcasecmp(language
, "pt") || !strcasecmp(language
, "pt_BR")) { /* Portuguese syntax */
440 return(ast_say_number_full_pt(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
441 } else if (!strcasecmp(language
, "se") ) { /* Swedish syntax */
442 return(ast_say_number_full_se(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
443 } else if (!strcasecmp(language
, "tw") || !strcasecmp(language
, "zh") ) { /* Taiwanese / Chinese syntax */
444 return(ast_say_number_full_tw(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
445 } else if (!strcasecmp(language
, "gr") ) { /* Greek syntax */
446 return(ast_say_number_full_gr(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
447 } else if (!strcasecmp(language
, "ru") ) { /* Russian syntax */
448 return(ast_say_number_full_ru(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
449 } else if (!strcasecmp(language
, "ge") ) { /* Georgian syntax */
450 return(ast_say_number_full_ge(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
453 /* Default to english */
454 return(ast_say_number_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
457 /*! \brief ast_say_number_full_en: English syntax */
458 /* This is the default syntax, if no other syntax defined in this file is used */
459 static int ast_say_number_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
465 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
467 while(!res
&& (num
|| playh
)) {
469 snprintf(fn
, sizeof(fn
), "digits/minus");
470 if ( num
> INT_MIN
) {
476 snprintf(fn
, sizeof(fn
), "digits/hundred");
478 } else if (num
< 20) {
479 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
481 } else if (num
< 100) {
482 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
483 num
-= ((num
/ 10) * 10);
486 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
488 num
-= ((num
/ 100) * 100);
490 if (num
< 1000000) { /* 1,000,000 */
491 res
= ast_say_number_full_en(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
495 snprintf(fn
, sizeof(fn
), "digits/thousand");
497 if (num
< 1000000000) { /* 1,000,000,000 */
498 res
= ast_say_number_full_en(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
502 snprintf(fn
, sizeof(fn
), "digits/million");
504 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
511 if(!ast_streamfile(chan
, fn
, language
)) {
512 if ((audiofd
> -1) && (ctrlfd
> -1))
513 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
515 res
= ast_waitstream(chan
, ints
);
517 ast_stopstream(chan
);
523 static int exp10_int(int power
)
526 for (x
=0;x
<power
;x
++)
531 /*! \brief ast_say_number_full_cz: Czech syntax */
533 * 1m,2m - gender male
534 * 1w,2w - gender female
538 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
540 * for each number 10^(3n + 3) exist 3 files represented as:
541 * 1 tousand = jeden tisic = 1_E3
542 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
543 * 5,6,... tousands = pet,sest,... tisic = 5_E3
549 * tousand, milion are gender male, so 1 and 2 is 1m 2m
550 * miliard is gender female, so 1 and 2 is 1w 2w
552 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
)
562 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
567 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
569 while(!res
&& (num
|| playh
)) {
571 snprintf(fn
, sizeof(fn
), "digits/minus");
572 if ( num
> INT_MIN
) {
577 } else if (num
< 3 ) {
578 snprintf(fn
, sizeof(fn
), "digits/%d%c",num
,options
[0]);
581 } else if (num
< 20) {
582 snprintf(fn
, sizeof(fn
), "digits/%d",num
);
585 } else if (num
< 100) {
586 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
587 num
-= ((num
/ 10) * 10);
588 } else if (num
< 1000) {
589 hundered
= num
/ 100;
590 if ( hundered
== 1 ) {
591 snprintf(fn
, sizeof(fn
), "digits/1sto");
592 } else if ( hundered
== 2 ) {
593 snprintf(fn
, sizeof(fn
), "digits/2ste");
595 res
= ast_say_number_full_cz(chan
,hundered
,ints
,language
,options
,audiofd
,ctrlfd
);
598 if (hundered
== 3 || hundered
== 4) {
599 snprintf(fn
, sizeof(fn
), "digits/sta");
600 } else if ( hundered
> 4 ) {
601 snprintf(fn
, sizeof(fn
), "digits/set");
604 num
-= (hundered
* 100);
605 } else { /* num > 1000 */
606 length
= (int)log10(num
)+1;
607 while ( (length
% 3 ) != 1 ) {
610 left
= num
/ (exp10_int(length
-1));
613 case 9: options
= "w"; /* 1,000,000,000 gender female */
615 default : options
= "m"; /* others are male */
618 if ( left
> 1 ) { /* we dont say "one thousand" but only thousand */
619 res
= ast_say_number_full_cz(chan
,left
,ints
,language
,options
,audiofd
,ctrlfd
);
623 if ( left
>= 5 ) { /* >= 5 have the same declesion */
624 snprintf(fn
, sizeof(fn
), "digits/5_E%d",length
-1);
625 } else if ( left
>= 2 && left
<= 4 ) {
626 snprintf(fn
, sizeof(fn
), "digits/2-4_E%d",length
-1);
627 } else { /* left == 1 */
628 snprintf(fn
, sizeof(fn
), "digits/1_E%d",length
-1);
630 num
-= left
* (exp10_int(length
-1));
633 if(!ast_streamfile(chan
, fn
, language
)) {
634 if ((audiofd
> -1) && (ctrlfd
> -1)) {
635 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
637 res
= ast_waitstream(chan
, ints
);
640 ast_stopstream(chan
);
646 /*! \brief ast_say_number_full_da: Danish syntax */
648 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
650 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
)
655 int cn
= 1; /* +1 = commune; -1 = neuter */
658 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
660 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
662 while(!res
&& (num
|| playh
|| playa
)) {
663 /* The grammar for Danish numbers is the same as for English except
665 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
666 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
667 * "one-and twenty" and 68 is "eight-and sixty".
668 * - "million" is different in singular and plural form
669 * - numbers > 1000 with zero as the third digit from last have an
670 * "and" before the last two digits, i.e. 2034 is "two thousand and
671 * four-and thirty" and 1000012 is "one million and twelve".
674 snprintf(fn
, sizeof(fn
), "digits/minus");
675 if ( num
> INT_MIN
) {
681 snprintf(fn
, sizeof(fn
), "digits/hundred");
684 snprintf(fn
, sizeof(fn
), "digits/and");
686 } else if (num
== 1 && cn
== -1) {
687 snprintf(fn
, sizeof(fn
), "digits/1N");
689 } else if (num
< 20) {
690 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
692 } else if (num
< 100) {
695 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
698 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
703 int hundreds
= num
/ 100;
705 snprintf(fn
, sizeof(fn
), "digits/1N");
707 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
710 num
-= 100 * hundreds
;
716 res
= ast_say_number_full_da(chan
, num
/ 1000, ints
, language
, "n", audiofd
, ctrlfd
);
720 snprintf(fn
, sizeof(fn
), "digits/thousand");
722 if (num
< 1000000000) {
723 int millions
= num
/ 1000000;
724 res
= ast_say_number_full_da(chan
, millions
, ints
, language
, "c", audiofd
, ctrlfd
);
728 snprintf(fn
, sizeof(fn
), "digits/million");
730 snprintf(fn
, sizeof(fn
), "digits/millions");
733 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
737 if (num
&& num
< 100)
742 if(!ast_streamfile(chan
, fn
, language
)) {
743 if ((audiofd
> -1) && (ctrlfd
> -1))
744 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
746 res
= ast_waitstream(chan
, ints
);
748 ast_stopstream(chan
);
754 /*! \brief ast_say_number_full_de: German syntax */
756 In addition to English, the following sounds are required:
758 "1-and" through "9-and"
761 NB "1" is recorded as 'eins'
763 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
)
766 int mf
= 1; /* +1 = male and neuter; -1 = female */
770 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
772 if (options
&& (!strncasecmp(options
, "f",1)))
776 /* The grammar for German numbers is the same as for English except
778 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
779 * "one-and twenty" and 68 is "eight-and sixty".
780 * - "one" varies according to gender
781 * - 100 is 'hundert', however all other instances are 'ein hundert'
782 * - 1000 is 'tausend', however all other instances are 'ein tausend'
783 * - 1000000 is always 'eine million'
784 * - "million" is different in singular and plural form
787 snprintf(fn
, sizeof(fn
), "digits/minus");
788 if ( num
> INT_MIN
) {
793 } else if (num
< 100 && t
) {
794 snprintf(fn
, sizeof(fn
), "digits/and");
796 } else if (num
== 1 && mf
== -1) {
797 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
799 } else if (num
< 20) {
800 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
802 } else if (num
< 100) {
805 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
808 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
811 } else if (num
== 100 && t
== 0) {
812 snprintf(fn
, sizeof(fn
), "digits/hundred");
814 } else if (num
< 1000) {
815 int hundreds
= num
/ 100;
818 snprintf(fn
, sizeof(fn
), "digits/1N");
820 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
822 snprintf(fna
, sizeof(fna
), "digits/hundred");
824 } else if (num
== 1000 && t
== 0) {
825 snprintf(fn
, sizeof(fn
), "digits/thousand");
827 } else if (num
< 1000000) {
828 int thousands
= num
/ 1000;
831 if (thousands
== 1) {
832 snprintf(fn
, sizeof(fn
), "digits/1N");
833 snprintf(fna
, sizeof(fna
), "digits/thousand");
835 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
838 snprintf(fn
, sizeof(fn
), "digits/thousand");
840 } else if (num
< 1000000000) {
841 int millions
= num
/ 1000000;
845 snprintf(fn
, sizeof(fn
), "digits/1F");
846 snprintf(fna
, sizeof(fna
), "digits/million");
848 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
851 snprintf(fn
, sizeof(fn
), "digits/millions");
853 } else if (num
<= INT_MAX
) {
854 int billions
= num
/ 1000000000;
855 num
= num
% 1000000000;
858 snprintf(fn
, sizeof(fn
), "digits/1F");
859 snprintf(fna
, sizeof(fna
), "digits/milliard");
861 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
865 snprintf(fn
, sizeof(fn
), "digits/milliards");
868 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
872 if(!ast_streamfile(chan
, fn
, language
)) {
873 if ((audiofd
> -1) && (ctrlfd
> -1))
874 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
876 res
= ast_waitstream(chan
, ints
);
878 ast_stopstream(chan
);
880 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
881 if ((audiofd
> -1) && (ctrlfd
> -1))
882 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
884 res
= ast_waitstream(chan
, ints
);
886 ast_stopstream(chan
);
894 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
896 In addition to American English, the following sounds are required: "and"
898 static int ast_say_number_full_en_GB(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
905 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
907 while(!res
&& (num
|| playh
|| playa
)) {
909 snprintf(fn
, sizeof(fn
), "digits/minus");
910 if ( num
> INT_MIN
) {
916 snprintf(fn
, sizeof(fn
), "digits/hundred");
919 snprintf(fn
, sizeof(fn
), "digits/and");
921 } else if (num
< 20) {
922 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
924 } else if (num
< 100) {
925 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
926 num
-= ((num
/ 10) * 10);
927 } else if (num
< 1000) {
928 int hundreds
= num
/ 100;
929 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
932 num
-= 100 * hundreds
;
935 } else if (num
< 1000000) {
936 res
= ast_say_number_full_en_GB(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
939 snprintf(fn
, sizeof(fn
), "digits/thousand");
941 if (num
&& num
< 100)
943 } else if (num
< 1000000000) {
944 int millions
= num
/ 1000000;
945 res
= ast_say_number_full_en_GB(chan
, millions
, ints
, language
, audiofd
, ctrlfd
);
948 snprintf(fn
, sizeof(fn
), "digits/million");
950 if (num
&& num
< 100)
953 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
958 if(!ast_streamfile(chan
, fn
, language
)) {
959 if ((audiofd
> -1) && (ctrlfd
> -1))
960 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
962 res
= ast_waitstream(chan
, ints
);
964 ast_stopstream(chan
);
970 /*! \brief ast_say_number_full_es: Spanish syntax */
972 Requires a few new audios:
973 1F.gsm: feminine 'una'
974 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
976 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
)
980 int mf
= 0; /* +1 = male; -1 = female */
983 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
986 if (!strncasecmp(options
, "f",1))
988 else if (!strncasecmp(options
, "m", 1))
992 while (!res
&& num
) {
994 snprintf(fn
, sizeof(fn
), "digits/minus");
995 if ( num
> INT_MIN
) {
1001 snprintf(fn
, sizeof(fn
), "digits/and");
1003 } else if (num
== 1) {
1005 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1007 snprintf(fn
, sizeof(fn
), "digits/%dM", num
);
1009 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1011 } else if (num
< 31) {
1012 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1014 } else if (num
< 100) {
1015 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1016 num
-= ((num
/10)*10);
1019 } else if (num
== 100) {
1020 snprintf(fn
, sizeof(fn
), "digits/100");
1022 } else if (num
< 200) {
1023 snprintf(fn
, sizeof(fn
), "digits/100-and");
1027 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100)*100);
1028 num
-= ((num
/100)*100);
1029 } else if (num
< 2000) {
1031 snprintf(fn
, sizeof(fn
), "digits/thousand");
1033 if (num
< 1000000) {
1034 res
= ast_say_number_full_es(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1038 snprintf(fn
, sizeof(fn
), "digits/thousand");
1040 if (num
< 2147483640) {
1041 if ((num
/1000000) == 1) {
1042 res
= ast_say_number_full_es(chan
, num
/ 1000000, ints
, language
, "M", audiofd
, ctrlfd
);
1045 snprintf(fn
, sizeof(fn
), "digits/million");
1047 res
= ast_say_number_full_es(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1050 snprintf(fn
, sizeof(fn
), "digits/millions");
1052 num
= num
% 1000000;
1054 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1062 if(!ast_streamfile(chan
, fn
, language
)) {
1063 if ((audiofd
> -1) && (ctrlfd
> -1))
1064 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1066 res
= ast_waitstream(chan
, ints
);
1068 ast_stopstream(chan
);
1076 /*! \brief ast_say_number_full_fr: French syntax */
1077 /* Extra sounds needed:
1080 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
)
1085 int mf
= 1; /* +1 = male; -1 = female */
1088 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1090 if (options
&& !strncasecmp(options
, "f",1))
1093 while(!res
&& (num
|| playh
|| playa
)) {
1095 snprintf(fn
, sizeof(fn
), "digits/minus");
1096 if ( num
> INT_MIN
) {
1102 snprintf(fn
, sizeof(fn
), "digits/hundred");
1105 snprintf(fn
, sizeof(fn
), "digits/et");
1107 } else if (num
== 1) {
1109 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1111 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1113 } else if (num
< 21) {
1114 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1116 } else if (num
< 70) {
1117 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1118 if ((num
% 10) == 1) playa
++;
1120 } else if (num
< 80) {
1121 snprintf(fn
, sizeof(fn
), "digits/60");
1122 if ((num
% 10) == 1) playa
++;
1124 } else if (num
< 100) {
1125 snprintf(fn
, sizeof(fn
), "digits/80");
1127 } else if (num
< 200) {
1128 snprintf(fn
, sizeof(fn
), "digits/hundred");
1130 } else if (num
< 1000) {
1131 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1134 } else if (num
< 2000) {
1135 snprintf(fn
, sizeof(fn
), "digits/thousand");
1137 } else if (num
< 1000000) {
1138 res
= ast_say_number_full_fr(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1141 snprintf(fn
, sizeof(fn
), "digits/thousand");
1143 } else if (num
< 1000000000) {
1144 res
= ast_say_number_full_fr(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1147 snprintf(fn
, sizeof(fn
), "digits/million");
1148 num
= num
% 1000000;
1150 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1154 if(!ast_streamfile(chan
, fn
, language
)) {
1155 if ((audiofd
> -1) && (ctrlfd
> -1))
1156 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1158 res
= ast_waitstream(chan
, ints
);
1160 ast_stopstream(chan
);
1168 /*! \brief ast_say_number_full_he: Hebrew syntax */
1169 /* Extra sounds needed:
1173 2hundred: 2 hundreds
1174 2thousands: 2 thousand
1175 thousands: plural of 'thousand'
1176 3sF 'Smichut forms (female)
1183 3s 'Smichut' forms (male)
1200 TODO: 've' should sometimed be 'hu':
1201 * before 'shtaym' (2, F)
1202 * before 'shnaym' (2, M)
1203 * before 'shlosha' (3, M)
1204 * before 'shmone' (8, M)
1205 * before 'shlosim' (30)
1206 * before 'shmonim' (80)
1212 #define SAY_NUM_BUF_SIZE 256
1213 static int ast_say_number_full_he(struct ast_channel
*chan
, int num
,
1214 const char *ints
, const char *language
, const char *options
,
1215 int audiofd
, int ctrlfd
)
1218 int state
= 0; /* no need to save anything */
1219 int mf
= 1; /* +1 = Masculin; -1 = Feminin */
1220 char fn
[SAY_NUM_BUF_SIZE
] = "";
1221 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: started. "
1222 "num: %d, options=\"%s\"\n",
1226 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1228 if (options
&& !strncasecmp(options
, "f",1))
1231 /* Do we have work to do? */
1232 while(!res
&& (num
|| (state
>0) )) {
1233 /* first type of work: play a second sound. In this loop
1234 * we can only play one sound file at a time. Thus playing
1235 * a second one requires repeating the loop just for the
1236 * second file. The variable 'state' remembers where we were.
1237 * state==0 is the normal mode and it means that we continue
1238 * to check if the number num has yet anything left.
1240 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: num: %d, "
1241 "state=%d, options=\"%s\", mf=%d\n",
1242 num
, state
, options
, mf
1245 snprintf(fn
, sizeof(fn
), "digits/hundred");
1247 } else if (state
==2) {
1248 snprintf(fn
, sizeof(fn
), "digits/ve");
1250 } else if (state
==3) {
1251 snprintf(fn
, sizeof(fn
), "digits/thousands");
1253 } else if (num
<21) {
1255 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1257 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1259 } else if (num
< 100) {
1260 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1263 } else if (num
< 200) {
1264 snprintf(fn
, sizeof(fn
), "digits/1hundred");
1267 } else if (num
< 300) {
1268 snprintf(fn
, sizeof(fn
), "digits/2hundred");
1271 } else if (num
< 1000) {
1272 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1275 } else if (num
< 2000) {
1276 snprintf(fn
, sizeof(fn
), "digits/thousand");
1278 } else if (num
< 3000) {
1279 snprintf(fn
, sizeof(fn
), "digits/2thousand");
1282 } else if (num
< 20000) {
1283 snprintf(fn
, sizeof(fn
), "digits/%ds",(num
/1000));
1286 } else if (num
< 1000000) {
1287 res
= ast_say_number_full_he(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1290 snprintf(fn
, sizeof(fn
), "digits/thousand");
1292 } else if (num
< 1000000000) {
1293 res
= ast_say_number_full_he(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1296 snprintf(fn
, sizeof(fn
), "digits/million");
1297 num
= num
% 1000000;
1299 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1303 if(!ast_streamfile(chan
, fn
, language
)) {
1304 if ((audiofd
> -1) && (ctrlfd
> -1))
1305 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1307 res
= ast_waitstream(chan
, ints
);
1309 ast_stopstream(chan
);
1315 /*! \brief ast_say_number_full_it: Italian */
1316 static int ast_say_number_full_it(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1324 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1329 Like english, numbers up to 20 are a single 'word', and others
1330 compound, but with exceptions.
1331 For example 21 is not twenty-one, but there is a single word in 'it'.
1332 Idem for 28 (ie when a the 2nd part of a compund number
1333 starts with a vowel)
1335 There are exceptions also for hundred, thousand and million.
1336 In english 100 = one hundred, 200 is two hundred.
1337 In italian 100 = cento , like to say hundred (without one),
1338 200 and more are like english.
1340 Same applies for thousand:
1341 1000 is one thousand in en, 2000 is two thousand.
1342 In it we have 1000 = mille , 2000 = 2 mila
1344 For million(s) we use the plural, if more than one
1345 Also, one million is abbreviated in it, like on-million,
1346 or 'un milione', not 'uno milione'.
1347 So the right file is provided.
1350 while(!res
&& (num
|| playh
)) {
1352 snprintf(fn
, sizeof(fn
), "digits/minus");
1353 if ( num
> INT_MIN
) {
1359 snprintf(fn
, sizeof(fn
), "digits/hundred");
1361 } else if (num
< 20) {
1362 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1364 } else if (num
== 21) {
1365 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1367 } else if (num
== 28) {
1368 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1370 } else if (num
== 31) {
1371 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1373 } else if (num
== 38) {
1374 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1376 } else if (num
== 41) {
1377 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1379 } else if (num
== 48) {
1380 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1382 } else if (num
== 51) {
1383 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1385 } else if (num
== 58) {
1386 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1388 } else if (num
== 61) {
1389 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1391 } else if (num
== 68) {
1392 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1394 } else if (num
== 71) {
1395 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1397 } else if (num
== 78) {
1398 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1400 } else if (num
== 81) {
1401 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1403 } else if (num
== 88) {
1404 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1406 } else if (num
== 91) {
1407 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1409 } else if (num
== 98) {
1410 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1412 } else if (num
< 100) {
1413 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1414 num
-= ((num
/ 10) * 10);
1417 if ((num
/ 100) > 1) {
1418 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1421 snprintf(fn
, sizeof(fn
), "digits/hundred");
1423 num
-= ((num
/ 100) * 100);
1425 if (num
< 1000000) { /* 1,000,000 */
1427 res
= ast_say_number_full_it(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1432 if ((tempnum
/ 1000) < 2)
1433 snprintf(fn
, sizeof(fn
), "digits/thousand");
1434 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1435 snprintf(fn
, sizeof(fn
), "digits/thousands");
1437 if (num
< 1000000000) { /* 1,000,000,000 */
1438 if ((num
/ 1000000) > 1)
1439 res
= ast_say_number_full_it(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1443 num
= num
% 1000000;
1444 if ((tempnum
/ 1000000) < 2)
1445 snprintf(fn
, sizeof(fn
), "digits/million");
1447 snprintf(fn
, sizeof(fn
), "digits/millions");
1449 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1456 if(!ast_streamfile(chan
, fn
, language
)) {
1457 if ((audiofd
> -1) && (ctrlfd
> -1))
1458 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1460 res
= ast_waitstream(chan
, ints
);
1462 ast_stopstream(chan
);
1468 /*! \brief ast_say_number_full_nl: dutch syntax */
1469 /* New files: digits/nl-en
1471 static int ast_say_number_full_nl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1478 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1479 while (!res
&& (num
|| playh
)) {
1481 snprintf(fn
, sizeof(fn
), "digits/minus");
1482 if ( num
> INT_MIN
) {
1488 snprintf(fn
, sizeof(fn
), "digits/hundred");
1490 } else if (num
< 20) {
1491 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1493 } else if (num
< 100) {
1496 res
= ast_say_number_full_nl(chan
, units
, ints
, language
, audiofd
, ctrlfd
);
1500 snprintf(fn
, sizeof(fn
), "digits/nl-en");
1502 snprintf(fn
, sizeof(fn
), "digits/%d", num
- units
);
1507 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1509 num
-= ((num
/ 100) * 100);
1511 if (num
< 1000000) { /* 1,000,000 */
1512 res
= ast_say_number_full_en(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1516 snprintf(fn
, sizeof(fn
), "digits/thousand");
1518 if (num
< 1000000000) { /* 1,000,000,000 */
1519 res
= ast_say_number_full_en(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1522 num
= num
% 1000000;
1523 snprintf(fn
, sizeof(fn
), "digits/million");
1525 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1533 if(!ast_streamfile(chan
, fn
, language
)) {
1534 if ((audiofd
> -1) && (ctrlfd
> -1))
1535 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1537 res
= ast_waitstream(chan
, ints
);
1539 ast_stopstream(chan
);
1545 /*! \brief ast_say_number_full_no: Norwegian syntax */
1547 In addition to American English, the following sounds are required: "and", "1N"
1549 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
)
1554 int cn
= 1; /* +1 = commune; -1 = neuter */
1558 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1560 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
1562 while(!res
&& (num
|| playh
|| playa
)) {
1563 /* The grammar for Norwegian numbers is the same as for English except
1564 * for the following:
1565 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1566 * "and" before the last two digits, i.e. 2034 is "two thousand and
1567 * thirty-four" and 1000012 is "one million and twelve".
1570 snprintf(fn
, sizeof(fn
), "digits/minus");
1571 if ( num
> INT_MIN
) {
1577 snprintf(fn
, sizeof(fn
), "digits/hundred");
1580 snprintf(fn
, sizeof(fn
), "digits/and");
1582 } else if (num
== 1 && cn
== -1) {
1583 snprintf(fn
, sizeof(fn
), "digits/1N");
1585 } else if (num
< 20) {
1586 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1588 } else if (num
< 100) {
1589 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1590 num
-= ((num
/ 10) * 10);
1591 } else if (num
< 1000) {
1592 int hundreds
= num
/ 100;
1594 snprintf(fn
, sizeof(fn
), "digits/1N");
1596 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
1599 num
-= 100 * hundreds
;
1602 } else if (num
< 1000000) {
1603 res
= ast_say_number_full_no(chan
, num
/ 1000, ints
, language
, "n", audiofd
, ctrlfd
);
1606 snprintf(fn
, sizeof(fn
), "digits/thousand");
1608 if (num
&& num
< 100)
1610 } else if (num
< 1000000000) {
1611 int millions
= num
/ 1000000;
1612 res
= ast_say_number_full_no(chan
, millions
, ints
, language
, "c", audiofd
, ctrlfd
);
1615 snprintf(fn
, sizeof(fn
), "digits/million");
1616 num
= num
% 1000000;
1617 if (num
&& num
< 100)
1620 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1625 if(!ast_streamfile(chan
, fn
, language
)) {
1626 if ((audiofd
> -1) && (ctrlfd
> -1))
1627 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1629 res
= ast_waitstream(chan
, ints
);
1631 ast_stopstream(chan
);
1638 char *separator_dziesiatek
;
1642 char *dziesiatki
[10];
1647 static char *pl_rzad_na_tekst(odmiana
*odm
, int i
, int rzad
)
1653 return odm
->rzedy
[rzad
- 1][0];
1654 if ((i
> 21 || i
< 11) && i
%10 > 1 && i
%10 < 5)
1655 return odm
->rzedy
[rzad
- 1][1];
1657 return odm
->rzedy
[rzad
- 1][2];
1660 static char* pl_append(char* buffer
, char* str
)
1662 strcpy(buffer
, str
);
1663 buffer
+= strlen(str
);
1667 static void pl_odtworz_plik(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, char *fn
)
1669 char file_name
[255] = "digits/";
1670 strcat(file_name
, fn
);
1671 ast_log(LOG_DEBUG
, "Trying to play: %s\n", file_name
);
1672 if (!ast_streamfile(chan
, file_name
, language
)) {
1673 if ((audiofd
> -1) && (ctrlfd
> -1))
1674 ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1676 ast_waitstream(chan
, ints
);
1678 ast_stopstream(chan
);
1681 static void powiedz(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, odmiana
*odm
, int rzad
, int i
)
1683 /* Initialise variables to allow compilation on Debian-stable, etc */
1693 if (i
== 0 && rzad
> 0) {
1697 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[0]);
1701 m1000E6
= i
% 1000000000;
1702 i1000E6
= i
/ 1000000000;
1704 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+3, i1000E6
);
1706 m1000E3
= m1000E6
% 1000000;
1707 i1000E3
= m1000E6
/ 1000000;
1709 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+2, i1000E3
);
1711 m1000
= m1000E3
% 1000;
1712 i1000
= m1000E3
/ 1000;
1714 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+1, i1000
);
1720 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->setki
[i100
]);
1722 if ( m100
> 0 && m100
<=9 ) {
1724 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
]);
1726 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[m100
]);
1727 } else if (m100
% 10 == 0) {
1728 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1729 } else if (m100
<= 19 ) {
1730 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->nastki
[m100
% 10]);
1731 } else if (m100
!= 0) {
1732 if (odm
->separator_dziesiatek
[0]==' ') {
1733 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1734 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
% 10]);
1738 b
= pl_append(b
, odm
->dziesiatki
[m100
/ 10]);
1739 b
= pl_append(b
, odm
->separator_dziesiatek
);
1740 b
= pl_append(b
, odm
->cyfry2
[m100
% 10]);
1741 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, buf
);
1746 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, pl_rzad_na_tekst(odm
, i
, rzad
));
1750 /* ast_say_number_full_pl: Polish syntax */
1751 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
)
1761 1000000000.2 miliardy
1762 1000000000.5 miliardow
1826 70m siedemdziesieciu
1838 90m dziewiedziesieciu
1840 and combinations of eg.: 20_1, 30m_3m, etc...
1844 char *zenski_cyfry
[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1846 char *zenski_cyfry2
[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1848 char *meski_cyfry
[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1850 char *meski_cyfry2
[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1852 char *meski_setki
[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1854 char *meski_dziesiatki
[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1856 char *meski_nastki
[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1858 char *nijaki_cyfry
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1860 char *nijaki_cyfry2
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1862 char *nijaki_setki
[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1864 char *nijaki_dziesiatki
[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1866 char *nijaki_nastki
[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1868 char *rzedy
[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1870 /* Initialise variables to allow compilation on Debian-stable, etc */
1873 static odmiana
*odmiana_nieosobowa
= NULL
;
1874 static odmiana
*odmiana_meska
= NULL
;
1875 static odmiana
*odmiana_zenska
= NULL
;
1877 if (odmiana_nieosobowa
== NULL
) {
1878 odmiana_nieosobowa
= (odmiana
*) malloc(sizeof(odmiana
));
1880 odmiana_nieosobowa
->separator_dziesiatek
= " ";
1882 memcpy(odmiana_nieosobowa
->cyfry
, nijaki_cyfry
, sizeof(odmiana_nieosobowa
->cyfry
));
1883 memcpy(odmiana_nieosobowa
->cyfry2
, nijaki_cyfry2
, sizeof(odmiana_nieosobowa
->cyfry
));
1884 memcpy(odmiana_nieosobowa
->setki
, nijaki_setki
, sizeof(odmiana_nieosobowa
->setki
));
1885 memcpy(odmiana_nieosobowa
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_nieosobowa
->dziesiatki
));
1886 memcpy(odmiana_nieosobowa
->nastki
, nijaki_nastki
, sizeof(odmiana_nieosobowa
->nastki
));
1887 memcpy(odmiana_nieosobowa
->rzedy
, rzedy
, sizeof(odmiana_nieosobowa
->rzedy
));
1890 if (odmiana_zenska
== NULL
) {
1891 odmiana_zenska
= (odmiana
*) malloc(sizeof(odmiana
));
1893 odmiana_zenska
->separator_dziesiatek
= " ";
1895 memcpy(odmiana_zenska
->cyfry
, zenski_cyfry
, sizeof(odmiana_zenska
->cyfry
));
1896 memcpy(odmiana_zenska
->cyfry2
, zenski_cyfry2
, sizeof(odmiana_zenska
->cyfry
));
1897 memcpy(odmiana_zenska
->setki
, nijaki_setki
, sizeof(odmiana_zenska
->setki
));
1898 memcpy(odmiana_zenska
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_zenska
->dziesiatki
));
1899 memcpy(odmiana_zenska
->nastki
, nijaki_nastki
, sizeof(odmiana_zenska
->nastki
));
1900 memcpy(odmiana_zenska
->rzedy
, rzedy
, sizeof(odmiana_zenska
->rzedy
));
1903 if (odmiana_meska
== NULL
) {
1904 odmiana_meska
= (odmiana
*) malloc(sizeof(odmiana
));
1906 odmiana_meska
->separator_dziesiatek
= " ";
1908 memcpy(odmiana_meska
->cyfry
, meski_cyfry
, sizeof(odmiana_meska
->cyfry
));
1909 memcpy(odmiana_meska
->cyfry2
, meski_cyfry2
, sizeof(odmiana_meska
->cyfry
));
1910 memcpy(odmiana_meska
->setki
, meski_setki
, sizeof(odmiana_meska
->setki
));
1911 memcpy(odmiana_meska
->dziesiatki
, meski_dziesiatki
, sizeof(odmiana_meska
->dziesiatki
));
1912 memcpy(odmiana_meska
->nastki
, meski_nastki
, sizeof(odmiana_meska
->nastki
));
1913 memcpy(odmiana_meska
->rzedy
, rzedy
, sizeof(odmiana_meska
->rzedy
));
1917 if (strncasecmp(options
, "f", 1) == 0)
1919 else if (strncasecmp(options
, "m", 1) == 0)
1922 o
= odmiana_nieosobowa
;
1924 o
= odmiana_nieosobowa
;
1926 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, o
, 0, num
);
1930 /* ast_say_number_full_pt: Portuguese syntax */
1931 /* Extra sounds needed: */
1932 /* For feminin all sound files end with F */
1933 /* 100E for 100+ something */
1934 /* 1000000S for plural */
1935 /* pt-e for 'and' */
1936 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
)
1940 int mf
= 1; /* +1 = male; -1 = female */
1944 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1946 if (options
&& !strncasecmp(options
, "f",1))
1949 while(!res
&& num
) {
1951 snprintf(fn
, sizeof(fn
), "digits/minus");
1952 if ( num
> INT_MIN
) {
1957 } else if (num
< 20) {
1958 if ((num
== 1 || num
== 2) && (mf
< 0))
1959 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1961 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1963 } else if (num
< 100) {
1964 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 10) * 10);
1968 } else if (num
< 1000) {
1970 snprintf(fn
, sizeof(fn
), "digits/100");
1972 snprintf(fn
, sizeof(fn
), "digits/100E");
1974 if (mf
< 0 && num
> 199)
1975 snprintf(fn
, sizeof(fn
), "digits/%dF", (num
/ 100) * 100);
1977 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100) * 100);
1982 } else if (num
< 1000000) {
1984 res
= ast_say_number_full_pt(chan
, (num
/ 1000) * mf
, ints
, language
, options
, audiofd
, ctrlfd
);
1988 snprintf(fn
, sizeof(fn
), "digits/1000");
1989 if ((num
% 1000) && ((num
% 1000) < 100 || !(num
% 100)))
1992 } else if (num
< 1000000000) {
1993 res
= ast_say_number_full_pt(chan
, (num
/ 1000000), ints
, language
, options
, audiofd
, ctrlfd
);
1997 snprintf(fn
, sizeof(fn
), "digits/1000000");
1999 snprintf(fn
, sizeof(fn
), "digits/1000000S");
2001 if ((num
% 1000000) &&
2003 ((!((num
/ 1000) % 1000) && ((num
% 1000) < 100 || !(num
% 100))) ||
2004 /* no hundreds and below */
2005 (!(num
% 1000) && (((num
/ 1000) % 1000) < 100 || !((num
/ 1000) % 100))) ) )
2007 num
= num
% 1000000;
2009 /* number is too big */
2010 ast_log(LOG_WARNING
, "Number '%d' is too big to say.", num
);
2014 if (!ast_streamfile(chan
, fn
, language
)) {
2015 if ((audiofd
> -1) && (ctrlfd
> -1))
2016 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2018 res
= ast_waitstream(chan
, ints
);
2020 ast_stopstream(chan
);
2022 if (!res
&& playh
) {
2023 res
= wait_file(chan
, ints
, "digits/pt-e", language
);
2024 ast_stopstream(chan
);
2031 /*! \brief ast_say_number_full_se: Swedish syntax */
2032 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
)
2037 int cn
= 1; /* +1 = commune; -1 = neuter */
2039 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2040 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
2042 while(!res
&& (num
|| playh
)) {
2044 snprintf(fn
, sizeof(fn
), "digits/minus");
2045 if ( num
> INT_MIN
) {
2051 snprintf(fn
, sizeof(fn
), "digits/hundred");
2053 } else if (num
< 20) {
2054 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2056 } else if (num
< 100) {
2057 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2058 num
-= ((num
/ 10) * 10);
2059 } else if (num
== 1 && cn
== -1) { /* En eller ett? */
2060 snprintf(fn
, sizeof(fn
), "digits/1N");
2064 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2066 num
-= ((num
/ 100) * 100);
2068 if (num
< 1000000) { /* 1,000,000 */
2069 res
= ast_say_number_full_se(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
2074 snprintf(fn
, sizeof(fn
), "digits/thousand");
2076 if (num
< 1000000000) { /* 1,000,000,000 */
2077 res
= ast_say_number_full_se(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
2081 num
= num
% 1000000;
2082 snprintf(fn
, sizeof(fn
), "digits/million");
2084 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2091 if(!ast_streamfile(chan
, fn
, language
)) {
2092 if ((audiofd
> -1) && (ctrlfd
> -1))
2093 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2095 res
= ast_waitstream(chan
, ints
);
2096 ast_stopstream(chan
);
2103 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2104 static int ast_say_number_full_tw(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2110 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2112 while(!res
&& (num
|| playh
)) {
2114 snprintf(fn
, sizeof(fn
), "digits/minus");
2115 if ( num
> INT_MIN
) {
2121 snprintf(fn
, sizeof(fn
), "digits/hundred");
2123 } else if (num
< 10) {
2124 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2126 } else if (num
< 100) {
2127 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2128 num
-= ((num
/ 10) * 10);
2131 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2133 num
-= ((num
/ 100) * 100);
2135 if (num
< 1000000) { /* 1,000,000 */
2136 res
= ast_say_number_full_tw(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
2140 snprintf(fn
, sizeof(fn
), "digits/thousand");
2142 if (num
< 1000000000) { /* 1,000,000,000 */
2143 res
= ast_say_number_full_tw(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
2146 num
= num
% 1000000;
2147 snprintf(fn
, sizeof(fn
), "digits/million");
2149 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2156 if(!ast_streamfile(chan
, fn
, language
)) {
2157 if ((audiofd
> -1) && (ctrlfd
> -1))
2158 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2160 res
= ast_waitstream(chan
, ints
);
2162 ast_stopstream(chan
);
2169 /*! \brief determine last digits for thousands/millions (ru) */
2170 static int get_lastdigits_ru(int num
) {
2173 } else if (num
< 100) {
2174 return get_lastdigits_ru(num
% 10);
2175 } else if (num
< 1000) {
2176 return get_lastdigits_ru(num
% 100);
2178 return 0; /* number too big */
2182 /*! \brief ast_say_number_full_ru: Russian syntax */
2183 /*! \brief additional files:
2184 n00.gsm (one hundred, two hundred, ...)
2187 thousands-i.gsm (tisyachi)
2188 million-a.gsm (milliona)
2194 where 'n' from 1 to 9
2196 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
)
2202 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2204 while(!res
&& (num
)) {
2206 snprintf(fn
, sizeof(fn
), "digits/minus");
2207 if ( num
> INT_MIN
) {
2212 } else if (num
< 20) {
2213 if(options
&& strlen(options
) == 1 && num
< 3) {
2214 snprintf(fn
, sizeof(fn
), "digits/%d%s", num
, options
);
2216 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2219 } else if (num
< 100) {
2220 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 10));
2222 } else if (num
< 1000){
2223 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 100));
2225 } else if (num
< 1000000) { /* 1,000,000 */
2226 lastdigits
= get_lastdigits_ru(num
/ 1000);
2228 if (lastdigits
< 3) {
2229 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, "f", audiofd
, ctrlfd
);
2231 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2235 if (lastdigits
== 1) {
2236 snprintf(fn
, sizeof(fn
), "digits/thousand");
2237 } else if (lastdigits
> 1 && lastdigits
< 5) {
2238 snprintf(fn
, sizeof(fn
), "digits/thousands-i");
2240 snprintf(fn
, sizeof(fn
), "digits/thousands");
2243 } else if (num
< 1000000000) { /* 1,000,000,000 */
2244 lastdigits
= get_lastdigits_ru(num
/ 1000000);
2246 res
= ast_say_number_full_ru(chan
, num
/ 1000000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2249 if (lastdigits
== 1) {
2250 snprintf(fn
, sizeof(fn
), "digits/million");
2251 } else if (lastdigits
> 1 && lastdigits
< 5) {
2252 snprintf(fn
, sizeof(fn
), "digits/million-a");
2254 snprintf(fn
, sizeof(fn
), "digits/millions");
2258 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2262 if (!ast_streamfile(chan
, fn
, language
)) {
2263 if ((audiofd
> -1) && (ctrlfd
> -1))
2264 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2266 res
= ast_waitstream(chan
, ints
);
2268 ast_stopstream(chan
);
2275 /*! \brief ast_say_enumeration_full: call language-specific functions */
2276 /* Called from AGI */
2277 static int say_enumeration_full(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2279 if (!strcasecmp(language
,"en") ) { /* English syntax */
2280 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2281 } else if (!strcasecmp(language
, "da") ) { /* Danish syntax */
2282 return(ast_say_enumeration_full_da(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2283 } else if (!strcasecmp(language
, "de") ) { /* German syntax */
2284 return(ast_say_enumeration_full_de(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2287 /* Default to english */
2288 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2291 /*! \brief ast_say_enumeration_full_en: English syntax */
2292 /* This is the default syntax, if no other syntax defined in this file is used */
2293 static int ast_say_enumeration_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2298 while(!res
&& num
) {
2300 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2301 if ( num
> INT_MIN
) {
2306 } else if (num
< 20) {
2307 snprintf(fn
, sizeof(fn
), "digits/h-%d", num
);
2309 } else if (num
< 100) {
2310 int tens
= num
/ 10;
2313 snprintf(fn
, sizeof(fn
), "digits/h-%d", (tens
* 10));
2315 snprintf(fn
, sizeof(fn
), "digits/%d", (tens
* 10));
2317 } else if (num
< 1000) {
2318 int hundreds
= num
/ 100;
2320 if (hundreds
> 1 || t
== 1) {
2321 res
= ast_say_number_full_en(chan
, hundreds
, ints
, language
, audiofd
, ctrlfd
);
2326 snprintf(fn
, sizeof(fn
), "digits/hundred");
2328 snprintf(fn
, sizeof(fn
), "digits/h-hundred");
2330 } else if (num
< 1000000) {
2331 int thousands
= num
/ 1000;
2333 if (thousands
> 1 || t
== 1) {
2334 res
= ast_say_number_full_en(chan
, thousands
, ints
, language
, audiofd
, ctrlfd
);
2339 snprintf(fn
, sizeof(fn
), "digits/thousand");
2341 snprintf(fn
, sizeof(fn
), "digits/h-thousand");
2344 } else if (num
< 1000000000) {
2345 int millions
= num
/ 1000000;
2346 num
= num
% 1000000;
2348 res
= ast_say_number_full_en(chan
, millions
, ints
, language
, audiofd
, ctrlfd
);
2352 snprintf(fn
, sizeof(fn
), "digits/million");
2354 snprintf(fn
, sizeof(fn
), "digits/h-million");
2356 } else if (num
< INT_MAX
) {
2357 int billions
= num
/ 1000000000;
2358 num
= num
% 1000000000;
2360 res
= ast_say_number_full_en(chan
, billions
, ints
, language
, audiofd
, ctrlfd
);
2364 snprintf(fn
, sizeof(fn
), "digits/billion");
2366 snprintf(fn
, sizeof(fn
), "digits/h-billion");
2368 } else if (num
== INT_MAX
) {
2369 snprintf(fn
, sizeof(fn
), "digits/h-last");
2372 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2377 if (!ast_streamfile(chan
, fn
, language
)) {
2378 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2379 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2381 res
= ast_waitstream(chan
, ints
);
2384 ast_stopstream(chan
);
2390 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2391 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
)
2393 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2395 char fn
[256] = "", fna
[256] = "";
2398 if (options
&& !strncasecmp(options
, "f",1)) {
2400 } else if (options
&& !strncasecmp(options
, "n",1)) {
2407 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2409 while(!res
&& num
) {
2411 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2412 if ( num
> INT_MIN
) {
2417 } else if (num
< 100 && t
) {
2418 snprintf(fn
, sizeof(fn
), "digits/and");
2420 } else if (num
< 20) {
2421 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2423 } else if (num
< 100) {
2424 int ones
= num
% 10;
2426 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2429 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2432 } else if (num
== 100 && t
== 0) {
2433 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2435 } else if (num
< 1000) {
2436 int hundreds
= num
/ 100;
2438 if (hundreds
== 1) {
2439 snprintf(fn
, sizeof(fn
), "digits/1N");
2441 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2444 snprintf(fna
, sizeof(fna
), "digits/hundred");
2446 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2449 } else if (num
< 1000000) {
2450 int thousands
= num
/ 1000;
2452 if (thousands
== 1) {
2454 snprintf(fn
, sizeof(fn
), "digits/1N");
2455 snprintf(fna
, sizeof(fna
), "digits/thousand");
2458 snprintf(fn
, sizeof(fn
), "digits/1N");
2459 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2461 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2465 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2470 snprintf(fn
, sizeof(fn
), "digits/thousand");
2472 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2476 } else if (num
< 1000000000) {
2477 int millions
= num
/ 1000000;
2478 num
= num
% 1000000;
2479 if (millions
== 1) {
2481 snprintf(fn
, sizeof(fn
), "digits/1F");
2482 snprintf(fna
, sizeof(fna
), "digits/million");
2484 snprintf(fn
, sizeof(fn
), "digits/1N");
2485 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2488 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2493 snprintf(fn
, sizeof(fn
), "digits/millions");
2495 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2499 } else if (num
< INT_MAX
) {
2500 int billions
= num
/ 1000000000;
2501 num
= num
% 1000000000;
2502 if (billions
== 1) {
2504 snprintf(fn
, sizeof(fn
), "digits/1F");
2505 snprintf(fna
, sizeof(fna
), "digits/milliard");
2507 snprintf(fn
, sizeof(fn
), "digits/1N");
2508 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2511 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2515 snprintf(fn
, sizeof(fna
), "digits/milliards");
2517 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2521 } else if (num
== INT_MAX
) {
2522 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2525 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2530 if (!ast_streamfile(chan
, fn
, language
)) {
2531 if ((audiofd
> -1) && (ctrlfd
> -1))
2532 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2534 res
= ast_waitstream(chan
, ints
);
2536 ast_stopstream(chan
);
2538 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2539 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2540 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2542 res
= ast_waitstream(chan
, ints
);
2545 ast_stopstream(chan
);
2553 /*! \brief ast_say_enumeration_full_de: German syntax */
2554 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
)
2556 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2558 char fn
[256] = "", fna
[256] = "";
2561 if (options
&& !strncasecmp(options
, "f",1)) {
2563 } else if (options
&& !strncasecmp(options
, "n",1)) {
2570 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2572 while(!res
&& num
) {
2574 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2575 if ( num
> INT_MIN
) {
2580 } else if (num
< 100 && t
) {
2581 snprintf(fn
, sizeof(fn
), "digits/and");
2583 } else if (num
< 20) {
2584 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2586 } else if (num
< 100) {
2587 int ones
= num
% 10;
2589 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2592 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2595 } else if (num
== 100 && t
== 0) {
2596 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2598 } else if (num
< 1000) {
2599 int hundreds
= num
/ 100;
2601 if (hundreds
== 1) {
2602 snprintf(fn
, sizeof(fn
), "digits/1N");
2604 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2607 snprintf(fna
, sizeof(fna
), "digits/hundred");
2609 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2612 } else if (num
< 1000000) {
2613 int thousands
= num
/ 1000;
2615 if (thousands
== 1) {
2617 snprintf(fn
, sizeof(fn
), "digits/1N");
2618 snprintf(fna
, sizeof(fna
), "digits/thousand");
2621 snprintf(fn
, sizeof(fn
), "digits/1N");
2622 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2624 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2628 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2633 snprintf(fn
, sizeof(fn
), "digits/thousand");
2635 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2639 } else if (num
< 1000000000) {
2640 int millions
= num
/ 1000000;
2641 num
= num
% 1000000;
2642 if (millions
== 1) {
2644 snprintf(fn
, sizeof(fn
), "digits/1F");
2645 snprintf(fna
, sizeof(fna
), "digits/million");
2647 snprintf(fn
, sizeof(fn
), "digits/1N");
2648 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2651 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2656 snprintf(fn
, sizeof(fn
), "digits/millions");
2658 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2662 } else if (num
< INT_MAX
) {
2663 int billions
= num
/ 1000000000;
2664 num
= num
% 1000000000;
2665 if (billions
== 1) {
2667 snprintf(fn
, sizeof(fn
), "digits/1F");
2668 snprintf(fna
, sizeof(fna
), "digits/milliard");
2670 snprintf(fn
, sizeof(fn
), "digits/1N");
2671 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2674 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2678 snprintf(fn
, sizeof(fna
), "digits/milliards");
2680 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2684 } else if (num
== INT_MAX
) {
2685 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2688 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2693 if (!ast_streamfile(chan
, fn
, language
)) {
2694 if ((audiofd
> -1) && (ctrlfd
> -1))
2695 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2697 res
= ast_waitstream(chan
, ints
);
2699 ast_stopstream(chan
);
2701 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2702 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2703 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2705 res
= ast_waitstream(chan
, ints
);
2708 ast_stopstream(chan
);
2716 static int say_date(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2718 if (!strcasecmp(lang
, "en") ) { /* English syntax */
2719 return(ast_say_date_en(chan
, t
, ints
, lang
));
2720 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
2721 return(ast_say_date_da(chan
, t
, ints
, lang
));
2722 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
2723 return(ast_say_date_de(chan
, t
, ints
, lang
));
2724 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
2725 return(ast_say_date_fr(chan
, t
, ints
, lang
));
2726 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
2727 return(ast_say_date_nl(chan
, t
, ints
, lang
));
2728 } else if (!strcasecmp(lang
, "pt") || !strcasecmp(lang
, "pt_BR")) { /* Portuguese syntax */
2729 return(ast_say_date_pt(chan
, t
, ints
, lang
));
2730 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
2731 return(ast_say_date_gr(chan
, t
, ints
, lang
));
2732 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
2733 return(ast_say_date_ge(chan
, t
, ints
, lang
));
2736 /* Default to English */
2737 return(ast_say_date_en(chan
, t
, ints
, lang
));
2740 /* English syntax */
2741 int ast_say_date_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2746 ast_localtime(&t
,&tm
,NULL
);
2748 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2749 res
= ast_streamfile(chan
, fn
, lang
);
2751 res
= ast_waitstream(chan
, ints
);
2754 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2755 res
= ast_streamfile(chan
, fn
, lang
);
2757 res
= ast_waitstream(chan
, ints
);
2760 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2762 res
= ast_waitstream(chan
, ints
);
2764 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2769 int ast_say_date_da(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2774 ast_localtime(&t
,&tm
,NULL
);
2776 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2777 res
= ast_streamfile(chan
, fn
, lang
);
2779 res
= ast_waitstream(chan
, ints
);
2782 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2784 res
= ast_waitstream(chan
, ints
);
2786 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2787 res
= ast_streamfile(chan
, fn
, lang
);
2789 res
= ast_waitstream(chan
, ints
);
2793 int year
= tm
.tm_year
+ 1900;
2794 if (year
> 1999) { /* year 2000 and later */
2795 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2798 /* I'm not going to handle 1100 and prior */
2799 /* We'll just be silent on the year, instead of bombing out. */
2801 /* year 1100 to 1999. will anybody need this?!? */
2802 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2803 res
= wait_file(chan
, ints
, fn
, lang
);
2805 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2806 if (!res
&& year
% 100 != 0) {
2807 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
2817 int ast_say_date_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2822 ast_localtime(&t
,&tm
,NULL
);
2824 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2825 res
= ast_streamfile(chan
, fn
, lang
);
2827 res
= ast_waitstream(chan
, ints
);
2830 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2832 res
= ast_waitstream(chan
, ints
);
2834 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2835 res
= ast_streamfile(chan
, fn
, lang
);
2837 res
= ast_waitstream(chan
, ints
);
2841 int year
= tm
.tm_year
+ 1900;
2842 if (year
> 1999) { /* year 2000 and later */
2843 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2846 /* I'm not going to handle 1100 and prior */
2847 /* We'll just be silent on the year, instead of bombing out. */
2849 /* year 1100 to 1999. will anybody need this?!? */
2850 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2851 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2852 res
= wait_file(chan
, ints
, fn
, lang
);
2854 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2855 if (!res
&& year
% 100 != 0) {
2856 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
2866 int ast_say_date_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2871 ast_localtime(&t
,&tm
,NULL
);
2873 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2874 res
= ast_streamfile(chan
, fn
, lang
);
2876 res
= ast_waitstream(chan
, ints
);
2879 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2881 res
= ast_waitstream(chan
, ints
);
2883 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2884 res
= ast_streamfile(chan
, fn
, lang
);
2886 res
= ast_waitstream(chan
, ints
);
2889 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2894 int ast_say_date_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2899 ast_localtime(&t
,&tm
,NULL
);
2901 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2902 res
= ast_streamfile(chan
, fn
, lang
);
2904 res
= ast_waitstream(chan
, ints
);
2907 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2909 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2910 res
= ast_streamfile(chan
, fn
, lang
);
2912 res
= ast_waitstream(chan
, ints
);
2915 res
= ast_waitstream(chan
, ints
);
2917 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2921 /* Portuguese syntax */
2922 int ast_say_date_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2928 ast_localtime(&t
, &tm
, NULL
);
2929 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2931 res
= wait_file(chan
, ints
, fn
, lang
);
2933 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
2935 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
2936 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2938 res
= wait_file(chan
, ints
, fn
, lang
);
2940 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
2942 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2947 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
)
2949 if (!strcasecmp(lang
, "en") ) { /* English syntax */
2950 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
2951 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
2952 return(ast_say_date_with_format_da(chan
, time
, ints
, lang
, format
, timezone
));
2953 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
2954 return(ast_say_date_with_format_de(chan
, time
, ints
, lang
, format
, timezone
));
2955 } else if (!strcasecmp(lang
, "es") || !strcasecmp(lang
, "mx")) { /* Spanish syntax */
2956 return(ast_say_date_with_format_es(chan
, time
, ints
, lang
, format
, timezone
));
2957 } else if (!strcasecmp(lang
, "he")) { /* Hebrew syntax */
2958 return(ast_say_date_with_format_he(chan
, time
, ints
, lang
, format
, timezone
));
2959 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
2960 return(ast_say_date_with_format_fr(chan
, time
, ints
, lang
, format
, timezone
));
2961 } else if (!strcasecmp(lang
, "it") ) { /* Italian syntax */
2962 return(ast_say_date_with_format_it(chan
, time
, ints
, lang
, format
, timezone
));
2963 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
2964 return(ast_say_date_with_format_nl(chan
, time
, ints
, lang
, format
, timezone
));
2965 } else if (!strcasecmp(lang
, "pl") ) { /* Polish syntax */
2966 return(ast_say_date_with_format_pl(chan
, time
, ints
, lang
, format
, timezone
));
2967 } else if (!strcasecmp(lang
, "pt") || !strcasecmp(lang
, "pt_BR")) { /* Portuguese syntax */
2968 return(ast_say_date_with_format_pt(chan
, time
, ints
, lang
, format
, timezone
));
2969 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
2970 return(ast_say_date_with_format_tw(chan
, time
, ints
, lang
, format
, timezone
));
2971 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
2972 return(ast_say_date_with_format_gr(chan
, time
, ints
, lang
, format
, timezone
));
2975 /* Default to English */
2976 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
2979 /* English syntax */
2980 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
)
2983 int res
=0, offset
, sndoffset
;
2984 char sndfile
[256], nextmsg
[256];
2987 format
= "ABdY 'digits/at' IMp";
2989 ast_localtime(&time
,&tm
,timezone
);
2991 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
2992 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
2993 switch (format
[offset
]) {
2994 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
2996 /* Literal name of a sound file */
2998 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
2999 sndfile
[sndoffset
] = format
[offset
];
3000 sndfile
[sndoffset
] = '\0';
3001 res
= wait_file(chan
,ints
,sndfile
,lang
);
3005 /* Sunday - Saturday */
3006 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3007 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3012 /* January - December */
3013 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3014 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3017 /* Month enumerated */
3018 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, (char *) NULL
);
3022 /* First - Thirtyfirst */
3023 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
3027 if (tm
.tm_year
> 99) {
3028 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
3029 } else if (tm
.tm_year
< 1) {
3030 /* I'm not going to handle 1900 and prior */
3031 /* We'll just be silent on the year, instead of bombing out. */
3033 res
= wait_file(chan
, ints
, "digits/19", lang
);
3035 if (tm
.tm_year
<= 9) {
3037 res
= wait_file(chan
,ints
, "digits/oh", lang
);
3040 res
|= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char *) NULL
);
3047 if (tm
.tm_hour
== 0)
3048 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3049 else if (tm
.tm_hour
> 12)
3050 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3052 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3053 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3058 if (format
[offset
] == 'H') {
3060 if (tm
.tm_hour
< 10) {
3061 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3065 if (tm
.tm_hour
== 0) {
3066 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3070 if (tm
.tm_hour
!= 0) {
3071 int remainder
= tm
.tm_hour
;
3072 if (tm
.tm_hour
> 20) {
3073 res
= wait_file(chan
,ints
, "digits/20",lang
);
3077 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
3078 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3086 if (tm
.tm_min
== 0) {
3087 if (format
[offset
] == 'M') {
3088 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
3090 res
= wait_file(chan
, ints
, "digits/hundred", lang
);
3092 } else if (tm
.tm_min
< 10) {
3093 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3095 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
3096 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3099 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
3105 if (tm
.tm_hour
> 11)
3106 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3108 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3109 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3112 /* Shorthand for "Today", "Yesterday", or ABdY */
3113 /* XXX As emphasized elsewhere, this should the native way in your
3114 * language to say the date, with changes in what you say, depending
3115 * upon how recent the date is. XXX */
3119 time_t beg_today
, tt
;
3121 gettimeofday(&now
,NULL
);
3123 ast_localtime(&tt
,&tmnow
,timezone
);
3124 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3125 /* In any case, it saves not having to do ast_mktime() */
3126 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3127 if (beg_today
< time
) {
3129 res
= wait_file(chan
,ints
, "digits/today",lang
);
3130 } else if (beg_today
- 86400 < time
) {
3132 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3133 } else if (beg_today
- 86400 * 6 < time
) {
3134 /* Within the last week */
3135 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3136 } else if (beg_today
- 2628000 < time
) {
3137 /* Less than a month ago - "Sunday, October third" */
3138 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3139 } else if (beg_today
- 15768000 < time
) {
3140 /* Less than 6 months ago - "August seventh" */
3141 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3143 /* More than 6 months ago - "April nineteenth two thousand three" */
3144 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3149 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3150 /* XXX As emphasized elsewhere, this should the native way in your
3151 * language to say the date, with changes in what you say, depending
3152 * upon how recent the date is. XXX */
3156 time_t beg_today
, tt
;
3158 gettimeofday(&now
,NULL
);
3160 ast_localtime(&tt
,&tmnow
,timezone
);
3161 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3162 /* In any case, it saves not having to do ast_mktime() */
3163 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3164 if (beg_today
< time
) {
3166 } else if ((beg_today
- 86400) < time
) {
3168 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3169 } else if (beg_today
- 86400 * 6 < time
) {
3170 /* Within the last week */
3171 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3172 } else if (beg_today
- 2628000 < time
) {
3173 /* Less than a month ago - "Sunday, October third" */
3174 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3175 } else if (beg_today
- 15768000 < time
) {
3176 /* Less than 6 months ago - "August seventh" */
3177 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3179 /* More than 6 months ago - "April nineteenth two thousand three" */
3180 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3185 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HM", timezone
);
3189 if (tm
.tm_sec
== 0) {
3190 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3191 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3192 } else if (tm
.tm_sec
< 10) {
3193 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3195 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3196 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3199 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, (char *) NULL
);
3203 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HMS", timezone
);
3207 /* Just ignore spaces and tabs */
3210 /* Unknown character */
3211 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3213 /* Jump out on DTMF */
3222 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
)
3225 int res
=0, offset
, sndoffset
;
3226 char sndfile
[256], nextmsg
[256];
3229 format
= "A dBY HMS";
3231 ast_localtime(&time
,&tm
,timezone
);
3233 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3234 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3235 switch (format
[offset
]) {
3236 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3238 /* Literal name of a sound file */
3240 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3241 sndfile
[sndoffset
] = format
[offset
];
3242 sndfile
[sndoffset
] = '\0';
3243 res
= wait_file(chan
,ints
,sndfile
,lang
);
3247 /* Sunday - Saturday */
3248 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3249 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3254 /* January - December */
3255 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3256 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3259 /* Month enumerated */
3260 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3264 /* First - Thirtyfirst */
3265 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3270 int year
= tm
.tm_year
+ 1900;
3271 if (year
> 1999) { /* year 2000 and later */
3272 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3275 /* I'm not going to handle 1100 and prior */
3276 /* We'll just be silent on the year, instead of bombing out. */
3278 /* year 1100 to 1999. will anybody need this?!? */
3279 /* say 1967 as 'nineteen hundred seven and sixty' */
3280 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3281 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3283 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3284 if (!res
&& year
% 100 != 0) {
3285 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3295 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3296 if (tm
.tm_hour
== 0)
3297 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3298 else if (tm
.tm_hour
> 12)
3299 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3301 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3303 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3307 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3308 if (tm
.tm_hour
< 10 && tm
.tm_hour
> 0) {
3309 res
= wait_file(chan
,ints
, "digits/0",lang
);
3314 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3318 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3319 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3321 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3322 if (tm
.tm_min
== 1) {
3323 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3325 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3332 if (tm
.tm_hour
> 11)
3333 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3335 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3336 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3339 /* Shorthand for "Today", "Yesterday", or AdBY */
3340 /* XXX As emphasized elsewhere, this should the native way in your
3341 * language to say the date, with changes in what you say, depending
3342 * upon how recent the date is. XXX */
3346 time_t beg_today
, tt
;
3348 gettimeofday(&now
,NULL
);
3350 ast_localtime(&tt
,&tmnow
,timezone
);
3351 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3352 /* In any case, it saves not having to do ast_mktime() */
3353 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3354 if (beg_today
< time
) {
3356 res
= wait_file(chan
,ints
, "digits/today",lang
);
3357 } else if (beg_today
- 86400 < time
) {
3359 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3361 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3366 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3367 /* XXX As emphasized elsewhere, this should the native way in your
3368 * language to say the date, with changes in what you say, depending
3369 * upon how recent the date is. XXX */
3373 time_t beg_today
, tt
;
3375 gettimeofday(&now
,NULL
);
3377 ast_localtime(&tt
,&tmnow
,timezone
);
3378 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3379 /* In any case, it saves not having to do ast_mktime() */
3380 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3381 if (beg_today
< time
) {
3383 } else if ((beg_today
- 86400) < time
) {
3385 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3386 } else if (beg_today
- 86400 * 6 < time
) {
3387 /* Within the last week */
3388 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "A", timezone
);
3390 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3395 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HM", timezone
);
3399 res
= wait_file(chan
,ints
, "digits/and",lang
);
3401 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3403 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3408 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HMS", timezone
);
3412 /* Just ignore spaces and tabs */
3415 /* Unknown character */
3416 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3418 /* Jump out on DTMF */
3427 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
)
3430 int res
=0, offset
, sndoffset
;
3431 char sndfile
[256], nextmsg
[256];
3434 format
= "A dBY HMS";
3436 ast_localtime(&time
,&tm
,timezone
);
3438 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3439 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3440 switch (format
[offset
]) {
3441 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3443 /* Literal name of a sound file */
3445 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3446 sndfile
[sndoffset
] = format
[offset
];
3447 sndfile
[sndoffset
] = '\0';
3448 res
= wait_file(chan
,ints
,sndfile
,lang
);
3452 /* Sunday - Saturday */
3453 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3454 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3459 /* January - December */
3460 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3461 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3464 /* Month enumerated */
3465 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3469 /* First - Thirtyfirst */
3470 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3475 int year
= tm
.tm_year
+ 1900;
3476 if (year
> 1999) { /* year 2000 and later */
3477 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3480 /* I'm not going to handle 1100 and prior */
3481 /* We'll just be silent on the year, instead of bombing out. */
3483 /* year 1100 to 1999. will anybody need this?!? */
3484 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3485 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3486 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3488 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3489 if (!res
&& year
% 100 != 0) {
3490 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3500 if (tm
.tm_hour
== 0)
3501 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3502 else if (tm
.tm_hour
> 12)
3503 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3505 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3506 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3508 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3514 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3516 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3521 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3522 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3524 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3525 if (tm
.tm_min
== 1) {
3526 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3528 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3535 if (tm
.tm_hour
> 11)
3536 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3538 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3539 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3542 /* Shorthand for "Today", "Yesterday", or AdBY */
3543 /* XXX As emphasized elsewhere, this should the native way in your
3544 * language to say the date, with changes in what you say, depending
3545 * upon how recent the date is. XXX */
3549 time_t beg_today
, tt
;
3551 gettimeofday(&now
,NULL
);
3553 ast_localtime(&tt
,&tmnow
,timezone
);
3554 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3555 /* In any case, it saves not having to do ast_mktime() */
3556 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3557 if (beg_today
< time
) {
3559 res
= wait_file(chan
,ints
, "digits/today",lang
);
3560 } else if (beg_today
- 86400 < time
) {
3562 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3564 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3569 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3570 /* XXX As emphasized elsewhere, this should the native way in your
3571 * language to say the date, with changes in what you say, depending
3572 * upon how recent the date is. XXX */
3576 time_t beg_today
, tt
;
3578 gettimeofday(&now
,NULL
);
3580 ast_localtime(&tt
,&tmnow
,timezone
);
3581 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3582 /* In any case, it saves not having to do ast_mktime() */
3583 beg_today
= now
.tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3584 if (beg_today
< time
) {
3586 } else if ((beg_today
- 86400) < time
) {
3588 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3589 } else if (beg_today
- 86400 * 6 < time
) {
3590 /* Within the last week */
3591 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "A", timezone
);
3593 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3598 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HM", timezone
);
3602 res
= wait_file(chan
,ints
, "digits/and",lang
);
3604 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3606 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3611 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HMS", timezone
);
3615 /* Just ignore spaces and tabs */
3618 /* Unknown character */
3619 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3621 /* Jump out on DTMF */
3629 /* TODO: this probably is not the correct format for doxygen remarks */
3631 /** ast_say_date_with_format_he Say formmated date in Hebrew
3633 * \ref ast_say_date_with_format_en for the details of the options
3635 * Changes from the English version:
3637 * * don't replicate in here the logic of ast_say_number_full_he
3639 * * year is always 4-digit (because it's simpler)
3641 * * added c, x, and X. Mainly for my tests
3643 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3646 * * A "ha" is missing in the standard date format, before the 'd'.
3647 * * The numbers of 3000--19000 are not handled well
3649 #define IL_DATE_STR "AdBY"
3650 #define IL_TIME_STR "IMp"
3651 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3652 int ast_say_date_with_format_he(struct ast_channel
*chan
, time_t time
,
3653 const char *ints
, const char *lang
, const char *format
,
3654 const char *timezone
)
3656 /* TODO: This whole function is cut&paste from
3657 * ast_say_date_with_format_en . Is that considered acceptable?
3660 int res
=0, offset
, sndoffset
;
3661 char sndfile
[256], nextmsg
[256];
3664 format
= IL_DATE_STR_FULL
;
3666 ast_localtime(&time
,&tm
,timezone
);
3668 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3669 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3670 switch (format
[offset
]) {
3671 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3673 /* Literal name of a sound file */
3675 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3676 sndfile
[sndoffset
] = format
[offset
];
3677 sndfile
[sndoffset
] = '\0';
3678 res
= wait_file(chan
,ints
,sndfile
,lang
);
3682 /* Sunday - Saturday */
3683 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3684 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3689 /* January - December */
3690 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3691 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3694 case 'e': /* Day of the month */
3695 /* I'm not sure exactly what the parameters
3696 * audiofd and ctrlfd to
3697 * ast_say_number_full_he mean, but it seems
3698 * safe to pass -1 there.
3700 * At least in one of the pathes :-(
3702 res
= ast_say_number_full_he(chan
, tm
.tm_mday
,
3703 ints
, lang
, "m", -1, -1
3706 case 'Y': /* Year */
3707 res
= ast_say_number_full_he(chan
, tm
.tm_year
+1900,
3708 ints
, lang
, "f", -1, -1
3712 case 'l': /* 12-Hour */
3714 int hour
= tm
.tm_hour
;
3716 if (hour
== 0) hour
=12;
3718 res
= ast_say_number_full_he(chan
, hour
,
3719 ints
, lang
, "f", -1, -1
3724 case 'k': /* 24-Hour */
3725 /* With 'H' there is an 'oh' after a single-
3727 if ((format
[offset
] == 'H') &&
3728 (tm
.tm_hour
<10)&&(tm
.tm_hour
>0)
3729 ) { /* e.g. oh-eight */
3730 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3733 res
= ast_say_number_full_he(chan
, tm
.tm_hour
,
3734 ints
, lang
, "f", -1, -1
3737 case 'M': /* Minute */
3738 res
= ast_say_number_full_he(chan
, tm
.tm_min
,
3739 ints
, lang
,"f", -1, -1
3745 if (tm
.tm_hour
> 11)
3746 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3748 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3749 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3752 /* Shorthand for "Today", "Yesterday", or "date" */
3754 /* Shorthand for "" (today), "Yesterday", A
3755 * (weekday), or "date" */
3756 /* XXX As emphasized elsewhere, this should the native way in your
3757 * language to say the date, with changes in what you say, depending
3758 * upon how recent the date is. XXX */
3762 time_t beg_today
, tt
;
3763 char todo
= format
[offset
]; /* The letter to format*/
3765 gettimeofday(&now
,NULL
);
3767 ast_localtime(&tt
,&tmnow
,timezone
);
3768 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3769 /* In any case, it saves not having to do ast_mktime() */
3770 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3771 if (beg_today
< time
) {
3774 res
= wait_file(chan
,
3779 } else if (beg_today
- 86400 < time
) {
3781 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3782 } else if ((todo
!= 'Q') &&
3783 (beg_today
- 86400 * 6 < time
))
3785 /* Within the last week */
3786 res
= ast_say_date_with_format_he(chan
,
3790 res
= ast_say_date_with_format_he(chan
,
3792 IL_DATE_STR
, timezone
);
3797 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HM", timezone
);
3799 case 'S': /* Seconds */
3800 res
= ast_say_number_full_he(chan
, tm
.tm_sec
,
3801 ints
, lang
, "f", -1, -1
3805 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HMS", timezone
);
3807 /* c, x, and X seem useful for testing. Not sure
3808 * if thiey're good for the general public */
3810 res
= ast_say_date_with_format_he(chan
, time
,
3811 ints
, lang
, IL_DATE_STR_FULL
, timezone
);
3814 res
= ast_say_date_with_format_he(chan
, time
,
3815 ints
, lang
, IL_DATE_STR
, timezone
);
3817 case 'X': /* Currently not locale-dependent...*/
3818 res
= ast_say_date_with_format_he(chan
, time
,
3819 ints
, lang
, IL_TIME_STR
, timezone
);
3823 /* Just ignore spaces and tabs */
3826 /* Unknown character */
3827 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3829 /* Jump out on DTMF */
3838 /* Spanish syntax */
3839 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
)
3842 int res
=0, offset
, sndoffset
;
3843 char sndfile
[256], nextmsg
[256];
3846 format
= "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
3848 ast_localtime(&time
,&tm
,timezone
);
3850 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3851 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3852 switch (format
[offset
]) {
3853 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3855 /* Literal name of a sound file */
3857 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3858 sndfile
[sndoffset
] = format
[offset
];
3859 sndfile
[sndoffset
] = '\0';
3860 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
3861 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3865 /* Sunday - Saturday */
3866 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3867 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3872 /* January - December */
3873 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3874 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3877 /* First - Twelfth */
3878 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
3879 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3883 /* First - Thirtyfirst */
3884 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
3888 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
3893 if (tm
.tm_hour
== 0)
3894 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3895 else if (tm
.tm_hour
> 12)
3896 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3898 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3899 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3904 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, NULL
);
3908 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
3913 if (tm
.tm_hour
> 18)
3914 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
3915 else if (tm
.tm_hour
> 12)
3916 res
= wait_file(chan
, ints
, "digits/afternoon", lang
);
3917 else if (tm
.tm_hour
)
3918 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
3921 /* Shorthand for "Today", "Yesterday", or ABdY */
3922 /* XXX As emphasized elsewhere, this should the native way in your
3923 * language to say the date, with changes in what you say, depending
3924 * upon how recent the date is. XXX */
3928 time_t beg_today
, tt
;
3930 gettimeofday(&now
,NULL
);
3932 ast_localtime(&tt
,&tmnow
,timezone
);
3933 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3934 /* In any case, it saves not having to do ast_mktime() */
3935 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3936 if (beg_today
< time
) {
3938 res
= wait_file(chan
,ints
, "digits/today",lang
);
3939 } else if (beg_today
- 86400 < time
) {
3941 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3943 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
3948 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3949 /* XXX As emphasized elsewhere, this should the native way in your
3950 * language to say the date, with changes in what you say, depending
3951 * upon how recent the date is. XXX */
3955 time_t beg_today
, tt
;
3957 gettimeofday(&now
,NULL
);
3959 ast_localtime(&tt
,&tmnow
,timezone
);
3960 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3961 /* In any case, it saves not having to do ast_mktime() */
3962 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3963 if (beg_today
< time
) {
3965 res
= wait_file(chan
,ints
, "digits/today",lang
);
3966 } else if ((beg_today
- 86400) < time
) {
3968 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3969 } else if (beg_today
- 86400 * 6 < time
) {
3970 /* Within the last week */
3971 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "A", timezone
);
3973 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
3978 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "H 'digits/y' M", timezone
);
3982 if (tm
.tm_sec
== 0) {
3983 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3984 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3985 } else if (tm
.tm_sec
< 10) {
3986 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3988 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3989 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3991 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
3992 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3993 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3996 ten
= (tm
.tm_sec
/ 10) * 10;
3997 one
= (tm
.tm_sec
% 10);
3998 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
3999 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4001 /* Fifty, not fifty-zero */
4003 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4004 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4010 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "HMS", timezone
);
4014 /* Just ignore spaces and tabs */
4017 /* Unknown character */
4018 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4020 /* Jump out on DTMF */
4031 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
)
4034 int res
=0, offset
, sndoffset
;
4035 char sndfile
[256], nextmsg
[256];
4038 format
= "AdBY 'digits/at' IMp";
4040 ast_localtime(&time
,&tm
,timezone
);
4042 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4043 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4044 switch (format
[offset
]) {
4045 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4047 /* Literal name of a sound file */
4049 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4050 sndfile
[sndoffset
] = format
[offset
];
4051 sndfile
[sndoffset
] = '\0';
4052 res
= wait_file(chan
,ints
,sndfile
,lang
);
4056 /* Sunday - Saturday */
4057 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4058 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4063 /* January - December */
4064 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4065 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4068 /* First - Twelfth */
4069 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4070 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4075 if (tm
.tm_mday
== 1) {
4076 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4077 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4079 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
4084 if (tm
.tm_year
> 99) {
4085 res
= wait_file(chan
,ints
, "digits/2",lang
);
4087 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4089 if (tm
.tm_year
> 100) {
4091 res
= ast_say_number(chan
, tm
.tm_year
- 100, ints
, lang
, (char * ) NULL
);
4095 if (tm
.tm_year
< 1) {
4096 /* I'm not going to handle 1900 and prior */
4097 /* We'll just be silent on the year, instead of bombing out. */
4099 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4101 wait_file(chan
,ints
, "digits/9",lang
);
4102 wait_file(chan
,ints
, "digits/hundred",lang
);
4103 res
= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char * ) NULL
);
4111 if (tm
.tm_hour
== 0)
4112 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4113 else if (tm
.tm_hour
> 12)
4114 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4116 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4117 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4119 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4124 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char * ) NULL
);
4126 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4130 if (tm
.tm_min
== 0) {
4133 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char * ) NULL
);
4138 if (tm
.tm_hour
> 11)
4139 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4141 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4142 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4145 /* Shorthand for "Today", "Yesterday", or AdBY */
4146 /* XXX As emphasized elsewhere, this should the native way in your
4147 * language to say the date, with changes in what you say, depending
4148 * upon how recent the date is. XXX */
4152 time_t beg_today
, tt
;
4154 gettimeofday(&now
,NULL
);
4156 ast_localtime(&tt
,&tmnow
,timezone
);
4157 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4158 /* In any case, it saves not having to do ast_mktime() */
4159 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4160 if (beg_today
< time
) {
4162 res
= wait_file(chan
,ints
, "digits/today",lang
);
4163 } else if (beg_today
- 86400 < time
) {
4165 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4167 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4172 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4173 /* XXX As emphasized elsewhere, this should the native way in your
4174 * language to say the date, with changes in what you say, depending
4175 * upon how recent the date is. XXX */
4179 time_t beg_today
, tt
;
4181 gettimeofday(&now
,NULL
);
4183 ast_localtime(&tt
,&tmnow
,timezone
);
4184 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4185 /* In any case, it saves not having to do ast_mktime() */
4186 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4187 if (beg_today
< time
) {
4189 } else if ((beg_today
- 86400) < time
) {
4191 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4192 } else if (beg_today
- 86400 * 6 < time
) {
4193 /* Within the last week */
4194 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "A", timezone
);
4196 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4201 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HM", timezone
);
4205 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char * ) NULL
);
4207 res
= wait_file(chan
,ints
, "digits/second",lang
);
4211 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HMS", timezone
);
4215 /* Just ignore spaces and tabs */
4218 /* Unknown character */
4219 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4221 /* Jump out on DTMF */
4229 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
)
4232 int res
=0, offset
, sndoffset
;
4233 char sndfile
[256], nextmsg
[256];
4236 format
= "AdB 'digits/at' IMp";
4238 ast_localtime(&time
,&tm
,timezone
);
4240 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4241 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4242 switch (format
[offset
]) {
4243 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4245 /* Literal name of a sound file */
4247 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4248 sndfile
[sndoffset
] = format
[offset
];
4249 sndfile
[sndoffset
] = '\0';
4250 res
= wait_file(chan
,ints
,sndfile
,lang
);
4254 /* Sunday - Saturday */
4255 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4256 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4261 /* January - December */
4262 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4263 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4266 /* First - Twelfth */
4267 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4268 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4272 /* First day of the month is spelled as ordinal */
4273 if (tm
.tm_mday
== 1) {
4274 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4275 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4278 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4284 if (tm
.tm_year
> 99) {
4285 res
= wait_file(chan
,ints
, "digits/ore-2000",lang
);
4286 if (tm
.tm_year
> 100) {
4288 /* This works until the end of 2021 */
4289 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4290 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4294 if (tm
.tm_year
< 1) {
4295 /* I'm not going to handle 1900 and prior */
4296 /* We'll just be silent on the year, instead of bombing out. */
4298 res
= wait_file(chan
,ints
, "digits/ore-1900",lang
);
4299 if ((!res
) && (tm
.tm_year
!= 0)) {
4300 if (tm
.tm_year
<= 21) {
4302 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4303 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4305 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4307 ten
= tm
.tm_year
/ 10;
4308 one
= tm
.tm_year
% 10;
4309 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4310 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4313 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4314 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4325 if (tm
.tm_hour
== 0)
4326 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4327 else if (tm
.tm_hour
> 12)
4328 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4330 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4331 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4336 if (tm
.tm_hour
== 0) {
4337 res
= wait_file(chan
,ints
, "digits/ore-mezzanotte",lang
);
4338 } else if (tm
.tm_hour
== 1) {
4339 res
= wait_file(chan
,ints
, "digits/ore-una",lang
);
4341 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4346 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4351 if (tm
.tm_hour
> 11)
4352 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4354 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4355 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4358 /* Shorthand for "Today", "Yesterday", or ABdY */
4359 /* XXX As emphasized elsewhere, this should the native way in your
4360 * language to say the date, with changes in what you say, depending
4361 * upon how recent the date is. XXX */
4365 time_t beg_today
, tt
;
4367 gettimeofday(&now
,NULL
);
4369 ast_localtime(&tt
,&tmnow
,timezone
);
4370 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4371 /* In any case, it saves not having to do ast_mktime() */
4372 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4373 if (beg_today
< time
) {
4375 res
= wait_file(chan
,ints
, "digits/today",lang
);
4376 } else if (beg_today
- 86400 < time
) {
4378 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4380 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4385 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4389 time_t beg_today
, tt
;
4391 gettimeofday(&now
,NULL
);
4393 ast_localtime(&tt
,&tmnow
,timezone
);
4394 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4395 /* In any case, it saves not having to do ast_mktime() */
4396 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4397 if (beg_today
< time
) {
4399 } else if ((beg_today
- 86400) < time
) {
4401 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4402 } else if (beg_today
- 86400 * 6 < time
) {
4403 /* Within the last week */
4404 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "A", timezone
);
4406 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4411 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HM", timezone
);
4415 if (tm
.tm_sec
== 0) {
4416 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4417 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4418 } else if (tm
.tm_sec
< 10) {
4419 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4421 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4422 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4424 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
4425 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4426 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4429 ten
= (tm
.tm_sec
/ 10) * 10;
4430 one
= (tm
.tm_sec
% 10);
4431 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
4432 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4434 /* Fifty, not fifty-zero */
4436 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4437 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4443 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HMS", timezone
);
4447 /* Just ignore spaces and tabs */
4450 /* Unknown character */
4451 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4453 /* Jump out on DTMF */
4462 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
)
4465 int res
=0, offset
, sndoffset
;
4466 char sndfile
[256], nextmsg
[256];
4469 format
= "ABdY 'digits/at' IMp";
4471 ast_localtime(&time
,&tm
,timezone
);
4473 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4474 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4475 switch (format
[offset
]) {
4476 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4478 /* Literal name of a sound file */
4480 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4481 sndfile
[sndoffset
] = format
[offset
];
4482 sndfile
[sndoffset
] = '\0';
4483 res
= wait_file(chan
,ints
,sndfile
,lang
);
4487 /* Sunday - Saturday */
4488 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4489 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4494 /* January - December */
4495 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4496 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4499 /* First - Twelfth */
4500 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4501 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4505 /* First - Thirtyfirst */
4506 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, NULL
);
4510 if (tm
.tm_year
> 99) {
4511 res
= wait_file(chan
,ints
, "digits/2",lang
);
4513 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4515 if (tm
.tm_year
> 100) {
4517 /* This works until the end of 2020 */
4518 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4519 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4523 if (tm
.tm_year
< 1) {
4524 /* I'm not going to handle 1900 and prior */
4525 /* We'll just be silent on the year, instead of bombing out. */
4527 res
= wait_file(chan
,ints
, "digits/19",lang
);
4529 if (tm
.tm_year
<= 9) {
4531 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4533 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4534 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4536 } else if (tm
.tm_year
<= 20) {
4538 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4539 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4543 ten
= tm
.tm_year
/ 10;
4544 one
= tm
.tm_year
% 10;
4545 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4546 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4549 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4550 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4561 if (tm
.tm_hour
== 0)
4562 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4563 else if (tm
.tm_hour
> 12)
4564 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4566 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4567 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4572 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4574 res
= wait_file(chan
,ints
, "digits/nl-uur",lang
);
4579 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4584 if (tm
.tm_hour
> 11)
4585 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4587 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4588 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4591 /* Shorthand for "Today", "Yesterday", or ABdY */
4592 /* XXX As emphasized elsewhere, this should the native way in your
4593 * language to say the date, with changes in what you say, depending
4594 * upon how recent the date is. XXX */
4598 time_t beg_today
, tt
;
4600 gettimeofday(&now
,NULL
);
4602 ast_localtime(&tt
,&tmnow
,timezone
);
4603 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4604 /* In any case, it saves not having to do ast_mktime() */
4605 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4606 if (beg_today
< time
) {
4608 res
= wait_file(chan
,ints
, "digits/today",lang
);
4609 } else if (beg_today
- 86400 < time
) {
4611 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4613 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4618 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4622 time_t beg_today
, tt
;
4624 gettimeofday(&now
,NULL
);
4626 ast_localtime(&tt
,&tmnow
,timezone
);
4627 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4628 /* In any case, it saves not having to do ast_mktime() */
4629 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4630 if (beg_today
< time
) {
4632 } else if ((beg_today
- 86400) < time
) {
4634 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4635 } else if (beg_today
- 86400 * 6 < time
) {
4636 /* Within the last week */
4637 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "A", timezone
);
4639 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4644 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HM", timezone
);
4648 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, (char *) NULL
);
4651 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HMS", timezone
);
4655 /* Just ignore spaces and tabs */
4658 /* Unknown character */
4659 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4661 /* Jump out on DTMF */
4670 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
)
4673 int res
=0, offset
, sndoffset
;
4674 char sndfile
[256], nextmsg
[256];
4676 ast_localtime(&thetime
, &tm
, timezone
);
4678 for (offset
= 0 ; format
[offset
] != '\0' ; offset
++) {
4680 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4681 switch (format
[offset
]) {
4682 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4684 /* Literal name of a sound file */
4686 for (sndoffset
= 0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4687 sndfile
[sndoffset
] = format
[offset
];
4688 sndfile
[sndoffset
] = '\0';
4689 res
= wait_file(chan
, ints
, sndfile
, lang
);
4693 /* Sunday - Saturday */
4694 snprintf(nextmsg
, sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4695 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4700 /* January - December */
4701 snprintf(nextmsg
, sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4702 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4705 /* Month enumerated */
4706 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, NULL
);
4710 /* First - Thirtyfirst */
4711 remainder
= tm
.tm_mday
;
4712 if (tm
.tm_mday
> 30) {
4713 res
= wait_file(chan
, ints
, "digits/h-30", lang
);
4716 if (tm
.tm_mday
> 20 && tm
.tm_mday
< 30) {
4717 res
= wait_file(chan
, ints
, "digits/h-20", lang
);
4721 snprintf(nextmsg
, sizeof(nextmsg
), "digits/h-%d", remainder
);
4722 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4727 if (tm
.tm_year
> 100) {
4728 res
= wait_file(chan
, ints
, "digits/2", lang
);
4730 res
= wait_file(chan
, ints
, "digits/1000.2",lang
);
4731 if (tm
.tm_year
> 100) {
4733 res
= ast_say_enumeration(chan
, tm
.tm_year
- 100, ints
, lang
, NULL
);
4735 } else if (tm
.tm_year
== 100) {
4736 res
= wait_file(chan
, ints
, "digits/h-2000", lang
);
4738 if (tm
.tm_year
< 1) {
4739 /* I'm not going to handle 1900 and prior */
4740 /* We'll just be silent on the year, instead of bombing out. */
4743 res
= wait_file(chan
, ints
, "digits/1000", lang
);
4745 wait_file(chan
, ints
, "digits/900", lang
);
4746 res
= ast_say_enumeration(chan
, tm
.tm_year
, ints
, lang
, NULL
);
4751 wait_file(chan
, ints
, "digits/year", lang
);
4756 if (tm
.tm_hour
== 0)
4757 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-12");
4758 else if (tm
.tm_hour
> 12)
4759 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
- 12);
4761 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4763 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4768 if (tm
.tm_hour
!= 0) {
4769 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4770 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4772 res
= wait_file(chan
, ints
, "digits/t-24", lang
);
4777 if (tm
.tm_min
== 0) {
4778 if (format
[offset
] == 'M') {
4779 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
4781 res
= wait_file(chan
, ints
, "digits/100", lang
);
4784 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4789 if (tm
.tm_hour
> 11)
4790 snprintf(nextmsg
, sizeof(nextmsg
), "digits/p-m");
4792 snprintf(nextmsg
, sizeof(nextmsg
), "digits/a-m");
4793 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4796 /* Shorthand for "Today", "Yesterday", or AdBY */
4798 time_t tv_sec
= time(NULL
);
4802 ast_localtime(&tv_sec
,&tmnow
, timezone
);
4803 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4804 /* In any case, it saves not having to do ast_mktime() */
4805 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4806 if (beg_today
< thetime
) {
4808 res
= wait_file(chan
, ints
, "digits/today", lang
);
4809 } else if (beg_today
- 86400 < thetime
) {
4811 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4813 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4818 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4820 time_t tv_sec
= time(NULL
);
4824 ast_localtime(&tv_sec
, &tmnow
, timezone
);
4825 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4826 /* In any case, it saves not having to do ast_mktime() */
4827 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4828 if (beg_today
< thetime
) {
4830 } else if ((beg_today
- 86400) < thetime
) {
4832 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4833 } else if (beg_today
- 86400 * 6 < thetime
) {
4834 /* Within the last week */
4835 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "A", timezone
);
4837 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4842 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HM", timezone
);
4846 res
= wait_file(chan
, ints
, "digits/and", lang
);
4848 if (tm
.tm_sec
== 1) {
4849 res
= wait_file(chan
, ints
, "digits/1z", lang
);
4851 res
= wait_file(chan
, ints
, "digits/second-a", lang
);
4853 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4856 ten
= tm
.tm_sec
/ 10;
4857 one
= tm
.tm_sec
% 10;
4859 if (one
> 1 && one
< 5 && ten
!= 1)
4860 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
4862 res
= wait_file(chan
,ints
, "digits/second",lang
);
4868 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HMS", timezone
);
4872 /* Just ignore spaces and tabs */
4875 /* Unknown character */
4876 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4878 /* Jump out on DTMF */
4885 /* Portuguese syntax */
4886 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
)
4889 int res
=0, offset
, sndoffset
;
4890 char sndfile
[256], nextmsg
[256];
4893 format
= "Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/at' IMp";
4895 ast_localtime(&time
,&tm
,timezone
);
4897 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4898 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4899 switch (format
[offset
]) {
4900 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4902 /* Literal name of a sound file */
4904 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4905 sndfile
[sndoffset
] = format
[offset
];
4906 sndfile
[sndoffset
] = '\0';
4907 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
4908 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4912 /* Sunday - Saturday */
4913 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4914 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4919 /* January - December */
4920 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4921 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4924 /* First - Twelfth */
4925 if (!strcasecmp(lang
, "pt_BR")) {
4926 res
= ast_say_number(chan
, tm
.tm_mon
+1, ints
, lang
, (char *) NULL
);
4928 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4929 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4934 /* First - Thirtyfirst */
4935 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4939 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
4944 if (!strcasecmp(lang
, "pt_BR")) {
4945 if (tm
.tm_hour
== 0) {
4946 if (format
[offset
] == 'I')
4947 res
= wait_file(chan
, ints
, "digits/pt-a", lang
);
4949 res
= wait_file(chan
, ints
, "digits/pt-meianoite", lang
);
4950 } else if (tm
.tm_hour
== 12) {
4951 if (format
[offset
] == 'I')
4952 res
= wait_file(chan
, ints
, "digits/pt-ao", lang
);
4954 res
= wait_file(chan
, ints
, "digits/pt-meiodia", lang
);
4956 if (format
[offset
] == 'I') {
4957 if ((tm
.tm_hour
% 12) != 1)
4958 res
= wait_file(chan
, ints
, "digits/pt-as", lang
);
4960 res
= wait_file(chan
, ints
, "digits/pt-a", lang
);
4963 res
= ast_say_number(chan
, (tm
.tm_hour
% 12), ints
, lang
, "f");
4964 if ((!res
) && (format
[offset
] == 'I'))
4965 res
= ast_say_date_with_format(chan
, time
, ints
, lang
, "P", timezone
);
4968 if (tm
.tm_hour
== 0) {
4969 if (format
[offset
] == 'I')
4970 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
4972 res
= wait_file(chan
, ints
, "digits/pt-meianoite", lang
);
4974 else if (tm
.tm_hour
== 12) {
4975 if (format
[offset
] == 'I')
4976 res
= wait_file(chan
, ints
, "digits/pt-ao", lang
);
4978 res
= wait_file(chan
, ints
, "digits/pt-meiodia", lang
);
4981 if (format
[offset
] == 'I') {
4982 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
4983 if ((tm
.tm_hour
% 12) != 1)
4985 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
4988 res
= ast_say_number(chan
, (tm
.tm_hour
% 12), ints
, lang
, "f");
4995 if (!strcasecmp(lang
, "pt_BR")) {
4996 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
4997 if ((!res
) && (format
[offset
] == 'H')) {
4998 if (tm
.tm_hour
> 1) {
4999 res
= wait_file(chan
,ints
,"digits/hours",lang
);
5001 res
= wait_file(chan
,ints
,"digits/hour",lang
);
5005 res
= ast_say_number(chan
, -tm
.tm_hour
, ints
, lang
, NULL
);
5007 if (tm
.tm_hour
!= 0) {
5008 int remainder
= tm
.tm_hour
;
5009 if (tm
.tm_hour
> 20) {
5010 res
= wait_file(chan
,ints
, "digits/20",lang
);
5014 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
5015 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5023 if (!strcasecmp(lang
, "pt_BR")) {
5024 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, NULL
);
5026 if (tm
.tm_min
> 1) {
5027 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
5029 res
= wait_file(chan
,ints
,"digits/minute",lang
);
5033 if (tm
.tm_min
== 0) {
5034 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
5035 if (tm
.tm_hour
!= 1)
5037 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
); } else {
5038 res
= wait_file(chan
,ints
,"digits/pt-e",lang
);
5040 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5047 if (!strcasecmp(lang
, "pt_BR")) {
5048 if ((tm
.tm_hour
!= 0) && (tm
.tm_hour
!= 12)) {
5049 res
= wait_file(chan
, ints
, "digits/pt-da", lang
);
5051 if ((tm
.tm_hour
>= 0) && (tm
.tm_hour
< 12))
5052 res
= wait_file(chan
, ints
, "digits/morning", lang
);
5053 else if ((tm
.tm_hour
>= 12) && (tm
.tm_hour
< 18))
5054 res
= wait_file(chan
, ints
, "digits/afternoon", lang
);
5055 else res
= wait_file(chan
, ints
, "digits/night", lang
);
5059 if (tm
.tm_hour
> 12)
5060 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
5061 else if (tm
.tm_hour
&& tm
.tm_hour
< 12)
5062 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
5066 /* Shorthand for "Today", "Yesterday", or ABdY */
5067 /* XXX As emphasized elsewhere, this should the native way in your
5068 * language to say the date, with changes in what you say, depending
5069 * upon how recent the date is. XXX */
5073 time_t beg_today
, tt
;
5075 gettimeofday(&now
,NULL
);
5077 ast_localtime(&tt
,&tmnow
,timezone
);
5078 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5079 /* In any case, it saves not having to do ast_mktime() */
5080 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5081 if (beg_today
< time
) {
5083 res
= wait_file(chan
,ints
, "digits/today",lang
);
5084 } else if (beg_today
- 86400 < time
) {
5086 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5088 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5093 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5094 /* XXX As emphasized elsewhere, this should the native way in your
5095 * language to say the date, with changes in what you say, depending
5096 * upon how recent the date is. XXX */
5100 time_t beg_today
, tt
;
5102 gettimeofday(&now
,NULL
);
5104 ast_localtime(&tt
,&tmnow
,timezone
);
5105 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5106 /* In any case, it saves not having to do ast_mktime() */
5107 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5108 if (beg_today
< time
) {
5110 } else if ((beg_today
- 86400) < time
) {
5112 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5113 } else if (beg_today
- 86400 * 6 < time
) {
5114 /* Within the last week */
5115 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "A", timezone
);
5117 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5122 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "H 'digits/pt-e' M", timezone
);
5126 if (!strcasecmp(lang
, "pt_BR")) {
5127 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, NULL
);
5129 if (tm
.tm_sec
> 1) {
5130 res
= wait_file(chan
,ints
,"digits/seconds",lang
);
5132 res
= wait_file(chan
,ints
,"digits/second",lang
);
5134 } else if (tm
.tm_sec
< 10) {
5135 res
= wait_file(chan
,ints
, "digits/oh",lang
);
5137 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5138 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5140 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
5141 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5142 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5145 ten
= (tm
.tm_sec
/ 10) * 10;
5146 one
= (tm
.tm_sec
% 10);
5147 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
5148 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5150 /* Fifty, not fifty-zero */
5152 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
5153 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5160 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "HMS", timezone
);
5164 /* Just ignore spaces and tabs */
5167 /* Unknown character */
5168 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5170 /* Jump out on DTMF */
5178 /* Taiwanese / Chinese syntax */
5179 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
)
5182 int res
=0, offset
, sndoffset
;
5183 char sndfile
[256], nextmsg
[256];
5188 ast_localtime(&time
,&tm
,timezone
);
5190 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
5191 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
5192 switch (format
[offset
]) {
5193 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5195 /* Literal name of a sound file */
5197 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
5198 sndfile
[sndoffset
] = format
[offset
];
5199 sndfile
[sndoffset
] = '\0';
5200 res
= wait_file(chan
,ints
,sndfile
,lang
);
5204 /* Sunday - Saturday */
5205 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
5206 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5211 /* January - December */
5212 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
5213 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5216 /* First - Twelfth */
5217 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
5218 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5222 /* First - Thirtyfirst */
5223 if (!(tm
.tm_mday
% 10) || (tm
.tm_mday
< 10)) {
5224 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_mday
);
5225 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5227 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_mday
- (tm
.tm_mday
% 10));
5228 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5230 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_mday
% 10);
5231 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5234 if(!res
) res
= wait_file(chan
,ints
,"ri",lang
);
5238 if (tm
.tm_year
> 99) {
5239 res
= wait_file(chan
,ints
, "digits/2",lang
);
5241 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
5243 if (tm
.tm_year
> 100) {
5245 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) / 10);
5246 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5248 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) % 10);
5249 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5254 res
= wait_file(chan
,ints
, "digits/year",lang
);
5257 if (tm
.tm_year
< 1) {
5258 /* I'm not going to handle 1900 and prior */
5259 /* We'll just be silent on the year, instead of bombing out. */
5261 res
= wait_file(chan
,ints
, "digits/1",lang
);
5263 res
= wait_file(chan
,ints
, "digits/9",lang
);
5266 if (tm
.tm_year
<= 9) {
5268 res
= wait_file(chan
,ints
, "digits/0",lang
);
5270 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
5271 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5275 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
/ 10);
5276 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5278 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
% 10);
5279 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5285 res
= wait_file(chan
,ints
, "digits/year",lang
);
5292 if (tm
.tm_hour
== 0)
5293 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
5294 else if (tm
.tm_hour
> 12)
5295 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
5297 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5298 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5300 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5304 if (tm
.tm_hour
< 10) {
5305 res
= wait_file(chan
, ints
, "digits/0", lang
);
5309 if (!(tm
.tm_hour
% 10) || tm
.tm_hour
< 10) {
5310 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5311 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5313 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- (tm
.tm_hour
% 10));
5314 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5316 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
% 10);
5317 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5321 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5326 if (!(tm
.tm_min
% 10) || tm
.tm_min
< 10) {
5327 if (tm
.tm_min
< 10) {
5328 res
= wait_file(chan
, ints
, "digits/0", lang
);
5330 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
5331 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5333 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
- (tm
.tm_min
% 10));
5334 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5336 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
% 10);
5337 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5341 res
= wait_file(chan
,ints
, "digits/minute",lang
);
5347 if (tm
.tm_hour
> 11)
5348 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
5350 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
5351 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5354 /* Shorthand for "Today", "Yesterday", or ABdY */
5355 /* XXX As emphasized elsewhere, this should the native way in your
5356 * language to say the date, with changes in what you say, depending
5357 * upon how recent the date is. XXX */
5361 time_t beg_today
, tt
;
5363 gettimeofday(&now
,NULL
);
5365 ast_localtime(&tt
,&tmnow
,timezone
);
5366 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5367 /* In any case, it saves not having to do ast_mktime() */
5368 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5369 if (beg_today
< time
) {
5371 res
= wait_file(chan
,ints
, "digits/today",lang
);
5372 } else if (beg_today
- 86400 < time
) {
5374 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5376 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5381 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5382 /* XXX As emphasized elsewhere, this should the native way in your
5383 * language to say the date, with changes in what you say, depending
5384 * upon how recent the date is. XXX */
5388 time_t beg_today
, tt
;
5390 gettimeofday(&now
,NULL
);
5392 ast_localtime(&tt
,&tmnow
,timezone
);
5393 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5394 /* In any case, it saves not having to do ast_mktime() */
5395 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5396 if (beg_today
< time
) {
5398 } else if ((beg_today
- 86400) < time
) {
5400 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5401 } else if (beg_today
- 86400 * 6 < time
) {
5402 /* Within the last week */
5403 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "A", timezone
);
5405 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5410 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "kM", timezone
);
5414 if (!(tm
.tm_sec
% 10) || tm
.tm_sec
< 10) {
5415 if (tm
.tm_sec
< 10) {
5416 res
= wait_file(chan
, ints
, "digits/0", lang
);
5418 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5419 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5421 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
- (tm
.tm_sec
% 10));
5422 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5424 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
% 10);
5425 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5429 res
= wait_file(chan
,ints
, "digits/second",lang
);
5433 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "HMS", timezone
);
5437 /* Just ignore spaces and tabs */
5440 /* Unknown character */
5441 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5443 /* Jump out on DTMF */
5451 static int say_time(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5453 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5454 return(ast_say_time_en(chan
, t
, ints
, lang
));
5455 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5456 return(ast_say_time_de(chan
, t
, ints
, lang
));
5457 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5458 return(ast_say_time_fr(chan
, t
, ints
, lang
));
5459 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5460 return(ast_say_time_nl(chan
, t
, ints
, lang
));
5461 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5462 return(ast_say_time_pt(chan
, t
, ints
, lang
));
5463 } else if (!strcasecmp(lang
, "pt_BR") ) { /* Brazilian Portuguese syntax */
5464 return(ast_say_time_pt_BR(chan
, t
, ints
, lang
));
5465 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5466 return(ast_say_time_tw(chan
, t
, ints
, lang
));
5467 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5468 return(ast_say_time_gr(chan
, t
, ints
, lang
));
5469 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
5470 return(ast_say_time_ge(chan
, t
, ints
, lang
));
5473 /* Default to English */
5474 return(ast_say_time_en(chan
, t
, ints
, lang
));
5477 /* English syntax */
5478 int ast_say_time_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5484 ast_localtime(&t
, &tm
, NULL
);
5488 else if (hour
== 12)
5490 else if (hour
> 12) {
5495 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5497 if (tm
.tm_min
> 9) {
5499 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5500 } else if (tm
.tm_min
) {
5502 res
= ast_streamfile(chan
, "digits/oh", lang
);
5504 res
= ast_waitstream(chan
, ints
);
5506 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5509 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5511 res
= ast_waitstream(chan
, ints
);
5515 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5518 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5521 res
= ast_waitstream(chan
, ints
);
5526 int ast_say_time_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5531 ast_localtime(&t
, &tm
, NULL
);
5533 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "n");
5535 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5537 res
= ast_waitstream(chan
, ints
);
5540 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
5545 int ast_say_time_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5550 ast_localtime(&t
, &tm
, NULL
);
5552 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5554 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5557 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5563 int ast_say_time_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5568 ast_localtime(&t
, &tm
, NULL
);
5570 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
5572 res
= ast_streamfile(chan
, "digits/nl-uur", lang
);
5574 res
= ast_waitstream(chan
, ints
);
5577 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, NULL
);
5581 /* Portuguese syntax */
5582 int ast_say_time_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5588 ast_localtime(&t
, &tm
, NULL
);
5591 res
= ast_say_number(chan
, hour
, ints
, lang
, "f");
5594 res
= wait_file(chan
, ints
, "digits/pt-e", lang
);
5596 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5599 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
5600 if (tm
.tm_hour
!= 1)
5602 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5605 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5609 /* Brazilian Portuguese syntax */
5610 int ast_say_time_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5615 ast_localtime(&t
, &tm
, NULL
);
5617 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5620 res
= wait_file(chan
, ints
, "digits/hours", lang
);
5622 res
= wait_file(chan
, ints
, "digits/hour", lang
);
5624 if ((!res
) && (tm
.tm_min
)) {
5625 res
= wait_file(chan
, ints
, "digits/pt-e", lang
);
5627 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5630 res
= wait_file(chan
, ints
, "digits/minutes", lang
);
5632 res
= wait_file(chan
, ints
, "digits/minute", lang
);
5638 /* Taiwanese / Chinese syntax */
5639 int ast_say_time_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5645 ast_localtime(&t
, &tm
, NULL
);
5649 else if (hour
== 12)
5651 else if (hour
> 12) {
5657 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5660 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5663 res
= ast_waitstream(chan
, ints
);
5665 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5667 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5669 res
= ast_waitstream(chan
, ints
);
5671 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5673 res
= ast_streamfile(chan
, "digits/minute", lang
);
5675 res
= ast_waitstream(chan
, ints
);
5679 static int say_datetime(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5681 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5682 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5683 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5684 return(ast_say_datetime_de(chan
, t
, ints
, lang
));
5685 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5686 return(ast_say_datetime_fr(chan
, t
, ints
, lang
));
5687 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5688 return(ast_say_datetime_nl(chan
, t
, ints
, lang
));
5689 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5690 return(ast_say_datetime_pt(chan
, t
, ints
, lang
));
5691 } else if (!strcasecmp(lang
, "pt_BR") ) { /* Brazilian Portuguese syntax */
5692 return(ast_say_datetime_pt_BR(chan
, t
, ints
, lang
));
5693 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5694 return(ast_say_datetime_tw(chan
, t
, ints
, lang
));
5695 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5696 return(ast_say_datetime_gr(chan
, t
, ints
, lang
));
5697 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
5698 return(ast_say_datetime_ge(chan
, t
, ints
, lang
));
5701 /* Default to English */
5702 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5705 /* English syntax */
5706 int ast_say_datetime_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5713 ast_localtime(&t
, &tm
, NULL
);
5715 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5716 res
= ast_streamfile(chan
, fn
, lang
);
5718 res
= ast_waitstream(chan
, ints
);
5721 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5722 res
= ast_streamfile(chan
, fn
, lang
);
5724 res
= ast_waitstream(chan
, ints
);
5727 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5732 else if (hour
== 12)
5734 else if (hour
> 12) {
5739 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5741 if (tm
.tm_min
> 9) {
5743 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5744 } else if (tm
.tm_min
) {
5746 res
= ast_streamfile(chan
, "digits/oh", lang
);
5748 res
= ast_waitstream(chan
, ints
);
5750 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5753 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5755 res
= ast_waitstream(chan
, ints
);
5759 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5762 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5765 res
= ast_waitstream(chan
, ints
);
5767 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5772 int ast_say_datetime_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5777 ast_localtime(&t
, &tm
, NULL
);
5778 res
= ast_say_date(chan
, t
, ints
, lang
);
5780 ast_say_time(chan
, t
, ints
, lang
);
5786 int ast_say_datetime_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5792 ast_localtime(&t
, &tm
, NULL
);
5795 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5798 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5799 res
= ast_streamfile(chan
, fn
, lang
);
5801 res
= ast_waitstream(chan
, ints
);
5804 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5805 res
= ast_streamfile(chan
, fn
, lang
);
5807 res
= ast_waitstream(chan
, ints
);
5811 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5813 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5814 if (tm
.tm_min
> 0) {
5816 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5819 res
= ast_waitstream(chan
, ints
);
5821 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5826 int ast_say_datetime_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5831 ast_localtime(&t
, &tm
, NULL
);
5832 res
= ast_say_date(chan
, t
, ints
, lang
);
5834 res
= ast_streamfile(chan
, "digits/nl-om", lang
);
5836 res
= ast_waitstream(chan
, ints
);
5839 ast_say_time(chan
, t
, ints
, lang
);
5843 /* Portuguese syntax */
5844 int ast_say_datetime_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5851 ast_localtime(&t
, &tm
, NULL
);
5853 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5854 res
= ast_streamfile(chan
, fn
, lang
);
5856 res
= ast_waitstream(chan
, ints
);
5859 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5860 res
= ast_streamfile(chan
, fn
, lang
);
5862 res
= ast_waitstream(chan
, ints
);
5865 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5870 else if (hour
== 12)
5872 else if (hour
> 12) {
5877 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5879 if (tm
.tm_min
> 9) {
5881 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5882 } else if (tm
.tm_min
) {
5884 res
= ast_streamfile(chan
, "digits/oh", lang
);
5886 res
= ast_waitstream(chan
, ints
);
5888 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5891 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5893 res
= ast_waitstream(chan
, ints
);
5897 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5900 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5903 res
= ast_waitstream(chan
, ints
);
5905 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5909 /* Brazilian Portuguese syntax */
5910 int ast_say_datetime_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5915 ast_localtime(&t
, &tm
, NULL
);
5916 res
= ast_say_date(chan
, t
, ints
, lang
);
5918 res
= ast_say_time(chan
, t
, ints
, lang
);
5922 /* Taiwanese / Chinese syntax */
5923 int ast_say_datetime_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5930 ast_localtime(&t
, &tm
, NULL
);
5932 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5934 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5935 res
= ast_streamfile(chan
, fn
, lang
);
5937 res
= ast_waitstream(chan
, ints
);
5940 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5942 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5943 res
= ast_streamfile(chan
, fn
, lang
);
5945 res
= ast_waitstream(chan
, ints
);
5951 else if (hour
== 12)
5953 else if (hour
> 12) {
5959 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5962 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5965 res
= ast_waitstream(chan
, ints
);
5967 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5969 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5971 res
= ast_waitstream(chan
, ints
);
5973 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5975 res
= ast_streamfile(chan
, "digits/minute", lang
);
5977 res
= ast_waitstream(chan
, ints
);
5981 static int say_datetime_from_now(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5983 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5984 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
5985 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5986 return(ast_say_datetime_from_now_fr(chan
, t
, ints
, lang
));
5987 } else if (!strcasecmp(lang
, "pt") || !strcasecmp(lang
, "pt_BR")) { /* Portuguese syntax */
5988 return(ast_say_datetime_from_now_pt(chan
, t
, ints
, lang
));
5989 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
5990 return(ast_say_datetime_from_now_ge(chan
, t
, ints
, lang
));
5993 /* Default to English */
5994 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
5997 /* English syntax */
5998 int ast_say_datetime_from_now_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6009 ast_localtime(&t
, &tm
, NULL
);
6010 ast_localtime(&nowt
,&now
, NULL
);
6011 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6012 if ((daydiff
< 0) || (daydiff
> 6)) {
6013 /* Day of month and month */
6015 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6016 res
= ast_streamfile(chan
, fn
, lang
);
6018 res
= ast_waitstream(chan
, ints
);
6021 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6023 } else if (daydiff
) {
6024 /* Just what day of the week */
6026 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6027 res
= ast_streamfile(chan
, fn
, lang
);
6029 res
= ast_waitstream(chan
, ints
);
6031 } /* Otherwise, it was today */
6033 res
= ast_say_time(chan
, t
, ints
, lang
);
6038 int ast_say_datetime_from_now_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6049 ast_localtime(&t
, &tm
, NULL
);
6050 ast_localtime(&nowt
, &now
, NULL
);
6051 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6052 if ((daydiff
< 0) || (daydiff
> 6)) {
6053 /* Day of month and month */
6055 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6056 res
= ast_streamfile(chan
, fn
, lang
);
6058 res
= ast_waitstream(chan
, ints
);
6061 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6063 } else if (daydiff
) {
6064 /* Just what day of the week */
6066 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6067 res
= ast_streamfile(chan
, fn
, lang
);
6069 res
= ast_waitstream(chan
, ints
);
6071 } /* Otherwise, it was today */
6073 res
= ast_say_time(chan
, t
, ints
, lang
);
6077 /* Portuguese syntax */
6078 int ast_say_datetime_from_now_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6089 ast_localtime(&t
, &tm
, NULL
);
6090 ast_localtime(&nowt
, &now
, NULL
);
6091 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6092 if ((daydiff
< 0) || (daydiff
> 6)) {
6093 /* Day of month and month */
6095 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6097 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
6098 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6100 res
= wait_file(chan
, ints
, fn
, lang
);
6102 } else if (daydiff
) {
6103 /* Just what day of the week */
6104 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6106 res
= wait_file(chan
, ints
, fn
, lang
);
6107 } /* Otherwise, it was today */
6108 if (!strcasecmp(lang
, "pt_BR")) {
6109 if (tm
.tm_hour
> 1) {
6110 snprintf(fn
, sizeof(fn
), "digits/pt-as");
6112 snprintf(fn
, sizeof(fn
), "digits/pt-a");
6115 res
= wait_file(chan
, ints
, fn
, lang
);
6117 snprintf(fn
, sizeof(fn
), "digits/pt-ah");
6119 res
= wait_file(chan
, ints
, fn
, lang
);
6120 if (tm
.tm_hour
!= 1)
6122 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
6124 res
= ast_say_time(chan
, t
, ints
, lang
);
6130 /*********************************** GREEK SUPPORT ***************************************/
6134 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
6136 static int gr_say_number_female(int num
, struct ast_channel
*chan
, const char *ints
, const char *lang
){
6142 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
6144 snprintf(fn
, sizeof(fn
), "digits/female-%d", num
);
6145 res
= wait_file(chan
, ints
, fn
, lang
);
6146 } else if (num
< 13) {
6147 res
= ast_say_number(chan
, num
, ints
, lang
, (char *) NULL
);
6148 } else if (num
<100 ) {
6149 tmp
= (num
/10) * 10;
6151 snprintf(fn
, sizeof(fn
), "digits/%d", tmp
);
6152 res
= ast_streamfile(chan
, fn
, lang
);
6154 res
= ast_waitstream(chan
, ints
);
6156 gr_say_number_female(left
, chan
, ints
, lang
);
6167 * A list of the files that you need to create
6168 -> digits/xilia = "xilia"
6169 -> digits/myrio = "ekatomyrio"
6170 -> digits/thousands = "xiliades"
6171 -> digits/millions = "ektatomyria"
6172 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6173 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6175 Here we must note that we use digits/tens/100 to utter "ekato"
6176 and digits/hundred-100 to utter "ekaton"
6177 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6178 "terakosia". Here again we use hundreds/1000 for "xilia"
6179 and digits/thousnds for "xiliades"
6182 static int ast_say_number_full_gr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
,int audiofd
, int ctrlfd
)
6190 snprintf(fn
, sizeof(fn
), "digits/0");
6191 res
= ast_streamfile(chan
, fn
, chan
->language
);
6193 return ast_waitstream(chan
, ints
);
6196 while(!res
&& num
) {
6199 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
6201 } else if (num
<= 100) {
6202 /* 13 < num <= 100 */
6203 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
6204 num
-= ((num
/ 10) * 10);
6205 } else if (num
< 200) {
6206 /* 100 < num < 200 */
6207 snprintf(fn
, sizeof(fn
), "digits/hundred-100");
6208 num
-= ((num
/ 100) * 100);
6209 }else if (num
< 1000) {
6210 /* 200 < num < 1000 */
6211 snprintf(fn
, sizeof(fn
), "digits/hundred-%d", (num
/100)*100);
6212 num
-= ((num
/ 100) * 100);
6213 }else if (num
< 2000){
6214 snprintf(fn
, sizeof(fn
), "digits/xilia");
6215 num
-= ((num
/ 1000) * 1000);
6219 if (num
< 1000000) {
6220 res
= ast_say_number_full_gr(chan
, (num
/ 1000), ints
, chan
->language
, audiofd
, ctrlfd
);
6224 snprintf(fn
, sizeof(fn
), "digits/thousands");
6226 if (num
< 1000000000) { /* 1,000,000,000 */
6227 res
= ast_say_number_full_gr(chan
, (num
/ 1000000), ints
, chan
->language
,audiofd
, ctrlfd
);
6230 num
= num
% 1000000;
6231 snprintf(fn
, sizeof(fn
), "digits/millions");
6233 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
6239 if(!ast_streamfile(chan
, fn
, language
)) {
6240 if ((audiofd
> -1) && (ctrlfd
> -1))
6241 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
6243 res
= ast_waitstream(chan
, ints
);
6245 ast_stopstream(chan
);
6253 * The format is weekday - day - month -year
6255 * A list of the files that you need to create
6256 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6257 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6258 Attention the months are in
6263 static int ast_say_date_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6271 ast_localtime(&t
,&tm
,NULL
);
6272 /* W E E K - D A Y */
6274 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6275 res
= ast_streamfile(chan
, fn
, lang
);
6277 res
= ast_waitstream(chan
, ints
);
6281 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6285 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6286 res
= ast_streamfile(chan
, fn
, lang
);
6288 res
= ast_waitstream(chan
, ints
);
6292 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
6298 /* A list of the files that you need to create
6299 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6300 * digits/kai : "KAI"
6302 * digits/p-m : "meta meshmbrias"
6303 * digits/a-m : "pro meshmbrias"
6306 static int ast_say_time_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6313 ast_localtime(&t
, &tm
, NULL
);
6318 else if (hour
== 12)
6320 else if (hour
> 12) {
6325 res
= gr_say_number_female(hour
, chan
, ints
, lang
);
6328 res
= ast_streamfile(chan
, "digits/kai", lang
);
6330 res
= ast_waitstream(chan
, ints
);
6332 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
6335 res
= ast_streamfile(chan
, "digits/hwra", lang
);
6337 res
= ast_waitstream(chan
, ints
);
6341 res
= ast_streamfile(chan
, "digits/p-m", lang
);
6344 res
= ast_streamfile(chan
, "digits/a-m", lang
);
6347 res
= ast_waitstream(chan
, ints
);
6353 static int ast_say_datetime_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6359 ast_localtime(&t
, &tm
, NULL
);
6362 /* W E E K - D A Y */
6364 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6365 res
= ast_streamfile(chan
, fn
, lang
);
6367 res
= ast_waitstream(chan
, ints
);
6371 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6375 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6376 res
= ast_streamfile(chan
, fn
, lang
);
6378 res
= ast_waitstream(chan
, ints
);
6381 res
= ast_say_time_gr(chan
, t
, ints
, lang
);
6385 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
)
6389 int res
=0, offset
, sndoffset
;
6390 char sndfile
[256], nextmsg
[256];
6393 format
= "AdBY 'digits/at' IMp";
6395 ast_localtime(&time
,&tm
,timezone
);
6397 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
6398 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
6399 switch (format
[offset
]) {
6400 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6402 /* Literal name of a sound file */
6404 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
6405 sndfile
[sndoffset
] = format
[offset
];
6406 sndfile
[sndoffset
] = '\0';
6407 res
= wait_file(chan
,ints
,sndfile
,lang
);
6411 /* Sunday - Saturday */
6412 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
6413 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6418 /* January - December */
6419 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
6420 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6424 /* first - thirtyfirst */
6425 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6430 ast_say_number_full_gr(chan
, 1900+tm
.tm_year
, ints
, chan
->language
, -1, -1);
6435 if (tm
.tm_hour
== 0)
6436 gr_say_number_female(12, chan
, ints
, lang
);
6437 else if (tm
.tm_hour
> 12)
6438 gr_say_number_female(tm
.tm_hour
- 12, chan
, ints
, lang
);
6440 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6445 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6451 res
= ast_streamfile(chan
, "digits/kai", lang
);
6453 res
= ast_waitstream(chan
, ints
);
6455 res
= ast_say_number_full_gr(chan
, tm
.tm_min
, ints
, lang
, -1, -1);
6458 res
= ast_streamfile(chan
, "digits/oclock", lang
);
6460 res
= ast_waitstream(chan
, ints
);
6466 if (tm
.tm_hour
> 11)
6467 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
6469 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
6470 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6473 /* Shorthand for "Today", "Yesterday", or ABdY */
6474 /* XXX As emphasized elsewhere, this should the native way in your
6475 * language to say the date, with changes in what you say, depending
6476 * upon how recent the date is. XXX */
6480 time_t beg_today
, tt
;
6482 gettimeofday(&now
,NULL
);
6484 ast_localtime(&tt
,&tmnow
,timezone
);
6485 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6486 /* In any case, it saves not having to do ast_mktime() */
6487 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6488 if (beg_today
< time
) {
6490 res
= wait_file(chan
,ints
, "digits/today",lang
);
6491 } else if (beg_today
- 86400 < time
) {
6493 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6495 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6500 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6501 /* XXX As emphasized elsewhere, this should the native way in your
6502 * language to say the date, with changes in what you say, depending
6503 * upon how recent the date is. XXX */
6507 time_t beg_today
, tt
;
6509 gettimeofday(&now
,NULL
);
6511 ast_localtime(&tt
,&tmnow
,timezone
);
6512 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6513 /* In any case, it saves not having to do ast_mktime() */
6514 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6515 if (beg_today
< time
) {
6517 } else if ((beg_today
- 86400) < time
) {
6519 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6520 } else if (beg_today
- 86400 * 6 < time
) {
6521 /* Within the last week */
6522 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "A", timezone
);
6524 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6529 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HM", timezone
);
6533 snprintf(nextmsg
,sizeof(nextmsg
), "digits/kai");
6534 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6536 res
= ast_say_number_full_gr(chan
, tm
.tm_sec
, ints
, lang
, -1, -1);
6538 snprintf(nextmsg
,sizeof(nextmsg
), "digits/seconds");
6539 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6542 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HMS", timezone
);
6546 /* Just ignore spaces and tabs */
6549 /* Unknown character */
6550 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
6552 /* Jump out on DTMF */
6563 /*********************************** Georgian Support ***************************************/
6567 Convert a number into a semi-localized string. Only for Georgian.
6568 res must be of at least 256 bytes, preallocated.
6569 The output corresponds to Georgian spoken numbers, so
6570 it may be either converted to real words by applying a direct conversion
6571 table, or played just by substituting the entities with played files.
6573 Output may consist of the following tokens (separated by spaces):
6575 1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
6577 20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
6578 100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
6579 1000, 1000_. (atasi, atas).
6580 1000000, 1000000_. (milioni, milion).
6581 1000000000, 1000000000_. (miliardi, miliard).
6583 To be able to play the sounds, each of the above tokens needs
6584 a corresponding sound file. (e.g. 200_.gsm).
6586 static char* ast_translate_number_ge(int num
, char* res
, int res_len
)
6594 strncat(res
, "minus ", res_len
- strlen(res
) - 1);
6595 if ( num
> INT_MIN
) {
6603 /* directly read the numbers */
6604 if (num
<= 20 || num
== 40 || num
== 60 || num
== 80 || num
== 100) {
6605 snprintf(buf
, sizeof(buf
), "%d", num
);
6606 strncat(res
, buf
, res_len
- strlen(res
) - 1);
6611 if (num
< 40) { /* ocda... */
6612 strncat(res
, "20_ ", res_len
- strlen(res
) - 1);
6613 return ast_translate_number_ge(num
- 20, res
, res_len
);
6616 if (num
< 60) { /* ormocda... */
6617 strncat(res
, "40_ ", res_len
- strlen(res
) - 1);
6618 return ast_translate_number_ge(num
- 40, res
, res_len
);
6621 if (num
< 80) { /* samocda... */
6622 strncat(res
, "60_ ", res_len
- strlen(res
) - 1);
6623 return ast_translate_number_ge(num
- 60, res
, res_len
);
6626 if (num
< 100) { /* otxmocda... */
6627 strncat(res
, "80_ ", res_len
- strlen(res
) - 1);
6628 return ast_translate_number_ge(num
- 80, res
, res_len
);
6632 if (num
< 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
6633 remainder
= num
% 100;
6634 digit
= (num
- remainder
) / 100;
6636 if (remainder
== 0) {
6637 snprintf(buf
, sizeof(buf
), "%d", num
);
6638 strncat(res
, buf
, res_len
- strlen(res
) - 1);
6641 snprintf(buf
, sizeof(buf
), "%d_ ", digit
*100);
6642 strncat(res
, buf
, res_len
- strlen(res
) - 1);
6643 return ast_translate_number_ge(remainder
, res
, res_len
);
6649 strncat(res
, "1000", res_len
- strlen(res
) - 1);
6654 if (num
< 1000000) {
6655 remainder
= num
% 1000;
6656 digit
= (num
- remainder
) / 1000;
6658 if (remainder
== 0) {
6659 ast_translate_number_ge(digit
, res
, res_len
);
6660 strncat(res
, " 1000", res_len
- strlen(res
) - 1);
6665 strncat(res
, "1000_ ", res_len
- strlen(res
) - 1);
6666 return ast_translate_number_ge(remainder
, res
, res_len
);
6669 ast_translate_number_ge(digit
, res
, res_len
);
6670 strncat(res
, " 1000_ ", res_len
- strlen(res
) - 1);
6671 return ast_translate_number_ge(remainder
, res
, res_len
);
6676 if (num
== 1000000) {
6677 strncat(res
, "1 1000000", res_len
- strlen(res
) - 1);
6682 if (num
< 1000000000) {
6683 remainder
= num
% 1000000;
6684 digit
= (num
- remainder
) / 1000000;
6686 if (remainder
== 0) {
6687 ast_translate_number_ge(digit
, res
, res_len
);
6688 strncat(res
, " 1000000", res_len
- strlen(res
) - 1);
6692 ast_translate_number_ge(digit
, res
, res_len
);
6693 strncat(res
, " 1000000_ ", res_len
- strlen(res
) - 1);
6694 return ast_translate_number_ge(remainder
, res
, res_len
);
6699 if (num
== 1000000000) {
6700 strncat(res
, "1 1000000000", res_len
- strlen(res
) - 1);
6705 if (num
> 1000000000) {
6706 remainder
= num
% 1000000000;
6707 digit
= (num
- remainder
) / 1000000000;
6709 if (remainder
== 0) {
6710 ast_translate_number_ge(digit
, res
, res_len
);
6711 strncat(res
, " 1000000000", res_len
- strlen(res
) - 1);
6715 ast_translate_number_ge(digit
, res
, res_len
);
6716 strncat(res
, " 1000000000_ ", res_len
- strlen(res
) - 1);
6717 return ast_translate_number_ge(remainder
, res
, res_len
);
6727 /*! \brief ast_say_number_full_ge: Georgian syntax */
6728 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
)
6733 return ast_say_digits_full(chan
, 0, ints
, language
, audiofd
, ctrlfd
);
6736 ast_translate_number_ge(num
, fn
, 512);
6740 const char* remainder
= fn
;
6742 while (res
== 0 && (s
= strstr(remainder
, " "))) {
6743 size_t len
= s
- remainder
;
6744 char* new_string
= malloc(len
+ 1 + strlen("digits/"));
6746 sprintf(new_string
, "digits/");
6747 strncat(new_string
, remainder
, len
); /* we can't sprintf() it, it's not null-terminated. */
6748 /* new_string[len + strlen("digits/")] = '\0'; */
6750 if (!ast_streamfile(chan
, new_string
, language
)) {
6751 if ((audiofd
> -1) && (ctrlfd
> -1))
6752 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
6754 res
= ast_waitstream(chan
, ints
);
6756 ast_stopstream(chan
);
6760 remainder
= s
+ 1; /* position just after the found space char. */
6761 while(*remainder
== ' ') /* skip multiple spaces */
6766 /* the last chunk. */
6767 if (res
== 0 && *remainder
) {
6769 char* new_string
= malloc(strlen(remainder
) + 1 + strlen("digits/"));
6770 sprintf(new_string
, "digits/%s", remainder
);
6772 if (!ast_streamfile(chan
, new_string
, language
)) {
6773 if ((audiofd
> -1) && (ctrlfd
> -1))
6774 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
6776 res
= ast_waitstream(chan
, ints
);
6778 ast_stopstream(chan
);
6792 Georgian support for date/time requires the following files (*.gsm):
6794 mon-1, mon-2, ... (ianvari, tebervali, ...)
6795 day-1, day-2, ... (orshabati, samshabati, ...)
6803 /* Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi". */
6804 static int ast_say_date_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6809 ast_localtime(&t
,&tm
,NULL
);
6812 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
6815 snprintf(fn
, sizeof(fn
), "digits/tslis %d", tm
.tm_wday
);
6816 res
= ast_streamfile(chan
, fn
, lang
);
6818 res
= ast_waitstream(chan
, ints
);
6822 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
6824 res = ast_waitstream(chan, ints);
6829 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6830 res
= ast_streamfile(chan
, fn
, lang
);
6832 res
= ast_waitstream(chan
, ints
);
6842 /* Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
6843 static int ast_say_time_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6848 ast_localtime(&t
, &tm
, NULL
);
6850 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char*)NULL
);
6852 res
= ast_streamfile(chan
, "digits/saati_da", lang
);
6854 res
= ast_waitstream(chan
, ints
);
6859 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char*)NULL
);
6862 res
= ast_streamfile(chan
, "digits/tsuti", lang
);
6864 res
= ast_waitstream(chan
, ints
);
6873 /* Georgian syntax. Say date, then say time. */
6874 static int ast_say_datetime_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6879 ast_localtime(&t
, &tm
, NULL
);
6880 res
= ast_say_date(chan
, t
, ints
, lang
);
6882 ast_say_time(chan
, t
, ints
, lang
);
6890 /* Georgian syntax */
6891 static int ast_say_datetime_from_now_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6902 ast_localtime(&t
, &tm
, NULL
);
6903 ast_localtime(&nowt
, &now
, NULL
);
6904 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6905 if ((daydiff
< 0) || (daydiff
> 6)) {
6906 /* Day of month and month */
6908 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6910 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6911 res
= ast_streamfile(chan
, fn
, lang
);
6913 res
= ast_waitstream(chan
, ints
);
6916 } else if (daydiff
) {
6917 /* Just what day of the week */
6919 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6920 res
= ast_streamfile(chan
, fn
, lang
);
6922 res
= ast_waitstream(chan
, ints
);
6924 } /* Otherwise, it was today */
6926 res
= ast_say_time(chan
, t
, ints
, lang
);
6934 * remap the 'say' functions to use those in this file
6936 static void __attribute__((constructor
)) __say_init(void)
6938 ast_say_number_full
= say_number_full
;
6939 ast_say_enumeration_full
= say_enumeration_full
;
6940 ast_say_digit_str_full
= say_digit_str_full
;
6941 ast_say_character_str_full
= say_character_str_full
;
6942 ast_say_phonetic_str_full
= say_phonetic_str_full
;
6943 ast_say_datetime
= say_datetime
;
6944 ast_say_time
= say_time
;
6945 ast_say_date
= say_date
;
6946 ast_say_datetime_from_now
= say_datetime_from_now
;
6947 ast_say_date_with_format
= say_date_with_format
;