2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
7 * George Konstantoulakis <gkon@inaccessnetworks.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
22 * \brief Say numbers and dates (maybe words one day too)
24 * \author Mark Spencer <markster@digium.com>
26 * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <gkon@inaccessnetworks.com>
28 * \note 2007-02-08 : Support for Georgian added by Alexander Shaduri <ashaduri@gmail.com>,
29 * Next Generation Networks (NGN).
34 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
36 #include <sys/types.h>
39 #include <netinet/in.h>
46 #include <iso/limits_iso.h>
49 #include "asterisk/file.h"
50 #include "asterisk/channel.h"
51 #include "asterisk/logger.h"
52 #include "asterisk/options.h"
53 #include "asterisk/say.h"
54 #include "asterisk/lock.h"
55 #include "asterisk/localtime.h"
56 #include "asterisk/utils.h"
58 /* Forward declaration */
59 static int wait_file(struct ast_channel
*chan
, const char *ints
, const char *file
, const char *lang
);
62 static int say_character_str_full(struct ast_channel
*chan
, const char *str
, const char *ints
, const char *lang
, int audiofd
, int ctrlfd
)
70 while (str
[num
] && !res
) {
80 fn
= "letters/exclaimation-point";
86 fn
= "letters/dollar";
95 fn
= "letters/equals";
101 fn
= "letters/slash";
104 fn
= "letters/space";
116 strcpy(fnbuf
, "digits/X");
122 if ('A' <= ltr
&& ltr
<= 'Z') ltr
+= 'a' - 'A'; /* file names are all lower-case */
123 strcpy(fnbuf
, "letters/X");
127 if (fn
&& ast_fileexists(fn
, NULL
, lang
) > 0) {
128 res
= ast_streamfile(chan
, fn
, lang
);
130 if ((audiofd
> -1) && (ctrlfd
> -1))
131 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
133 res
= ast_waitstream(chan
, ints
);
135 ast_stopstream(chan
);
143 static int say_phonetic_str_full(struct ast_channel
*chan
, const char *str
, const char *ints
, const char *lang
, int audiofd
, int ctrlfd
)
151 while (str
[num
] && !res
) {
161 fn
= "letters/exclaimation-point";
167 fn
= "letters/dollar";
176 fn
= "letters/equals";
182 fn
= "letters/slash";
185 fn
= "letters/space";
196 strcpy(fnbuf
, "digits/X");
200 default: /* '9' falls here... */
202 if ('A' <= ltr
&& ltr
<= 'Z') ltr
+= 'a' - 'A'; /* file names are all lower-case */
203 strcpy(fnbuf
, "phonetic/X_p");
207 if (fn
&& ast_fileexists(fn
, NULL
, lang
) > 0) {
208 res
= ast_streamfile(chan
, fn
, lang
);
210 if ((audiofd
> -1) && (ctrlfd
> -1))
211 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
213 res
= ast_waitstream(chan
, ints
);
215 ast_stopstream(chan
);
223 static int say_digit_str_full(struct ast_channel
*chan
, const char *str
, const char *ints
, const char *lang
, int audiofd
, int ctrlfd
)
230 while (str
[num
] && !res
) {
252 strcpy(fnbuf
, "digits/X");
257 if (fn
&& ast_fileexists(fn
, NULL
, lang
) > 0) {
258 res
= ast_streamfile(chan
, fn
, lang
);
260 if ((audiofd
> -1) && (ctrlfd
> -1))
261 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
263 res
= ast_waitstream(chan
, ints
);
265 ast_stopstream(chan
);
273 /* Forward declarations */
274 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
275 \note Not really language codes.
276 For these language codes, Asterisk will change the syntax when
277 saying numbers (and in some cases dates and voicemail messages
281 \arg \b en - English (US)
282 \arg \b en_GB - English (British)
283 \arg \b es - Spanish, Mexican
288 \arg \b no - Norwegian
290 \arg \b pt - Portuguese
291 \arg \b pt_BR - Portuguese (Brazil)
293 \arg \b tw - Taiwanese / Chinese
295 \arg \b ge - Georgian
298 For Some languages the numbers differ for gender and plural.
299 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
300 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
301 use the option argument 'p' for plural enumerations like in German
303 Date/Time functions currently have less languages supported than saynumber().
305 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
307 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
310 Portuguese sound files needed for Time/Date functions:
321 Spanish sound files needed for Time/Date functions:
326 Italian sound files needed for Time/Date functions:
332 /* Forward declarations of language specific variants of ast_say_number_full */
333 static int ast_say_number_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
334 static int ast_say_number_full_cz(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
335 static int ast_say_number_full_da(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
336 static int ast_say_number_full_de(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
337 static int ast_say_number_full_en_GB(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
338 static int ast_say_number_full_es(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
339 static int ast_say_number_full_fr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
340 static int ast_say_number_full_he(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
341 static int ast_say_number_full_it(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
342 static int ast_say_number_full_nl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
343 static int ast_say_number_full_no(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
344 static int ast_say_number_full_pl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
345 static int ast_say_number_full_pt(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
346 static int ast_say_number_full_se(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
347 static int ast_say_number_full_tw(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
348 static int ast_say_number_full_gr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
349 static int ast_say_number_full_ru(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
350 static int ast_say_number_full_ge(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
352 /* Forward declarations of language specific variants of ast_say_enumeration_full */
353 static int ast_say_enumeration_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
);
354 static int ast_say_enumeration_full_da(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
355 static int ast_say_enumeration_full_de(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
356 static int ast_say_enumeration_full_he(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
);
358 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
359 static int ast_say_date_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
360 static int ast_say_date_da(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
361 static int ast_say_date_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
362 static int ast_say_date_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
363 static int ast_say_date_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
364 static int ast_say_date_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
365 static int ast_say_date_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
366 static int ast_say_date_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
367 static int ast_say_date_he(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
369 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
);
370 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
);
371 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
);
372 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
);
373 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
);
374 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
);
375 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
);
376 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
);
377 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
);
378 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
);
379 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
);
380 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
);
382 static int ast_say_time_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
383 static int ast_say_time_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
384 static int ast_say_time_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
385 static int ast_say_time_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
386 static int ast_say_time_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
387 static int ast_say_time_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
388 static int ast_say_time_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
389 static int ast_say_time_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
390 static int ast_say_time_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
391 static int ast_say_time_he(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
393 static int ast_say_datetime_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
394 static int ast_say_datetime_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
395 static int ast_say_datetime_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
396 static int ast_say_datetime_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
397 static int ast_say_datetime_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
398 static int ast_say_datetime_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
399 static int ast_say_datetime_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
400 static int ast_say_datetime_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
401 static int ast_say_datetime_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
402 static int ast_say_datetime_he(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
404 static int ast_say_datetime_from_now_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
405 static int ast_say_datetime_from_now_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
406 static int ast_say_datetime_from_now_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
407 static int ast_say_datetime_from_now_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
408 static int ast_say_datetime_from_now_he(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
);
410 static int wait_file(struct ast_channel
*chan
, const char *ints
, const char *file
, const char *lang
)
413 if ((res
= ast_streamfile(chan
, file
, lang
)))
414 ast_log(LOG_WARNING
, "Unable to play message %s\n", file
);
416 res
= ast_waitstream(chan
, ints
);
420 /*! \brief ast_say_number_full: call language-specific functions */
421 /* Called from AGI */
422 static int say_number_full(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
424 if (!strcasecmp(language
,"en") ) { /* English syntax */
425 return(ast_say_number_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
426 } else if (!strcasecmp(language
, "cz") ) { /* Czech syntax */
427 return(ast_say_number_full_cz(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
428 } else if (!strcasecmp(language
, "da") ) { /* Danish syntax */
429 return(ast_say_number_full_da(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
430 } else if (!strcasecmp(language
, "de") ) { /* German syntax */
431 return(ast_say_number_full_de(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
432 } else if (!strcasecmp(language
, "en_GB") ) { /* British syntax */
433 return(ast_say_number_full_en_GB(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
434 } else if (!strcasecmp(language
, "no") ) { /* Norwegian syntax */
435 return(ast_say_number_full_no(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
436 } else if (!strcasecmp(language
, "es") || !strcasecmp(language
, "mx")) { /* Spanish syntax */
437 return(ast_say_number_full_es(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
438 } else if (!strcasecmp(language
, "fr") ) { /* French syntax */
439 return(ast_say_number_full_fr(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
440 } else if (!strcasecmp(language
, "he") ) { /* Hebrew syntax */
441 return(ast_say_number_full_he(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
442 } else if (!strcasecmp(language
, "it") ) { /* Italian syntax */
443 return(ast_say_number_full_it(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
444 } else if (!strcasecmp(language
, "nl") ) { /* Dutch syntax */
445 return(ast_say_number_full_nl(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
446 } else if (!strcasecmp(language
, "pl") ) { /* Polish syntax */
447 return(ast_say_number_full_pl(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
448 } else if (!strcasecmp(language
, "pt") || !strcasecmp(language
, "pt_BR")) { /* Portuguese syntax */
449 return(ast_say_number_full_pt(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
450 } else if (!strcasecmp(language
, "se") ) { /* Swedish syntax */
451 return(ast_say_number_full_se(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
452 } else if (!strcasecmp(language
, "tw") || !strcasecmp(language
, "zh") ) { /* Taiwanese / Chinese syntax */
453 return(ast_say_number_full_tw(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
454 } else if (!strcasecmp(language
, "gr") ) { /* Greek syntax */
455 return(ast_say_number_full_gr(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
456 } else if (!strcasecmp(language
, "ru") ) { /* Russian syntax */
457 return(ast_say_number_full_ru(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
458 } else if (!strcasecmp(language
, "ge") ) { /* Georgian syntax */
459 return(ast_say_number_full_ge(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
462 /* Default to english */
463 return(ast_say_number_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
466 /*! \brief ast_say_number_full_en: English syntax */
467 /* This is the default syntax, if no other syntax defined in this file is used */
468 static int ast_say_number_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
474 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
476 while (!res
&& (num
|| playh
)) {
478 snprintf(fn
, sizeof(fn
), "digits/minus");
479 if ( num
> INT_MIN
) {
485 snprintf(fn
, sizeof(fn
), "digits/hundred");
487 } else if (num
< 20) {
488 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
490 } else if (num
< 100) {
491 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
492 num
-= ((num
/ 10) * 10);
495 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
497 num
-= ((num
/ 100) * 100);
499 if (num
< 1000000) { /* 1,000,000 */
500 res
= ast_say_number_full_en(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
504 snprintf(fn
, sizeof(fn
), "digits/thousand");
506 if (num
< 1000000000) { /* 1,000,000,000 */
507 res
= ast_say_number_full_en(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
511 snprintf(fn
, sizeof(fn
), "digits/million");
513 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
520 if (!ast_streamfile(chan
, fn
, language
)) {
521 if ((audiofd
> -1) && (ctrlfd
> -1))
522 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
524 res
= ast_waitstream(chan
, ints
);
526 ast_stopstream(chan
);
532 static int exp10_int(int power
)
535 for (x
=0;x
<power
;x
++)
540 /*! \brief ast_say_number_full_cz: Czech syntax */
542 * 1m,2m - gender male
543 * 1w,2w - gender female
547 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
549 * for each number 10^(3n + 3) exist 3 files represented as:
550 * 1 tousand = jeden tisic = 1_E3
551 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
552 * 5,6,... tousands = pet,sest,... tisic = 5_E3
558 * tousand, milion are gender male, so 1 and 2 is 1m 2m
559 * miliard is gender female, so 1 and 2 is 1w 2w
561 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
)
571 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
576 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
578 while (!res
&& (num
|| playh
)) {
580 snprintf(fn
, sizeof(fn
), "digits/minus");
581 if ( num
> INT_MIN
) {
586 } else if (num
< 3 ) {
587 snprintf(fn
, sizeof(fn
), "digits/%d%c",num
,options
[0]);
590 } else if (num
< 20) {
591 snprintf(fn
, sizeof(fn
), "digits/%d",num
);
594 } else if (num
< 100) {
595 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
596 num
-= ((num
/ 10) * 10);
597 } else if (num
< 1000) {
598 hundered
= num
/ 100;
599 if ( hundered
== 1 ) {
600 snprintf(fn
, sizeof(fn
), "digits/1sto");
601 } else if ( hundered
== 2 ) {
602 snprintf(fn
, sizeof(fn
), "digits/2ste");
604 res
= ast_say_number_full_cz(chan
,hundered
,ints
,language
,options
,audiofd
,ctrlfd
);
607 if (hundered
== 3 || hundered
== 4) {
608 snprintf(fn
, sizeof(fn
), "digits/sta");
609 } else if ( hundered
> 4 ) {
610 snprintf(fn
, sizeof(fn
), "digits/set");
613 num
-= (hundered
* 100);
614 } else { /* num > 1000 */
615 length
= (int)log10(num
)+1;
616 while ( (length
% 3 ) != 1 ) {
619 left
= num
/ (exp10_int(length
-1));
622 case 9: options
= "w"; /* 1,000,000,000 gender female */
624 default : options
= "m"; /* others are male */
627 if ( left
> 1 ) { /* we dont say "one thousand" but only thousand */
628 res
= ast_say_number_full_cz(chan
,left
,ints
,language
,options
,audiofd
,ctrlfd
);
632 if ( left
>= 5 ) { /* >= 5 have the same declesion */
633 snprintf(fn
, sizeof(fn
), "digits/5_E%d",length
-1);
634 } else if ( left
>= 2 && left
<= 4 ) {
635 snprintf(fn
, sizeof(fn
), "digits/2-4_E%d",length
-1);
636 } else { /* left == 1 */
637 snprintf(fn
, sizeof(fn
), "digits/1_E%d",length
-1);
639 num
-= left
* (exp10_int(length
-1));
642 if (!ast_streamfile(chan
, fn
, language
)) {
643 if ((audiofd
> -1) && (ctrlfd
> -1)) {
644 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
646 res
= ast_waitstream(chan
, ints
);
649 ast_stopstream(chan
);
655 /*! \brief ast_say_number_full_da: Danish syntax */
657 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
659 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
)
664 int cn
= 1; /* +1 = commune; -1 = neuter */
667 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
669 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
671 while (!res
&& (num
|| playh
|| playa
)) {
672 /* The grammar for Danish numbers is the same as for English except
674 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
675 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
676 * "one-and twenty" and 68 is "eight-and sixty".
677 * - "million" is different in singular and plural form
678 * - numbers > 1000 with zero as the third digit from last have an
679 * "and" before the last two digits, i.e. 2034 is "two thousand and
680 * four-and thirty" and 1000012 is "one million and twelve".
683 snprintf(fn
, sizeof(fn
), "digits/minus");
684 if ( num
> INT_MIN
) {
690 snprintf(fn
, sizeof(fn
), "digits/hundred");
693 snprintf(fn
, sizeof(fn
), "digits/and");
695 } else if (num
== 1 && cn
== -1) {
696 snprintf(fn
, sizeof(fn
), "digits/1N");
698 } else if (num
< 20) {
699 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
701 } else if (num
< 100) {
704 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
707 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
712 int hundreds
= num
/ 100;
714 snprintf(fn
, sizeof(fn
), "digits/1N");
716 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
719 num
-= 100 * hundreds
;
725 res
= ast_say_number_full_da(chan
, num
/ 1000, ints
, language
, "n", audiofd
, ctrlfd
);
729 snprintf(fn
, sizeof(fn
), "digits/thousand");
731 if (num
< 1000000000) {
732 int millions
= num
/ 1000000;
733 res
= ast_say_number_full_da(chan
, millions
, ints
, language
, "c", audiofd
, ctrlfd
);
737 snprintf(fn
, sizeof(fn
), "digits/million");
739 snprintf(fn
, sizeof(fn
), "digits/millions");
742 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
746 if (num
&& num
< 100)
751 if (!ast_streamfile(chan
, fn
, language
)) {
752 if ((audiofd
> -1) && (ctrlfd
> -1))
753 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
755 res
= ast_waitstream(chan
, ints
);
757 ast_stopstream(chan
);
763 /*! \brief ast_say_number_full_de: German syntax */
765 In addition to English, the following sounds are required:
767 "1-and" through "9-and"
770 NB "1" is recorded as 'eins'
772 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
)
775 int mf
= 1; /* +1 = male and neuter; -1 = female */
779 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
781 if (options
&& (!strncasecmp(options
, "f",1)))
784 while (!res
&& num
) {
785 /* The grammar for German numbers is the same as for English except
787 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
788 * "one-and twenty" and 68 is "eight-and sixty".
789 * - "one" varies according to gender
790 * - 100 is 'hundert', however all other instances are 'ein hundert'
791 * - 1000 is 'tausend', however all other instances are 'ein tausend'
792 * - 1000000 is always 'eine million'
793 * - "million" is different in singular and plural form
796 snprintf(fn
, sizeof(fn
), "digits/minus");
797 if ( num
> INT_MIN
) {
802 } else if (num
< 100 && t
) {
803 snprintf(fn
, sizeof(fn
), "digits/and");
805 } else if (num
== 1 && mf
== -1) {
806 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
808 } else if (num
< 20) {
809 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
811 } else if (num
< 100) {
814 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
817 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
820 } else if (num
== 100 && t
== 0) {
821 snprintf(fn
, sizeof(fn
), "digits/hundred");
823 } else if (num
< 1000) {
824 int hundreds
= num
/ 100;
827 snprintf(fn
, sizeof(fn
), "digits/1N");
829 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
831 snprintf(fna
, sizeof(fna
), "digits/hundred");
833 } else if (num
== 1000 && t
== 0) {
834 snprintf(fn
, sizeof(fn
), "digits/thousand");
836 } else if (num
< 1000000) {
837 int thousands
= num
/ 1000;
840 if (thousands
== 1) {
841 snprintf(fn
, sizeof(fn
), "digits/1N");
842 snprintf(fna
, sizeof(fna
), "digits/thousand");
844 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
847 snprintf(fn
, sizeof(fn
), "digits/thousand");
849 } else if (num
< 1000000000) {
850 int millions
= num
/ 1000000;
854 snprintf(fn
, sizeof(fn
), "digits/1F");
855 snprintf(fna
, sizeof(fna
), "digits/million");
857 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
860 snprintf(fn
, sizeof(fn
), "digits/millions");
862 } else if (num
<= INT_MAX
) {
863 int billions
= num
/ 1000000000;
864 num
= num
% 1000000000;
867 snprintf(fn
, sizeof(fn
), "digits/1F");
868 snprintf(fna
, sizeof(fna
), "digits/milliard");
870 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
874 snprintf(fn
, sizeof(fn
), "digits/milliards");
877 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
881 if (!ast_streamfile(chan
, fn
, language
)) {
882 if ((audiofd
> -1) && (ctrlfd
> -1))
883 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
885 res
= ast_waitstream(chan
, ints
);
887 ast_stopstream(chan
);
889 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
890 if ((audiofd
> -1) && (ctrlfd
> -1))
891 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
893 res
= ast_waitstream(chan
, ints
);
895 ast_stopstream(chan
);
903 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
905 In addition to American English, the following sounds are required: "and"
907 static int ast_say_number_full_en_GB(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
914 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
916 while (!res
&& (num
|| playh
|| playa
)) {
918 snprintf(fn
, sizeof(fn
), "digits/minus");
919 if ( num
> INT_MIN
) {
925 snprintf(fn
, sizeof(fn
), "digits/hundred");
928 snprintf(fn
, sizeof(fn
), "digits/and");
930 } else if (num
< 20) {
931 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
933 } else if (num
< 100) {
934 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
935 num
-= ((num
/ 10) * 10);
936 } else if (num
< 1000) {
937 int hundreds
= num
/ 100;
938 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
941 num
-= 100 * hundreds
;
944 } else if (num
< 1000000) {
945 res
= ast_say_number_full_en_GB(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
948 snprintf(fn
, sizeof(fn
), "digits/thousand");
950 if (num
&& num
< 100)
952 } else if (num
< 1000000000) {
953 int millions
= num
/ 1000000;
954 res
= ast_say_number_full_en_GB(chan
, millions
, ints
, language
, audiofd
, ctrlfd
);
957 snprintf(fn
, sizeof(fn
), "digits/million");
959 if (num
&& num
< 100)
962 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
967 if (!ast_streamfile(chan
, fn
, language
)) {
968 if ((audiofd
> -1) && (ctrlfd
> -1))
969 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
971 res
= ast_waitstream(chan
, ints
);
973 ast_stopstream(chan
);
979 /*! \brief ast_say_number_full_es: Spanish syntax */
981 Requires a few new audios:
982 1F.gsm: feminine 'una'
983 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
985 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
)
989 int mf
= 0; /* +1 = male; -1 = female */
992 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
995 if (!strncasecmp(options
, "f",1))
997 else if (!strncasecmp(options
, "m", 1))
1001 while (!res
&& num
) {
1003 snprintf(fn
, sizeof(fn
), "digits/minus");
1004 if ( num
> INT_MIN
) {
1010 snprintf(fn
, sizeof(fn
), "digits/and");
1012 } else if (num
== 1) {
1014 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1016 snprintf(fn
, sizeof(fn
), "digits/%dM", num
);
1018 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1020 } else if (num
< 31) {
1021 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1023 } else if (num
< 100) {
1024 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1025 num
-= ((num
/10)*10);
1028 } else if (num
== 100) {
1029 snprintf(fn
, sizeof(fn
), "digits/100");
1031 } else if (num
< 200) {
1032 snprintf(fn
, sizeof(fn
), "digits/100-and");
1036 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100)*100);
1037 num
-= ((num
/100)*100);
1038 } else if (num
< 2000) {
1040 snprintf(fn
, sizeof(fn
), "digits/thousand");
1042 if (num
< 1000000) {
1043 res
= ast_say_number_full_es(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1047 snprintf(fn
, sizeof(fn
), "digits/thousand");
1049 if (num
< 2147483640) {
1050 if ((num
/1000000) == 1) {
1051 res
= ast_say_number_full_es(chan
, num
/ 1000000, ints
, language
, "M", audiofd
, ctrlfd
);
1054 snprintf(fn
, sizeof(fn
), "digits/million");
1056 res
= ast_say_number_full_es(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1059 snprintf(fn
, sizeof(fn
), "digits/millions");
1061 num
= num
% 1000000;
1063 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1071 if (!ast_streamfile(chan
, fn
, language
)) {
1072 if ((audiofd
> -1) && (ctrlfd
> -1))
1073 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1075 res
= ast_waitstream(chan
, ints
);
1077 ast_stopstream(chan
);
1085 /*! \brief ast_say_number_full_fr: French syntax */
1086 /* Extra sounds needed:
1089 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
)
1094 int mf
= 1; /* +1 = male; -1 = female */
1097 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1099 if (options
&& !strncasecmp(options
, "f",1))
1102 while (!res
&& (num
|| playh
|| playa
)) {
1104 snprintf(fn
, sizeof(fn
), "digits/minus");
1105 if ( num
> INT_MIN
) {
1111 snprintf(fn
, sizeof(fn
), "digits/hundred");
1114 snprintf(fn
, sizeof(fn
), "digits/et");
1116 } else if (num
== 1) {
1118 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
1120 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1122 } else if (num
< 21) {
1123 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1125 } else if (num
< 70) {
1126 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10)*10);
1127 if ((num
% 10) == 1) playa
++;
1129 } else if (num
< 80) {
1130 snprintf(fn
, sizeof(fn
), "digits/60");
1131 if ((num
% 10) == 1) playa
++;
1133 } else if (num
< 100) {
1134 snprintf(fn
, sizeof(fn
), "digits/80");
1136 } else if (num
< 200) {
1137 snprintf(fn
, sizeof(fn
), "digits/hundred");
1139 } else if (num
< 1000) {
1140 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1143 } else if (num
< 2000) {
1144 snprintf(fn
, sizeof(fn
), "digits/thousand");
1146 } else if (num
< 1000000) {
1147 res
= ast_say_number_full_fr(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
1150 snprintf(fn
, sizeof(fn
), "digits/thousand");
1152 } else if (num
< 1000000000) {
1153 res
= ast_say_number_full_fr(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
1156 snprintf(fn
, sizeof(fn
), "digits/million");
1157 num
= num
% 1000000;
1159 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1163 if (!ast_streamfile(chan
, fn
, language
)) {
1164 if ((audiofd
> -1) && (ctrlfd
> -1))
1165 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1167 res
= ast_waitstream(chan
, ints
);
1169 ast_stopstream(chan
);
1178 /* Check doc/lang/hebrew-digits.txt for information about the various
1179 * recordings required to make this translation work properly */
1180 #define SAY_NUM_BUF_SIZE 256
1181 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
)
1184 int state
= 0; /* no need to save anything */
1185 int mf
= -1; /* +1 = Masculin; -1 = Feminin */
1188 char fn
[SAY_NUM_BUF_SIZE
] = "";
1190 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: started. num: %d, options=\"%s\"\n", num
, options
);
1193 return ast_say_digits_full(chan
, 0, ints
, language
, audiofd
, ctrlfd
);
1195 if (options
&& !strncasecmp(options
, "m", 1)) {
1198 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num
, state
, options
, mf
);
1200 /* Do we have work to do? */
1201 while (!res
&& (num
|| (state
> 0))) {
1202 /* first type of work: play a second sound. In this loop
1203 * we can only play one sound file at a time. Thus playing
1204 * a second one requires repeating the loop just for the
1205 * second file. The variable 'state' remembers where we were.
1206 * state==0 is the normal mode and it means that we continue
1207 * to check if the number num has yet anything left.
1209 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d, tmpnum=%d\n", num
, state
, options
, mf
, tmpnum
);
1213 } else if (state
== 2) {
1214 if ((num
>= 11) && (num
< 21)) {
1216 snprintf(fn
, sizeof(fn
), "digits/ve");
1218 snprintf(fn
, sizeof(fn
), "digits/uu");
1223 snprintf(fn
, sizeof(fn
), "digits/ve");
1226 snprintf(fn
, sizeof(fn
), "digits/uu");
1230 snprintf(fn
, sizeof(fn
), "digits/ve");
1232 snprintf(fn
, sizeof(fn
), "digits/uu");
1236 snprintf(fn
, sizeof(fn
), "digits/ve");
1239 snprintf(fn
, sizeof(fn
), "digits/ve");
1242 snprintf(fn
, sizeof(fn
), "digits/ve");
1245 snprintf(fn
, sizeof(fn
), "digits/ve");
1248 snprintf(fn
, sizeof(fn
), "digits/uu");
1251 snprintf(fn
, sizeof(fn
), "digits/ve");
1254 snprintf(fn
, sizeof(fn
), "digits/ve");
1259 } else if (state
== 3) {
1260 snprintf(fn
, sizeof(fn
), "digits/1k");
1262 } else if (num
< 0) {
1263 snprintf(fn
, sizeof(fn
), "digits/minus");
1265 } else if (num
< 20) {
1267 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1269 snprintf(fn
, sizeof(fn
), "digits/%dm", num
);
1272 } else if ((num
< 100) && (num
>= 20)) {
1273 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 10) * 10);
1278 } else if ((num
>= 100) && (num
< 1000)) {
1280 snprintf(fn
, sizeof(fn
), "digits/%d00", tmpnum
);
1281 num
= num
- (tmpnum
* 100);
1282 if ((num
> 0) && (num
< 11)) {
1285 } else if ((num
>= 1000) && (num
< 10000)) {
1286 tmpnum
= num
/ 1000;
1287 snprintf(fn
, sizeof(fn
), "digits/%dk", tmpnum
);
1288 num
= num
- (tmpnum
* 1000);
1289 if ((num
> 0) && (num
< 11)) {
1292 } else if (num
< 20000) {
1293 snprintf(fn
, sizeof(fn
), "digits/%dm", (num
/ 1000));
1296 } else if (num
< 1000000) {
1297 res
= ast_say_number_full_he(chan
, num
/ 1000, ints
, language
, "m", audiofd
, ctrlfd
);
1301 snprintf(fn
, sizeof(fn
), "digits/1k");
1303 if ((num
> 0) && (num
< 11)) {
1306 } else if (num
< 2000000) {
1307 snprintf(fn
, sizeof(fn
), "digits/million");
1308 num
= num
% 1000000;
1309 if ((num
> 0) && (num
< 11)) {
1312 } else if (num
< 3000000) {
1313 snprintf(fn
, sizeof(fn
), "digits/twomillion");
1314 num
= num
- 2000000;
1315 if ((num
> 0) && (num
< 11)) {
1318 } else if (num
< 1000000000) {
1319 res
= ast_say_number_full_he(chan
, num
/ 1000000, ints
, language
, "m", audiofd
, ctrlfd
);
1323 snprintf(fn
, sizeof(fn
), "digits/million");
1324 num
= num
% 1000000;
1325 if ((num
> 0) && (num
< 11)) {
1329 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1334 if (!ast_streamfile(chan
, fn
, language
)) {
1335 if ((audiofd
> -1) && (ctrlfd
> -1)) {
1336 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1338 res
= ast_waitstream(chan
, ints
);
1341 ast_stopstream(chan
);
1347 /*! \brief ast_say_number_full_it: Italian */
1348 static int ast_say_number_full_it(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1356 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1361 Like english, numbers up to 20 are a single 'word', and others
1362 compound, but with exceptions.
1363 For example 21 is not twenty-one, but there is a single word in 'it'.
1364 Idem for 28 (ie when a the 2nd part of a compund number
1365 starts with a vowel)
1367 There are exceptions also for hundred, thousand and million.
1368 In english 100 = one hundred, 200 is two hundred.
1369 In italian 100 = cento , like to say hundred (without one),
1370 200 and more are like english.
1372 Same applies for thousand:
1373 1000 is one thousand in en, 2000 is two thousand.
1374 In it we have 1000 = mille , 2000 = 2 mila
1376 For million(s) we use the plural, if more than one
1377 Also, one million is abbreviated in it, like on-million,
1378 or 'un milione', not 'uno milione'.
1379 So the right file is provided.
1382 while (!res
&& (num
|| playh
)) {
1384 snprintf(fn
, sizeof(fn
), "digits/minus");
1385 if ( num
> INT_MIN
) {
1391 snprintf(fn
, sizeof(fn
), "digits/hundred");
1393 } else if (num
< 20) {
1394 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1396 } else if (num
== 21) {
1397 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1399 } else if (num
== 28) {
1400 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1402 } else if (num
== 31) {
1403 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1405 } else if (num
== 38) {
1406 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1408 } else if (num
== 41) {
1409 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1411 } else if (num
== 48) {
1412 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1414 } else if (num
== 51) {
1415 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1417 } else if (num
== 58) {
1418 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1420 } else if (num
== 61) {
1421 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1423 } else if (num
== 68) {
1424 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1426 } else if (num
== 71) {
1427 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1429 } else if (num
== 78) {
1430 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1432 } else if (num
== 81) {
1433 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1435 } else if (num
== 88) {
1436 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1438 } else if (num
== 91) {
1439 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1441 } else if (num
== 98) {
1442 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1444 } else if (num
< 100) {
1445 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1446 num
-= ((num
/ 10) * 10);
1449 if ((num
/ 100) > 1) {
1450 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
1453 snprintf(fn
, sizeof(fn
), "digits/hundred");
1455 num
-= ((num
/ 100) * 100);
1457 if (num
< 1000000) { /* 1,000,000 */
1459 res
= ast_say_number_full_it(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1464 if ((tempnum
/ 1000) < 2)
1465 snprintf(fn
, sizeof(fn
), "digits/thousand");
1466 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1467 snprintf(fn
, sizeof(fn
), "digits/thousands");
1469 if (num
< 1000000000) { /* 1,000,000,000 */
1470 if ((num
/ 1000000) > 1)
1471 res
= ast_say_number_full_it(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1475 num
= num
% 1000000;
1476 if ((tempnum
/ 1000000) < 2)
1477 snprintf(fn
, sizeof(fn
), "digits/million");
1479 snprintf(fn
, sizeof(fn
), "digits/millions");
1481 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1488 if (!ast_streamfile(chan
, fn
, language
)) {
1489 if ((audiofd
> -1) && (ctrlfd
> -1))
1490 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1492 res
= ast_waitstream(chan
, ints
);
1494 ast_stopstream(chan
);
1500 /*! \brief ast_say_number_full_nl: dutch syntax */
1501 /* New files: digits/nl-en
1503 static int ast_say_number_full_nl(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
1510 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1511 while (!res
&& (num
|| playh
)) {
1513 snprintf(fn
, sizeof(fn
), "digits/minus");
1514 if ( num
> INT_MIN
) {
1520 snprintf(fn
, sizeof(fn
), "digits/hundred");
1522 } else if (num
< 20) {
1523 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1525 } else if (num
< 100) {
1528 res
= ast_say_number_full_nl(chan
, units
, ints
, language
, audiofd
, ctrlfd
);
1532 snprintf(fn
, sizeof(fn
), "digits/nl-en");
1534 snprintf(fn
, sizeof(fn
), "digits/%d", num
- units
);
1537 } else if (num
< 200) {
1538 /* hundred, not one-hundred */
1539 ast_copy_string(fn
, "digits/hundred", sizeof(fn
));
1540 num
-= ((num
/ 100) * 100);
1541 } else if (num
< 1000) {
1542 snprintf(fn
, sizeof(fn
), "digits/%d", num
/ 100);
1544 num
-= ((num
/ 100) * 100);
1547 /* thousand, not one-thousand */
1549 ast_copy_string(fn
, "digits/thousand", sizeof(fn
));
1550 } else if (num
< 10000) { /* 1,100 to 9,9999 */
1551 res
= ast_say_number_full_nl(chan
, num
/ 100, ints
, language
, audiofd
, ctrlfd
);
1555 ast_copy_string(fn
, "digits/hundred", sizeof(fn
));
1557 if (num
< 1000000) { /* 1,000,000 */
1558 res
= ast_say_number_full_nl(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
1562 snprintf(fn
, sizeof(fn
), "digits/thousand");
1564 if (num
< 1000000000) { /* 1,000,000,000 */
1565 res
= ast_say_number_full_nl(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
1568 num
= num
% 1000000;
1569 snprintf(fn
, sizeof(fn
), "digits/million");
1571 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1579 if (!ast_streamfile(chan
, fn
, language
)) {
1580 if ((audiofd
> -1) && (ctrlfd
> -1))
1581 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1583 res
= ast_waitstream(chan
, ints
);
1585 ast_stopstream(chan
);
1591 /*! \brief ast_say_number_full_no: Norwegian syntax */
1593 In addition to American English, the following sounds are required: "and", "1N"
1595 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
)
1600 int cn
= 1; /* +1 = commune; -1 = neuter */
1604 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1606 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
1608 while (!res
&& (num
|| playh
|| playa
)) {
1609 /* The grammar for Norwegian numbers is the same as for English except
1610 * for the following:
1611 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1612 * "and" before the last two digits, i.e. 2034 is "two thousand and
1613 * thirty-four" and 1000012 is "one million and twelve".
1616 snprintf(fn
, sizeof(fn
), "digits/minus");
1617 if ( num
> INT_MIN
) {
1623 snprintf(fn
, sizeof(fn
), "digits/hundred");
1626 snprintf(fn
, sizeof(fn
), "digits/and");
1628 } else if (num
== 1 && cn
== -1) {
1629 snprintf(fn
, sizeof(fn
), "digits/1N");
1631 } else if (num
< 20) {
1632 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
1634 } else if (num
< 100) {
1635 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
1636 num
-= ((num
/ 10) * 10);
1637 } else if (num
< 1000) {
1638 int hundreds
= num
/ 100;
1640 snprintf(fn
, sizeof(fn
), "digits/1N");
1642 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100));
1645 num
-= 100 * hundreds
;
1648 } else if (num
< 1000000) {
1649 res
= ast_say_number_full_no(chan
, num
/ 1000, ints
, language
, "n", audiofd
, ctrlfd
);
1652 snprintf(fn
, sizeof(fn
), "digits/thousand");
1654 if (num
&& num
< 100)
1656 } else if (num
< 1000000000) {
1657 int millions
= num
/ 1000000;
1658 res
= ast_say_number_full_no(chan
, millions
, ints
, language
, "c", audiofd
, ctrlfd
);
1661 snprintf(fn
, sizeof(fn
), "digits/million");
1662 num
= num
% 1000000;
1663 if (num
&& num
< 100)
1666 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
1671 if (!ast_streamfile(chan
, fn
, language
)) {
1672 if ((audiofd
> -1) && (ctrlfd
> -1))
1673 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1675 res
= ast_waitstream(chan
, ints
);
1677 ast_stopstream(chan
);
1684 char *separator_dziesiatek
;
1688 char *dziesiatki
[10];
1693 static char *pl_rzad_na_tekst(odmiana
*odm
, int i
, int rzad
)
1699 return odm
->rzedy
[rzad
- 1][0];
1700 if ((i
> 21 || i
< 11) && i
%10 > 1 && i
%10 < 5)
1701 return odm
->rzedy
[rzad
- 1][1];
1703 return odm
->rzedy
[rzad
- 1][2];
1706 static char* pl_append(char* buffer
, char* str
)
1708 strcpy(buffer
, str
);
1709 buffer
+= strlen(str
);
1713 static void pl_odtworz_plik(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, char *fn
)
1715 char file_name
[255] = "digits/";
1716 strcat(file_name
, fn
);
1717 ast_log(LOG_DEBUG
, "Trying to play: %s\n", file_name
);
1718 if (!ast_streamfile(chan
, file_name
, language
)) {
1719 if ((audiofd
> -1) && (ctrlfd
> -1))
1720 ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
1722 ast_waitstream(chan
, ints
);
1724 ast_stopstream(chan
);
1727 static void powiedz(struct ast_channel
*chan
, const char *language
, int audiofd
, int ctrlfd
, const char *ints
, odmiana
*odm
, int rzad
, int i
)
1729 /* Initialise variables to allow compilation on Debian-stable, etc */
1739 if (i
== 0 && rzad
> 0) {
1743 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[0]);
1747 m1000E6
= i
% 1000000000;
1748 i1000E6
= i
/ 1000000000;
1750 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+3, i1000E6
);
1752 m1000E3
= m1000E6
% 1000000;
1753 i1000E3
= m1000E6
/ 1000000;
1755 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+2, i1000E3
);
1757 m1000
= m1000E3
% 1000;
1758 i1000
= m1000E3
/ 1000;
1760 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, odm
, rzad
+1, i1000
);
1766 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->setki
[i100
]);
1768 if ( m100
> 0 && m100
<=9 ) {
1770 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
]);
1772 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry
[m100
]);
1773 } else if (m100
% 10 == 0) {
1774 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1775 } else if (m100
<= 19 ) {
1776 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->nastki
[m100
% 10]);
1777 } else if (m100
!= 0) {
1778 if (odm
->separator_dziesiatek
[0]==' ') {
1779 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->dziesiatki
[m100
/ 10]);
1780 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, odm
->cyfry2
[m100
% 10]);
1784 b
= pl_append(b
, odm
->dziesiatki
[m100
/ 10]);
1785 b
= pl_append(b
, odm
->separator_dziesiatek
);
1786 b
= pl_append(b
, odm
->cyfry2
[m100
% 10]);
1787 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, buf
);
1792 pl_odtworz_plik(chan
, language
, audiofd
, ctrlfd
, ints
, pl_rzad_na_tekst(odm
, i
, rzad
));
1796 /* ast_say_number_full_pl: Polish syntax */
1797 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
)
1807 1000000000.2 miliardy
1808 1000000000.5 miliardow
1872 70m siedemdziesieciu
1884 90m dziewiedziesieciu
1886 and combinations of eg.: 20_1, 30m_3m, etc...
1890 char *zenski_cyfry
[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
1892 char *zenski_cyfry2
[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
1894 char *meski_cyfry
[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
1896 char *meski_cyfry2
[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
1898 char *meski_setki
[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
1900 char *meski_dziesiatki
[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
1902 char *meski_nastki
[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
1904 char *nijaki_cyfry
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1906 char *nijaki_cyfry2
[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
1908 char *nijaki_setki
[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
1910 char *nijaki_dziesiatki
[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
1912 char *nijaki_nastki
[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
1914 char *rzedy
[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
1916 /* Initialise variables to allow compilation on Debian-stable, etc */
1919 static odmiana
*odmiana_nieosobowa
= NULL
;
1920 static odmiana
*odmiana_meska
= NULL
;
1921 static odmiana
*odmiana_zenska
= NULL
;
1923 if (odmiana_nieosobowa
== NULL
) {
1924 odmiana_nieosobowa
= (odmiana
*) malloc(sizeof(odmiana
));
1926 odmiana_nieosobowa
->separator_dziesiatek
= " ";
1928 memcpy(odmiana_nieosobowa
->cyfry
, nijaki_cyfry
, sizeof(odmiana_nieosobowa
->cyfry
));
1929 memcpy(odmiana_nieosobowa
->cyfry2
, nijaki_cyfry2
, sizeof(odmiana_nieosobowa
->cyfry
));
1930 memcpy(odmiana_nieosobowa
->setki
, nijaki_setki
, sizeof(odmiana_nieosobowa
->setki
));
1931 memcpy(odmiana_nieosobowa
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_nieosobowa
->dziesiatki
));
1932 memcpy(odmiana_nieosobowa
->nastki
, nijaki_nastki
, sizeof(odmiana_nieosobowa
->nastki
));
1933 memcpy(odmiana_nieosobowa
->rzedy
, rzedy
, sizeof(odmiana_nieosobowa
->rzedy
));
1936 if (odmiana_zenska
== NULL
) {
1937 odmiana_zenska
= (odmiana
*) malloc(sizeof(odmiana
));
1939 odmiana_zenska
->separator_dziesiatek
= " ";
1941 memcpy(odmiana_zenska
->cyfry
, zenski_cyfry
, sizeof(odmiana_zenska
->cyfry
));
1942 memcpy(odmiana_zenska
->cyfry2
, zenski_cyfry2
, sizeof(odmiana_zenska
->cyfry
));
1943 memcpy(odmiana_zenska
->setki
, nijaki_setki
, sizeof(odmiana_zenska
->setki
));
1944 memcpy(odmiana_zenska
->dziesiatki
, nijaki_dziesiatki
, sizeof(odmiana_zenska
->dziesiatki
));
1945 memcpy(odmiana_zenska
->nastki
, nijaki_nastki
, sizeof(odmiana_zenska
->nastki
));
1946 memcpy(odmiana_zenska
->rzedy
, rzedy
, sizeof(odmiana_zenska
->rzedy
));
1949 if (odmiana_meska
== NULL
) {
1950 odmiana_meska
= (odmiana
*) malloc(sizeof(odmiana
));
1952 odmiana_meska
->separator_dziesiatek
= " ";
1954 memcpy(odmiana_meska
->cyfry
, meski_cyfry
, sizeof(odmiana_meska
->cyfry
));
1955 memcpy(odmiana_meska
->cyfry2
, meski_cyfry2
, sizeof(odmiana_meska
->cyfry
));
1956 memcpy(odmiana_meska
->setki
, meski_setki
, sizeof(odmiana_meska
->setki
));
1957 memcpy(odmiana_meska
->dziesiatki
, meski_dziesiatki
, sizeof(odmiana_meska
->dziesiatki
));
1958 memcpy(odmiana_meska
->nastki
, meski_nastki
, sizeof(odmiana_meska
->nastki
));
1959 memcpy(odmiana_meska
->rzedy
, rzedy
, sizeof(odmiana_meska
->rzedy
));
1963 if (strncasecmp(options
, "f", 1) == 0)
1965 else if (strncasecmp(options
, "m", 1) == 0)
1968 o
= odmiana_nieosobowa
;
1970 o
= odmiana_nieosobowa
;
1972 powiedz(chan
, language
, audiofd
, ctrlfd
, ints
, o
, 0, num
);
1976 /* ast_say_number_full_pt: Portuguese syntax */
1977 /* Extra sounds needed: */
1978 /* For feminin all sound files end with F */
1979 /* 100E for 100+ something */
1980 /* 1000000S for plural */
1981 /* pt-e for 'and' */
1982 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
)
1986 int mf
= 1; /* +1 = male; -1 = female */
1990 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
1992 if (options
&& !strncasecmp(options
, "f",1))
1995 while (!res
&& num
) {
1997 snprintf(fn
, sizeof(fn
), "digits/minus");
1998 if ( num
> INT_MIN
) {
2003 } else if (num
< 20) {
2004 if ((num
== 1 || num
== 2) && (mf
< 0))
2005 snprintf(fn
, sizeof(fn
), "digits/%dF", num
);
2007 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2009 } else if (num
< 100) {
2010 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 10) * 10);
2014 } else if (num
< 1000) {
2016 snprintf(fn
, sizeof(fn
), "digits/100");
2018 snprintf(fn
, sizeof(fn
), "digits/100E");
2020 if (mf
< 0 && num
> 199)
2021 snprintf(fn
, sizeof(fn
), "digits/%dF", (num
/ 100) * 100);
2023 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 100) * 100);
2028 } else if (num
< 1000000) {
2030 res
= ast_say_number_full_pt(chan
, (num
/ 1000) * mf
, ints
, language
, options
, audiofd
, ctrlfd
);
2034 snprintf(fn
, sizeof(fn
), "digits/1000");
2035 if ((num
% 1000) && ((num
% 1000) < 100 || !(num
% 100)))
2038 } else if (num
< 1000000000) {
2039 res
= ast_say_number_full_pt(chan
, (num
/ 1000000), ints
, language
, options
, audiofd
, ctrlfd
);
2043 snprintf(fn
, sizeof(fn
), "digits/1000000");
2045 snprintf(fn
, sizeof(fn
), "digits/1000000S");
2047 if ((num
% 1000000) &&
2049 ((!((num
/ 1000) % 1000) && ((num
% 1000) < 100 || !(num
% 100))) ||
2050 /* no hundreds and below */
2051 (!(num
% 1000) && (((num
/ 1000) % 1000) < 100 || !((num
/ 1000) % 100))) ) )
2053 num
= num
% 1000000;
2055 /* number is too big */
2056 ast_log(LOG_WARNING
, "Number '%d' is too big to say.", num
);
2060 if (!ast_streamfile(chan
, fn
, language
)) {
2061 if ((audiofd
> -1) && (ctrlfd
> -1))
2062 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2064 res
= ast_waitstream(chan
, ints
);
2066 ast_stopstream(chan
);
2068 if (!res
&& playh
) {
2069 res
= wait_file(chan
, ints
, "digits/pt-e", language
);
2070 ast_stopstream(chan
);
2077 /*! \brief ast_say_number_full_se: Swedish syntax */
2078 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
)
2083 int cn
= 1; /* +1 = commune; -1 = neuter */
2085 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2086 if (options
&& !strncasecmp(options
, "n",1)) cn
= -1;
2088 while (!res
&& (num
|| playh
)) {
2090 snprintf(fn
, sizeof(fn
), "digits/minus");
2091 if ( num
> INT_MIN
) {
2097 snprintf(fn
, sizeof(fn
), "digits/hundred");
2099 } else if (num
< 20) {
2100 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2102 } else if (num
< 100) {
2103 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2104 num
-= ((num
/ 10) * 10);
2105 } else if (num
== 1 && cn
== -1) { /* En eller ett? */
2106 snprintf(fn
, sizeof(fn
), "digits/1N");
2110 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2112 num
-= ((num
/ 100) * 100);
2114 if (num
< 1000000) { /* 1,000,000 */
2115 res
= ast_say_number_full_se(chan
, num
/ 1000, ints
, language
, options
, audiofd
, ctrlfd
);
2120 snprintf(fn
, sizeof(fn
), "digits/thousand");
2122 if (num
< 1000000000) { /* 1,000,000,000 */
2123 res
= ast_say_number_full_se(chan
, num
/ 1000000, ints
, language
, options
, audiofd
, ctrlfd
);
2127 num
= num
% 1000000;
2128 snprintf(fn
, sizeof(fn
), "digits/million");
2130 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2137 if (!ast_streamfile(chan
, fn
, language
)) {
2138 if ((audiofd
> -1) && (ctrlfd
> -1))
2139 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2141 res
= ast_waitstream(chan
, ints
);
2142 ast_stopstream(chan
);
2149 /*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
2150 static int ast_say_number_full_tw(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2156 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2158 while (!res
&& (num
|| playh
)) {
2160 snprintf(fn
, sizeof(fn
), "digits/minus");
2161 if ( num
> INT_MIN
) {
2167 snprintf(fn
, sizeof(fn
), "digits/hundred");
2169 } else if (num
< 10) {
2170 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2172 } else if (num
< 100) {
2173 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
2174 num
-= ((num
/ 10) * 10);
2177 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/100));
2179 num
-= ((num
/ 100) * 100);
2181 if (num
< 1000000) { /* 1,000,000 */
2182 res
= ast_say_number_full_tw(chan
, num
/ 1000, ints
, language
, audiofd
, ctrlfd
);
2186 snprintf(fn
, sizeof(fn
), "digits/thousand");
2188 if (num
< 1000000000) { /* 1,000,000,000 */
2189 res
= ast_say_number_full_tw(chan
, num
/ 1000000, ints
, language
, audiofd
, ctrlfd
);
2192 num
= num
% 1000000;
2193 snprintf(fn
, sizeof(fn
), "digits/million");
2195 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2202 if (!ast_streamfile(chan
, fn
, language
)) {
2203 if ((audiofd
> -1) && (ctrlfd
> -1))
2204 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2206 res
= ast_waitstream(chan
, ints
);
2208 ast_stopstream(chan
);
2215 /*! \brief determine last digits for thousands/millions (ru) */
2216 static int get_lastdigits_ru(int num
) {
2219 } else if (num
< 100) {
2220 return get_lastdigits_ru(num
% 10);
2221 } else if (num
< 1000) {
2222 return get_lastdigits_ru(num
% 100);
2224 return 0; /* number too big */
2228 /*! \brief ast_say_number_full_ru: Russian syntax */
2229 /*! \brief additional files:
2230 n00.gsm (one hundred, two hundred, ...)
2233 thousands-i.gsm (tisyachi)
2234 million-a.gsm (milliona)
2240 where 'n' from 1 to 9
2242 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
)
2248 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2250 while (!res
&& (num
)) {
2252 snprintf(fn
, sizeof(fn
), "digits/minus");
2253 if ( num
> INT_MIN
) {
2258 } else if (num
< 20) {
2259 if (options
&& strlen(options
) == 1 && num
< 3) {
2260 snprintf(fn
, sizeof(fn
), "digits/%d%s", num
, options
);
2262 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
2265 } else if (num
< 100) {
2266 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 10));
2268 } else if (num
< 1000){
2269 snprintf(fn
, sizeof(fn
), "digits/%d", num
- (num
% 100));
2271 } else if (num
< 1000000) { /* 1,000,000 */
2272 lastdigits
= get_lastdigits_ru(num
/ 1000);
2274 if (lastdigits
< 3) {
2275 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, "f", audiofd
, ctrlfd
);
2277 res
= ast_say_number_full_ru(chan
, num
/ 1000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2281 if (lastdigits
== 1) {
2282 snprintf(fn
, sizeof(fn
), "digits/thousand");
2283 } else if (lastdigits
> 1 && lastdigits
< 5) {
2284 snprintf(fn
, sizeof(fn
), "digits/thousands-i");
2286 snprintf(fn
, sizeof(fn
), "digits/thousands");
2289 } else if (num
< 1000000000) { /* 1,000,000,000 */
2290 lastdigits
= get_lastdigits_ru(num
/ 1000000);
2292 res
= ast_say_number_full_ru(chan
, num
/ 1000000, ints
, language
, NULL
, audiofd
, ctrlfd
);
2295 if (lastdigits
== 1) {
2296 snprintf(fn
, sizeof(fn
), "digits/million");
2297 } else if (lastdigits
> 1 && lastdigits
< 5) {
2298 snprintf(fn
, sizeof(fn
), "digits/million-a");
2300 snprintf(fn
, sizeof(fn
), "digits/millions");
2304 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2308 if (!ast_streamfile(chan
, fn
, language
)) {
2309 if ((audiofd
> -1) && (ctrlfd
> -1))
2310 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2312 res
= ast_waitstream(chan
, ints
);
2314 ast_stopstream(chan
);
2321 /*! \brief ast_say_enumeration_full: call language-specific functions */
2322 /* Called from AGI */
2323 static int say_enumeration_full(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2325 if (!strcasecmp(language
,"en") ) { /* English syntax */
2326 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2327 } else if (!strcasecmp(language
, "da") ) { /* Danish syntax */
2328 return(ast_say_enumeration_full_da(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2329 } else if (!strcasecmp(language
, "de") ) { /* German syntax */
2330 return(ast_say_enumeration_full_de(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2331 } else if (!strcasecmp(language
, "he")) { /* Hebrew syntax */
2332 return (ast_say_enumeration_full_he(chan
, num
, ints
, language
, options
, audiofd
, ctrlfd
));
2335 /* Default to english */
2336 return(ast_say_enumeration_full_en(chan
, num
, ints
, language
, audiofd
, ctrlfd
));
2339 /*! \brief ast_say_enumeration_full_en: English syntax */
2340 /* This is the default syntax, if no other syntax defined in this file is used */
2341 static int ast_say_enumeration_full_en(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, int audiofd
, int ctrlfd
)
2346 while (!res
&& num
) {
2348 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2349 if ( num
> INT_MIN
) {
2354 } else if (num
< 20) {
2355 snprintf(fn
, sizeof(fn
), "digits/h-%d", num
);
2357 } else if (num
< 100) {
2358 int tens
= num
/ 10;
2361 snprintf(fn
, sizeof(fn
), "digits/h-%d", (tens
* 10));
2363 snprintf(fn
, sizeof(fn
), "digits/%d", (tens
* 10));
2365 } else if (num
< 1000) {
2366 int hundreds
= num
/ 100;
2368 if (hundreds
> 1 || t
== 1) {
2369 res
= ast_say_number_full_en(chan
, hundreds
, ints
, language
, audiofd
, ctrlfd
);
2374 snprintf(fn
, sizeof(fn
), "digits/hundred");
2376 snprintf(fn
, sizeof(fn
), "digits/h-hundred");
2378 } else if (num
< 1000000) {
2379 int thousands
= num
/ 1000;
2381 if (thousands
> 1 || t
== 1) {
2382 res
= ast_say_number_full_en(chan
, thousands
, ints
, language
, audiofd
, ctrlfd
);
2387 snprintf(fn
, sizeof(fn
), "digits/thousand");
2389 snprintf(fn
, sizeof(fn
), "digits/h-thousand");
2392 } else if (num
< 1000000000) {
2393 int millions
= num
/ 1000000;
2394 num
= num
% 1000000;
2396 res
= ast_say_number_full_en(chan
, millions
, ints
, language
, audiofd
, ctrlfd
);
2400 snprintf(fn
, sizeof(fn
), "digits/million");
2402 snprintf(fn
, sizeof(fn
), "digits/h-million");
2404 } else if (num
< INT_MAX
) {
2405 int billions
= num
/ 1000000000;
2406 num
= num
% 1000000000;
2408 res
= ast_say_number_full_en(chan
, billions
, ints
, language
, audiofd
, ctrlfd
);
2412 snprintf(fn
, sizeof(fn
), "digits/billion");
2414 snprintf(fn
, sizeof(fn
), "digits/h-billion");
2416 } else if (num
== INT_MAX
) {
2417 snprintf(fn
, sizeof(fn
), "digits/h-last");
2420 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2425 if (!ast_streamfile(chan
, fn
, language
)) {
2426 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2427 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2429 res
= ast_waitstream(chan
, ints
);
2432 ast_stopstream(chan
);
2438 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2439 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
)
2441 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2443 char fn
[256] = "", fna
[256] = "";
2446 if (options
&& !strncasecmp(options
, "f",1)) {
2448 } else if (options
&& !strncasecmp(options
, "n",1)) {
2455 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2457 while (!res
&& num
) {
2459 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2460 if ( num
> INT_MIN
) {
2465 } else if (num
< 100 && t
) {
2466 snprintf(fn
, sizeof(fn
), "digits/and");
2468 } else if (num
< 20) {
2469 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2471 } else if (num
< 100) {
2472 int ones
= num
% 10;
2474 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2477 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2480 } else if (num
== 100 && t
== 0) {
2481 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2483 } else if (num
< 1000) {
2484 int hundreds
= num
/ 100;
2486 if (hundreds
== 1) {
2487 snprintf(fn
, sizeof(fn
), "digits/1N");
2489 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2492 snprintf(fna
, sizeof(fna
), "digits/hundred");
2494 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2497 } else if (num
< 1000000) {
2498 int thousands
= num
/ 1000;
2500 if (thousands
== 1) {
2502 snprintf(fn
, sizeof(fn
), "digits/1N");
2503 snprintf(fna
, sizeof(fna
), "digits/thousand");
2506 snprintf(fn
, sizeof(fn
), "digits/1N");
2507 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2509 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2513 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2518 snprintf(fn
, sizeof(fn
), "digits/thousand");
2520 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2524 } else if (num
< 1000000000) {
2525 int millions
= num
/ 1000000;
2526 num
= num
% 1000000;
2527 if (millions
== 1) {
2529 snprintf(fn
, sizeof(fn
), "digits/1F");
2530 snprintf(fna
, sizeof(fna
), "digits/million");
2532 snprintf(fn
, sizeof(fn
), "digits/1N");
2533 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2536 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2541 snprintf(fn
, sizeof(fn
), "digits/millions");
2543 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2547 } else if (num
< INT_MAX
) {
2548 int billions
= num
/ 1000000000;
2549 num
= num
% 1000000000;
2550 if (billions
== 1) {
2552 snprintf(fn
, sizeof(fn
), "digits/1F");
2553 snprintf(fna
, sizeof(fna
), "digits/milliard");
2555 snprintf(fn
, sizeof(fn
), "digits/1N");
2556 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2559 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2563 snprintf(fn
, sizeof(fna
), "digits/milliards");
2565 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2569 } else if (num
== INT_MAX
) {
2570 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2573 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2578 if (!ast_streamfile(chan
, fn
, language
)) {
2579 if ((audiofd
> -1) && (ctrlfd
> -1))
2580 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2582 res
= ast_waitstream(chan
, ints
);
2584 ast_stopstream(chan
);
2586 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2587 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2588 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2590 res
= ast_waitstream(chan
, ints
);
2593 ast_stopstream(chan
);
2601 /*! \brief ast_say_enumeration_full_de: German syntax */
2602 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
)
2604 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2606 char fn
[256] = "", fna
[256] = "";
2609 if (options
&& !strncasecmp(options
, "f",1)) {
2611 } else if (options
&& !strncasecmp(options
, "n",1)) {
2618 return ast_say_digits_full(chan
, 0,ints
, language
, audiofd
, ctrlfd
);
2620 while (!res
&& num
) {
2622 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2623 if ( num
> INT_MIN
) {
2628 } else if (num
< 100 && t
) {
2629 snprintf(fn
, sizeof(fn
), "digits/and");
2631 } else if (num
< 20) {
2632 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2634 } else if (num
< 100) {
2635 int ones
= num
% 10;
2637 snprintf(fn
, sizeof(fn
), "digits/%d-and", ones
);
2640 snprintf(fn
, sizeof(fn
), "digits/h-%d%s", num
, gender
);
2643 } else if (num
== 100 && t
== 0) {
2644 snprintf(fn
, sizeof(fn
), "digits/h-hundred%s", gender
);
2646 } else if (num
< 1000) {
2647 int hundreds
= num
/ 100;
2649 if (hundreds
== 1) {
2650 snprintf(fn
, sizeof(fn
), "digits/1N");
2652 snprintf(fn
, sizeof(fn
), "digits/%d", hundreds
);
2655 snprintf(fna
, sizeof(fna
), "digits/hundred");
2657 snprintf(fna
, sizeof(fna
), "digits/h-hundred%s", gender
);
2660 } else if (num
< 1000000) {
2661 int thousands
= num
/ 1000;
2663 if (thousands
== 1) {
2665 snprintf(fn
, sizeof(fn
), "digits/1N");
2666 snprintf(fna
, sizeof(fna
), "digits/thousand");
2669 snprintf(fn
, sizeof(fn
), "digits/1N");
2670 snprintf(fna
, sizeof(fna
), "digits/h-thousand%s", gender
);
2672 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2676 res
= ast_say_number_full_de(chan
, thousands
, ints
, language
, options
, audiofd
, ctrlfd
);
2681 snprintf(fn
, sizeof(fn
), "digits/thousand");
2683 snprintf(fn
, sizeof(fn
), "digits/h-thousand%s", gender
);
2687 } else if (num
< 1000000000) {
2688 int millions
= num
/ 1000000;
2689 num
= num
% 1000000;
2690 if (millions
== 1) {
2692 snprintf(fn
, sizeof(fn
), "digits/1F");
2693 snprintf(fna
, sizeof(fna
), "digits/million");
2695 snprintf(fn
, sizeof(fn
), "digits/1N");
2696 snprintf(fna
, sizeof(fna
), "digits/h-million%s", gender
);
2699 res
= ast_say_number_full_de(chan
, millions
, ints
, language
, options
, audiofd
, ctrlfd
);
2704 snprintf(fn
, sizeof(fn
), "digits/millions");
2706 snprintf(fn
, sizeof(fn
), "digits/h-million%s", gender
);
2710 } else if (num
< INT_MAX
) {
2711 int billions
= num
/ 1000000000;
2712 num
= num
% 1000000000;
2713 if (billions
== 1) {
2715 snprintf(fn
, sizeof(fn
), "digits/1F");
2716 snprintf(fna
, sizeof(fna
), "digits/milliard");
2718 snprintf(fn
, sizeof(fn
), "digits/1N");
2719 snprintf(fna
, sizeof(fna
), "digits/h-milliard%s", gender
);
2722 res
= ast_say_number_full_de(chan
, billions
, ints
, language
, options
, audiofd
, ctrlfd
);
2726 snprintf(fn
, sizeof(fna
), "digits/milliards");
2728 snprintf(fn
, sizeof(fna
), "digits/h-milliard%s", gender
);
2732 } else if (num
== INT_MAX
) {
2733 snprintf(fn
, sizeof(fn
), "digits/h-last%s", gender
);
2736 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2741 if (!ast_streamfile(chan
, fn
, language
)) {
2742 if ((audiofd
> -1) && (ctrlfd
> -1))
2743 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2745 res
= ast_waitstream(chan
, ints
);
2747 ast_stopstream(chan
);
2749 if (strlen(fna
) != 0 && !ast_streamfile(chan
, fna
, language
)) {
2750 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2751 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2753 res
= ast_waitstream(chan
, ints
);
2756 ast_stopstream(chan
);
2764 static int ast_say_enumeration_full_he(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
, const char *options
, int audiofd
, int ctrlfd
)
2768 int mf
= -1; /* +1 = Masculin; -1 = Feminin */
2769 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: started. num: %d, options=\"%s\"\n", num
, options
);
2771 if (options
&& !strncasecmp(options
, "m", 1)) {
2775 ast_verbose(VERBOSE_PREFIX_3
"ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num
, options
, mf
);
2777 while (!res
&& num
) {
2779 snprintf(fn
, sizeof(fn
), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
2780 if (num
> INT_MIN
) {
2785 } else if (num
< 21) {
2788 snprintf(fn
, sizeof(fn
), "digits/f-0%d", num
);
2790 snprintf(fn
, sizeof(fn
), "digits/f-%d", num
);
2794 snprintf(fn
, sizeof(fn
), "digits/m-0%d", num
);
2796 snprintf(fn
, sizeof(fn
), "digits/m-%d", num
);
2800 } else if ((num
< 100) && num
>= 20) {
2801 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/ 10) * 10);
2803 } else if ((num
>= 100) && (num
< 1000)) {
2804 int tmpnum
= num
/ 100;
2805 snprintf(fn
, sizeof(fn
), "digits/%d00", tmpnum
);
2806 num
= num
- (tmpnum
* 100);
2807 } else if ((num
>= 1000) && (num
< 10000)) {
2808 int tmpnum
= num
/ 1000;
2809 snprintf(fn
, sizeof(fn
), "digits/%dk", tmpnum
);
2810 num
= num
- (tmpnum
* 1000);
2811 } else if (num
< 20000) {
2812 snprintf(fn
, sizeof(fn
), "digits/m-%d", (num
/ 1000));
2814 } else if (num
< 1000000) {
2815 res
= ast_say_number_full_he(chan
, num
/ 1000, ints
, language
, "m", audiofd
, ctrlfd
);
2819 snprintf(fn
, sizeof(fn
), "digits/1k");
2821 } else if (num
< 2000000) {
2822 snprintf(fn
, sizeof(fn
), "digits/1m");
2823 num
= num
% 1000000;
2824 } else if (num
< 3000000) {
2825 snprintf(fn
, sizeof(fn
), "digits/2m");
2826 num
= num
- 2000000;
2827 } else if (num
< 1000000000) {
2828 res
= ast_say_number_full_he(chan
, num
/ 1000000, ints
, language
, "m", audiofd
, ctrlfd
);
2832 snprintf(fn
, sizeof(fn
), "digits/1m");
2833 num
= num
% 1000000;
2835 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
2839 if (!ast_streamfile(chan
, fn
, language
)) {
2840 if ((audiofd
> -1) && (ctrlfd
> -1)) {
2841 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
2843 res
= ast_waitstream(chan
, ints
);
2846 ast_stopstream(chan
);
2852 static int say_date(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2854 if (!strcasecmp(lang
, "en") ) { /* English syntax */
2855 return(ast_say_date_en(chan
, t
, ints
, lang
));
2856 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
2857 return(ast_say_date_da(chan
, t
, ints
, lang
));
2858 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
2859 return(ast_say_date_de(chan
, t
, ints
, lang
));
2860 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
2861 return(ast_say_date_fr(chan
, t
, ints
, lang
));
2862 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
2863 return(ast_say_date_nl(chan
, t
, ints
, lang
));
2864 } else if (!strcasecmp(lang
, "pt") || !strcasecmp(lang
, "pt_BR")) { /* Portuguese syntax */
2865 return(ast_say_date_pt(chan
, t
, ints
, lang
));
2866 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
2867 return(ast_say_date_gr(chan
, t
, ints
, lang
));
2868 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
2869 return(ast_say_date_ge(chan
, t
, ints
, lang
));
2870 } else if (!strcasecmp(lang
, "he")) { /* Hebrew syntax */
2871 return (ast_say_date_he(chan
, t
, ints
, lang
));
2874 /* Default to English */
2875 return(ast_say_date_en(chan
, t
, ints
, lang
));
2878 /* English syntax */
2879 int ast_say_date_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2884 ast_localtime(&t
,&tm
,NULL
);
2886 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2887 res
= ast_streamfile(chan
, fn
, lang
);
2889 res
= ast_waitstream(chan
, ints
);
2892 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2893 res
= ast_streamfile(chan
, fn
, lang
);
2895 res
= ast_waitstream(chan
, ints
);
2898 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2900 res
= ast_waitstream(chan
, ints
);
2902 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
2907 int ast_say_date_da(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2912 ast_localtime(&t
,&tm
,NULL
);
2914 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2915 res
= ast_streamfile(chan
, fn
, lang
);
2917 res
= ast_waitstream(chan
, ints
);
2920 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2922 res
= ast_waitstream(chan
, ints
);
2924 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2925 res
= ast_streamfile(chan
, fn
, lang
);
2927 res
= ast_waitstream(chan
, ints
);
2931 int year
= tm
.tm_year
+ 1900;
2932 if (year
> 1999) { /* year 2000 and later */
2933 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2936 /* I'm not going to handle 1100 and prior */
2937 /* We'll just be silent on the year, instead of bombing out. */
2939 /* year 1100 to 1999. will anybody need this?!? */
2940 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2941 res
= wait_file(chan
, ints
, fn
, lang
);
2943 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2944 if (!res
&& year
% 100 != 0) {
2945 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
2955 int ast_say_date_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
2960 ast_localtime(&t
,&tm
,NULL
);
2962 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
2963 res
= ast_streamfile(chan
, fn
, lang
);
2965 res
= ast_waitstream(chan
, ints
);
2968 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
2970 res
= ast_waitstream(chan
, ints
);
2972 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
2973 res
= ast_streamfile(chan
, fn
, lang
);
2975 res
= ast_waitstream(chan
, ints
);
2979 int year
= tm
.tm_year
+ 1900;
2980 if (year
> 1999) { /* year 2000 and later */
2981 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
2984 /* I'm not going to handle 1100 and prior */
2985 /* We'll just be silent on the year, instead of bombing out. */
2987 /* year 1100 to 1999. will anybody need this?!? */
2988 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
2989 snprintf(fn
,sizeof(fn
), "digits/%d", (year
/ 100) );
2990 res
= wait_file(chan
, ints
, fn
, lang
);
2992 res
= wait_file(chan
,ints
, "digits/hundred", lang
);
2993 if (!res
&& year
% 100 != 0) {
2994 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3004 int ast_say_date_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
3009 ast_localtime(&t
,&tm
,NULL
);
3011 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
3012 res
= ast_streamfile(chan
, fn
, lang
);
3014 res
= ast_waitstream(chan
, ints
);
3017 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
3019 res
= ast_waitstream(chan
, ints
);
3021 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
3022 res
= ast_streamfile(chan
, fn
, lang
);
3024 res
= ast_waitstream(chan
, ints
);
3027 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
3032 int ast_say_date_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
3037 ast_localtime(&t
,&tm
,NULL
);
3039 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
3040 res
= ast_streamfile(chan
, fn
, lang
);
3042 res
= ast_waitstream(chan
, ints
);
3045 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
3047 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
3048 res
= ast_streamfile(chan
, fn
, lang
);
3050 res
= ast_waitstream(chan
, ints
);
3053 res
= ast_waitstream(chan
, ints
);
3055 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
3059 /* Portuguese syntax */
3060 int ast_say_date_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
3066 ast_localtime(&t
, &tm
, NULL
);
3067 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
3069 res
= wait_file(chan
, ints
, fn
, lang
);
3071 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
3073 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
3074 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
3076 res
= wait_file(chan
, ints
, fn
, lang
);
3078 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
3080 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
3086 int ast_say_date_he(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
3091 ast_localtime(&t
, &tm
, NULL
);
3093 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
3094 res
= ast_streamfile(chan
, fn
, lang
);
3096 res
= ast_waitstream(chan
, ints
);
3100 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
3101 res
= ast_streamfile(chan
, fn
, lang
);
3103 res
= ast_waitstream(chan
, ints
);
3107 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, "m");
3110 res
= ast_waitstream(chan
, ints
);
3113 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, "m");
3118 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
)
3120 if (!strcasecmp(lang
, "en") ) { /* English syntax */
3121 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
3122 } else if (!strcasecmp(lang
, "da") ) { /* Danish syntax */
3123 return(ast_say_date_with_format_da(chan
, time
, ints
, lang
, format
, timezone
));
3124 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
3125 return(ast_say_date_with_format_de(chan
, time
, ints
, lang
, format
, timezone
));
3126 } else if (!strcasecmp(lang
, "es") || !strcasecmp(lang
, "mx")) { /* Spanish syntax */
3127 return (ast_say_date_with_format_es(chan
, time
, ints
, lang
, format
, timezone
));
3128 } else if (!strcasecmp(lang
, "he")) { /* Hebrew syntax */
3129 return (ast_say_date_with_format_he(chan
, time
, ints
, lang
, format
, timezone
));
3130 } else if (!strcasecmp(lang
, "fr")) { /* French syntax */
3131 return (ast_say_date_with_format_fr(chan
, time
, ints
, lang
, format
, timezone
));
3132 } else if (!strcasecmp(lang
, "it")) { /* Italian syntax */
3133 return (ast_say_date_with_format_it(chan
, time
, ints
, lang
, format
, timezone
));
3134 } else if (!strcasecmp(lang
, "nl")) { /* Dutch syntax */
3135 return (ast_say_date_with_format_nl(chan
, time
, ints
, lang
, format
, timezone
));
3136 } else if (!strcasecmp(lang
, "pl")) { /* Polish syntax */
3137 return (ast_say_date_with_format_pl(chan
, time
, ints
, lang
, format
, timezone
));
3138 } else if (!strcasecmp(lang
, "pt") || !strcasecmp(lang
, "pt_BR")) { /* Portuguese syntax */
3139 return(ast_say_date_with_format_pt(chan
, time
, ints
, lang
, format
, timezone
));
3140 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
3141 return(ast_say_date_with_format_tw(chan
, time
, ints
, lang
, format
, timezone
));
3142 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
3143 return(ast_say_date_with_format_gr(chan
, time
, ints
, lang
, format
, timezone
));
3146 /* Default to English */
3147 return(ast_say_date_with_format_en(chan
, time
, ints
, lang
, format
, timezone
));
3150 /* English syntax */
3151 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
)
3154 int res
=0, offset
, sndoffset
;
3155 char sndfile
[256], nextmsg
[256];
3158 format
= "ABdY 'digits/at' IMp";
3160 ast_localtime(&time
,&tm
,timezone
);
3162 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3163 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3164 switch (format
[offset
]) {
3165 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3167 /* Literal name of a sound file */
3169 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3170 sndfile
[sndoffset
] = format
[offset
];
3171 sndfile
[sndoffset
] = '\0';
3172 res
= wait_file(chan
,ints
,sndfile
,lang
);
3176 /* Sunday - Saturday */
3177 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3178 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3183 /* January - December */
3184 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3185 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3188 /* Month enumerated */
3189 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, (char *) NULL
);
3193 /* First - Thirtyfirst */
3194 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
3198 if (tm
.tm_year
> 99) {
3199 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
3200 } else if (tm
.tm_year
< 1) {
3201 /* I'm not going to handle 1900 and prior */
3202 /* We'll just be silent on the year, instead of bombing out. */
3204 res
= wait_file(chan
, ints
, "digits/19", lang
);
3206 if (tm
.tm_year
<= 9) {
3208 res
= wait_file(chan
,ints
, "digits/oh", lang
);
3211 res
|= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char *) NULL
);
3218 if (tm
.tm_hour
== 0)
3219 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3220 else if (tm
.tm_hour
> 12)
3221 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3223 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3224 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3229 if (format
[offset
] == 'H') {
3231 if (tm
.tm_hour
< 10) {
3232 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3236 if (tm
.tm_hour
== 0) {
3237 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3241 if (tm
.tm_hour
!= 0) {
3242 int remainder
= tm
.tm_hour
;
3243 if (tm
.tm_hour
> 20) {
3244 res
= wait_file(chan
,ints
, "digits/20",lang
);
3248 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
3249 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3257 if (tm
.tm_min
== 0) {
3258 if (format
[offset
] == 'M') {
3259 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
3261 res
= wait_file(chan
, ints
, "digits/hundred", lang
);
3263 } else if (tm
.tm_min
< 10) {
3264 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3266 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
3267 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3270 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
3276 if (tm
.tm_hour
> 11)
3277 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3279 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3280 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3283 /* Shorthand for "Today", "Yesterday", or ABdY */
3284 /* XXX As emphasized elsewhere, this should the native way in your
3285 * language to say the date, with changes in what you say, depending
3286 * upon how recent the date is. XXX */
3290 time_t beg_today
, tt
;
3292 gettimeofday(&now
,NULL
);
3294 ast_localtime(&tt
,&tmnow
,timezone
);
3295 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3296 /* In any case, it saves not having to do ast_mktime() */
3297 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3298 if (beg_today
< time
) {
3300 res
= wait_file(chan
,ints
, "digits/today",lang
);
3301 } else if (beg_today
- 86400 < time
) {
3303 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3304 } else if (beg_today
- 86400 * 6 < time
) {
3305 /* Within the last week */
3306 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3307 } else if (beg_today
- 2628000 < time
) {
3308 /* Less than a month ago - "Sunday, October third" */
3309 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3310 } else if (beg_today
- 15768000 < time
) {
3311 /* Less than 6 months ago - "August seventh" */
3312 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3314 /* More than 6 months ago - "April nineteenth two thousand three" */
3315 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3320 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
3321 /* XXX As emphasized elsewhere, this should the native way in your
3322 * language to say the date, with changes in what you say, depending
3323 * upon how recent the date is. XXX */
3327 time_t beg_today
, tt
;
3329 gettimeofday(&now
,NULL
);
3331 ast_localtime(&tt
,&tmnow
,timezone
);
3332 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3333 /* In any case, it saves not having to do ast_mktime() */
3334 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3335 if (beg_today
< time
) {
3337 } else if ((beg_today
- 86400) < time
) {
3339 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3340 } else if (beg_today
- 86400 * 6 < time
) {
3341 /* Within the last week */
3342 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "A", timezone
);
3343 } else if (beg_today
- 2628000 < time
) {
3344 /* Less than a month ago - "Sunday, October third" */
3345 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "ABd", timezone
);
3346 } else if (beg_today
- 15768000 < time
) {
3347 /* Less than 6 months ago - "August seventh" */
3348 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "Bd", timezone
);
3350 /* More than 6 months ago - "April nineteenth two thousand three" */
3351 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "BdY", timezone
);
3356 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HM", timezone
);
3360 if (tm
.tm_sec
== 0) {
3361 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3362 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3363 } else if (tm
.tm_sec
< 10) {
3364 res
= wait_file(chan
,ints
, "digits/oh",lang
);
3366 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
3367 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3370 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, (char *) NULL
);
3374 res
= ast_say_date_with_format_en(chan
, time
, ints
, lang
, "HMS", timezone
);
3378 /* Just ignore spaces and tabs */
3381 /* Unknown character */
3382 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3384 /* Jump out on DTMF */
3393 int ast_say_date_with_format_da(struct ast_channel
*chan
, time_t time
, const char *ints
, const char *lang
, const char *format
, const char *timezone
)
3396 int res
=0, offset
, sndoffset
;
3397 char sndfile
[256], nextmsg
[256];
3400 format
= "A dBY HMS";
3402 ast_localtime(&time
,&tm
,timezone
);
3404 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3405 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3406 switch (format
[offset
]) {
3407 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3409 /* Literal name of a sound file */
3411 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3412 sndfile
[sndoffset
] = format
[offset
];
3413 sndfile
[sndoffset
] = '\0';
3414 res
= wait_file(chan
,ints
,sndfile
,lang
);
3418 /* Sunday - Saturday */
3419 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3420 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3425 /* January - December */
3426 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3427 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3430 /* Month enumerated */
3431 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3435 /* First - Thirtyfirst */
3436 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3441 int year
= tm
.tm_year
+ 1900;
3442 if (year
> 1999) { /* year 2000 and later */
3443 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3446 /* I'm not going to handle 1100 and prior */
3447 /* We'll just be silent on the year, instead of bombing out. */
3449 /* year 1100 to 1999. will anybody need this?!? */
3450 /* say 1967 as 'nineteen hundred seven and sixty' */
3451 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3452 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3454 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3455 if (!res
&& year
% 100 != 0) {
3456 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3466 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3467 if (tm
.tm_hour
== 0)
3468 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3469 else if (tm
.tm_hour
> 12)
3470 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3472 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3474 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3478 /* 24-Hour, single digit hours preceeded by "oh" (0) */
3479 if (tm
.tm_hour
< 10 && tm
.tm_hour
> 0) {
3480 res
= wait_file(chan
,ints
, "digits/0",lang
);
3485 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3489 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3490 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3492 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3493 if (tm
.tm_min
== 1) {
3494 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3496 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3503 if (tm
.tm_hour
> 11)
3504 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3506 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3507 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3510 /* Shorthand for "Today", "Yesterday", or AdBY */
3511 /* XXX As emphasized elsewhere, this should the native way in your
3512 * language to say the date, with changes in what you say, depending
3513 * upon how recent the date is. XXX */
3517 time_t beg_today
, tt
;
3519 gettimeofday(&now
,NULL
);
3521 ast_localtime(&tt
,&tmnow
,timezone
);
3522 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3523 /* In any case, it saves not having to do ast_mktime() */
3524 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3525 if (beg_today
< time
) {
3527 res
= wait_file(chan
,ints
, "digits/today",lang
);
3528 } else if (beg_today
- 86400 < time
) {
3530 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3532 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3537 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3538 /* XXX As emphasized elsewhere, this should the native way in your
3539 * language to say the date, with changes in what you say, depending
3540 * upon how recent the date is. XXX */
3544 time_t beg_today
, tt
;
3546 gettimeofday(&now
,NULL
);
3548 ast_localtime(&tt
,&tmnow
,timezone
);
3549 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3550 /* In any case, it saves not having to do ast_mktime() */
3551 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3552 if (beg_today
< time
) {
3554 } else if ((beg_today
- 86400) < time
) {
3556 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3557 } else if (beg_today
- 86400 * 6 < time
) {
3558 /* Within the last week */
3559 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "A", timezone
);
3561 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "AdBY", timezone
);
3566 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HM", timezone
);
3570 res
= wait_file(chan
,ints
, "digits/and",lang
);
3572 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3574 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3579 res
= ast_say_date_with_format_da(chan
, time
, ints
, lang
, "HMS", timezone
);
3583 /* Just ignore spaces and tabs */
3586 /* Unknown character */
3587 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3589 /* Jump out on DTMF */
3598 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
)
3601 int res
=0, offset
, sndoffset
;
3602 char sndfile
[256], nextmsg
[256];
3605 format
= "A dBY HMS";
3607 ast_localtime(&time
,&tm
,timezone
);
3609 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3610 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3611 switch (format
[offset
]) {
3612 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3614 /* Literal name of a sound file */
3616 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3617 sndfile
[sndoffset
] = format
[offset
];
3618 sndfile
[sndoffset
] = '\0';
3619 res
= wait_file(chan
,ints
,sndfile
,lang
);
3623 /* Sunday - Saturday */
3624 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3625 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3630 /* January - December */
3631 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3632 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3635 /* Month enumerated */
3636 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, "m");
3640 /* First - Thirtyfirst */
3641 res
= ast_say_enumeration(chan
, tm
.tm_mday
, ints
, lang
, "m");
3646 int year
= tm
.tm_year
+ 1900;
3647 if (year
> 1999) { /* year 2000 and later */
3648 res
= ast_say_number(chan
, year
, ints
, lang
, (char *) NULL
);
3651 /* I'm not going to handle 1100 and prior */
3652 /* We'll just be silent on the year, instead of bombing out. */
3654 /* year 1100 to 1999. will anybody need this?!? */
3655 /* say 1967 as 'neunzehn hundert sieben und sechzig' */
3656 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (year
/ 100) );
3657 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3659 res
= wait_file(chan
,ints
, "digits/hundred",lang
);
3660 if (!res
&& year
% 100 != 0) {
3661 res
= ast_say_number(chan
, (year
% 100), ints
, lang
, (char *) NULL
);
3671 if (tm
.tm_hour
== 0)
3672 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
3673 else if (tm
.tm_hour
> 12)
3674 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
3676 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
3677 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3679 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3685 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
3687 res
= wait_file(chan
,ints
,"digits/oclock",lang
);
3692 if (tm
.tm_min
> 0 || format
[offset
+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
3693 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
3695 if ( !res
&& format
[offset
+ 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
3696 if (tm
.tm_min
== 1) {
3697 res
= wait_file(chan
,ints
,"digits/minute",lang
);
3699 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
3706 if (tm
.tm_hour
> 11)
3707 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
3709 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
3710 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3713 /* Shorthand for "Today", "Yesterday", or AdBY */
3714 /* XXX As emphasized elsewhere, this should the native way in your
3715 * language to say the date, with changes in what you say, depending
3716 * upon how recent the date is. XXX */
3720 time_t beg_today
, tt
;
3722 gettimeofday(&now
,NULL
);
3724 ast_localtime(&tt
,&tmnow
,timezone
);
3725 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3726 /* In any case, it saves not having to do ast_mktime() */
3727 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3728 if (beg_today
< time
) {
3730 res
= wait_file(chan
,ints
, "digits/today",lang
);
3731 } else if (beg_today
- 86400 < time
) {
3733 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3735 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3740 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
3741 /* XXX As emphasized elsewhere, this should the native way in your
3742 * language to say the date, with changes in what you say, depending
3743 * upon how recent the date is. XXX */
3747 time_t beg_today
, tt
;
3749 gettimeofday(&now
,NULL
);
3751 ast_localtime(&tt
,&tmnow
,timezone
);
3752 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3753 /* In any case, it saves not having to do ast_mktime() */
3754 beg_today
= now
.tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3755 if (beg_today
< time
) {
3757 } else if ((beg_today
- 86400) < time
) {
3759 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3760 } else if (beg_today
- 86400 * 6 < time
) {
3761 /* Within the last week */
3762 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "A", timezone
);
3764 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "AdBY", timezone
);
3769 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HM", timezone
);
3773 res
= wait_file(chan
,ints
, "digits/and",lang
);
3775 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, "f");
3777 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
3782 res
= ast_say_date_with_format_de(chan
, time
, ints
, lang
, "HMS", timezone
);
3786 /* Just ignore spaces and tabs */
3789 /* Unknown character */
3790 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3792 /* Jump out on DTMF */
3800 /* TODO: this probably is not the correct format for doxygen remarks */
3802 /** ast_say_date_with_format_he Say formmated date in Hebrew
3804 * \ref ast_say_date_with_format_en for the details of the options
3806 * Changes from the English version:
3808 * * don't replicate in here the logic of ast_say_number_full_he
3810 * * year is always 4-digit (because it's simpler)
3812 * * added c, x, and X. Mainly for my tests
3814 * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
3817 * * A "ha" is missing in the standard date format, before the 'd'.
3818 * * The numbers of 3000--19000 are not handled well
3820 #define IL_DATE_STR "AdBY"
3821 #define IL_TIME_STR "HM" /* NOTE: In Hebrew we do not support 12 hours, only 24. No AM or PM exists in the Hebrew language */
3822 #define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
3823 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
)
3825 /* TODO: This whole function is cut&paste from
3826 * ast_say_date_with_format_en . Is that considered acceptable?
3829 int res
= 0, offset
, sndoffset
;
3830 char sndfile
[256], nextmsg
[256];
3833 format
= IL_DATE_STR_FULL
;
3836 ast_localtime(&time
, &tm
, timezone
);
3838 for (offset
= 0; format
[offset
] != '\0'; offset
++) {
3839 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3840 switch (format
[offset
]) {
3841 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3843 /* Literal name of a sound file */
3845 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
3846 sndfile
[sndoffset
] = format
[offset
];
3847 sndfile
[sndoffset
] = '\0';
3848 res
= wait_file(chan
,ints
,sndfile
,lang
);
3852 /* Sunday - Saturday */
3853 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
3854 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3859 /* January - December */
3860 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
3861 res
= wait_file(chan
,ints
,nextmsg
,lang
);
3864 case 'e': /* Day of the month */
3865 /* I'm not sure exactly what the parameters
3866 * audiofd and ctrlfd to
3867 * ast_say_number_full_he mean, but it seems
3868 * safe to pass -1 there.
3870 * At least in one of the pathes :-(
3872 res
= ast_say_number_full_he(chan
, tm
.tm_mday
, ints
, lang
, "m", -1, -1);
3874 case 'Y': /* Year */
3875 res
= ast_say_number_full_he(chan
, tm
.tm_year
+1900,
3876 ints
, lang
, "f", -1, -1
3880 case 'l': /* 12-Hour -> we do not support 12 hour based langauges in Hebrew */
3882 case 'k': /* 24-Hour */
3883 res
= ast_say_number_full_he(chan
, tm
.tm_hour
, ints
, lang
, "f", -1, -1);
3885 case 'M': /* Minute */
3886 if (tm
.tm_min
>= 0 && tm
.tm_min
<= 9) /* say a leading zero if needed */
3887 res
= ast_say_number_full_he(chan
, 0, ints
, lang
, "f", -1, -1);
3888 res
= ast_say_number_full_he(chan
, tm
.tm_min
, ints
, lang
, "f", -1, -1);
3892 /* AM/PM - There is no AM/PM in Hebrew... */
3895 /* Shorthand for "Today", "Yesterday", or "date" */
3897 /* Shorthand for "" (today), "Yesterday", A
3898 * (weekday), or "date" */
3899 /* XXX As emphasized elsewhere, this should the native way in your
3900 * language to say the date, with changes in what you say, depending
3901 * upon how recent the date is. XXX */
3905 time_t beg_today
, tt
;
3906 char todo
= format
[offset
]; /* The letter to format*/
3908 gettimeofday(&now
,NULL
);
3910 ast_localtime(&tt
,&tmnow
,timezone
);
3911 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
3912 /* In any case, it saves not having to do ast_mktime() */
3913 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
3914 if (beg_today
< time
) {
3917 res
= wait_file(chan
,
3922 } else if (beg_today
- 86400 < time
) {
3924 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
3925 } else if ((todo
!= 'Q') &&
3926 (beg_today
- 86400 * 6 < time
))
3928 /* Within the last week */
3929 res
= ast_say_date_with_format_he(chan
,
3933 res
= ast_say_date_with_format_he(chan
,
3935 IL_DATE_STR
, timezone
);
3940 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HM", timezone
);
3942 case 'S': /* Seconds */
3943 res
= ast_say_number_full_he(chan
, tm
.tm_sec
,
3944 ints
, lang
, "f", -1, -1
3948 res
= ast_say_date_with_format_he(chan
, time
, ints
, lang
, "HMS", timezone
);
3950 /* c, x, and X seem useful for testing. Not sure
3951 * if thiey're good for the general public */
3953 res
= ast_say_date_with_format_he(chan
, time
,
3954 ints
, lang
, IL_DATE_STR_FULL
, timezone
);
3957 res
= ast_say_date_with_format_he(chan
, time
,
3958 ints
, lang
, IL_DATE_STR
, timezone
);
3960 case 'X': /* Currently not locale-dependent...*/
3961 res
= ast_say_date_with_format_he(chan
, time
,
3962 ints
, lang
, IL_TIME_STR
, timezone
);
3966 /* Just ignore spaces and tabs */
3969 /* Unknown character */
3970 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
3972 /* Jump out on DTMF */
3981 /* Spanish syntax */
3982 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
)
3985 int res
=0, offset
, sndoffset
;
3986 char sndfile
[256], nextmsg
[256];
3989 format
= "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
3991 ast_localtime(&time
,&tm
,timezone
);
3993 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
3994 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
3995 switch (format
[offset
]) {
3996 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
3998 /* Literal name of a sound file */
4000 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4001 sndfile
[sndoffset
] = format
[offset
];
4002 sndfile
[sndoffset
] = '\0';
4003 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
4004 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4008 /* Sunday - Saturday */
4009 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4010 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4015 /* January - December */
4016 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4017 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4020 /* First - Twelfth */
4021 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4022 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4026 /* First - Thirtyfirst */
4027 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4031 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
4036 if (tm
.tm_hour
== 0)
4037 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4038 else if (tm
.tm_hour
> 12)
4039 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4041 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4042 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4047 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, NULL
);
4051 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4056 if (tm
.tm_hour
> 18)
4057 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
4058 else if (tm
.tm_hour
> 12)
4059 res
= wait_file(chan
, ints
, "digits/afternoon", lang
);
4060 else if (tm
.tm_hour
)
4061 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
4064 /* Shorthand for "Today", "Yesterday", or ABdY */
4065 /* XXX As emphasized elsewhere, this should the native way in your
4066 * language to say the date, with changes in what you say, depending
4067 * upon how recent the date is. XXX */
4071 time_t beg_today
, tt
;
4073 gettimeofday(&now
,NULL
);
4075 ast_localtime(&tt
,&tmnow
,timezone
);
4076 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4077 /* In any case, it saves not having to do ast_mktime() */
4078 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4079 if (beg_today
< time
) {
4081 res
= wait_file(chan
,ints
, "digits/today",lang
);
4082 } else if (beg_today
- 86400 < time
) {
4084 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4086 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
4091 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4092 /* XXX As emphasized elsewhere, this should the native way in your
4093 * language to say the date, with changes in what you say, depending
4094 * upon how recent the date is. XXX */
4098 time_t beg_today
, tt
;
4100 gettimeofday(&now
,NULL
);
4102 ast_localtime(&tt
,&tmnow
,timezone
);
4103 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4104 /* In any case, it saves not having to do ast_mktime() */
4105 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4106 if (beg_today
< time
) {
4108 res
= wait_file(chan
,ints
, "digits/today",lang
);
4109 } else if ((beg_today
- 86400) < time
) {
4111 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4112 } else if (beg_today
- 86400 * 6 < time
) {
4113 /* Within the last week */
4114 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "A", timezone
);
4116 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone
);
4121 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "H 'digits/y' M", timezone
);
4125 if (tm
.tm_sec
== 0) {
4126 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4127 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4128 } else if (tm
.tm_sec
< 10) {
4129 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4131 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4132 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4134 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
4135 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4136 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4139 ten
= (tm
.tm_sec
/ 10) * 10;
4140 one
= (tm
.tm_sec
% 10);
4141 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
4142 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4144 /* Fifty, not fifty-zero */
4146 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4147 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4153 res
= ast_say_date_with_format_es(chan
, time
, ints
, lang
, "HMS", timezone
);
4157 /* Just ignore spaces and tabs */
4160 /* Unknown character */
4161 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4163 /* Jump out on DTMF */
4174 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
)
4177 int res
=0, offset
, sndoffset
;
4178 char sndfile
[256], nextmsg
[256];
4181 format
= "AdBY 'digits/at' IMp";
4183 ast_localtime(&time
,&tm
,timezone
);
4185 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4186 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4187 switch (format
[offset
]) {
4188 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4190 /* Literal name of a sound file */
4192 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4193 sndfile
[sndoffset
] = format
[offset
];
4194 sndfile
[sndoffset
] = '\0';
4195 res
= wait_file(chan
,ints
,sndfile
,lang
);
4199 /* Sunday - Saturday */
4200 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4201 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4206 /* January - December */
4207 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4208 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4211 /* First - Twelfth */
4212 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4213 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4218 if (tm
.tm_mday
== 1) {
4219 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4220 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4222 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
4227 if (tm
.tm_year
> 99) {
4228 res
= wait_file(chan
,ints
, "digits/2",lang
);
4230 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4232 if (tm
.tm_year
> 100) {
4234 res
= ast_say_number(chan
, tm
.tm_year
- 100, ints
, lang
, (char * ) NULL
);
4238 if (tm
.tm_year
< 1) {
4239 /* I'm not going to handle 1900 and prior */
4240 /* We'll just be silent on the year, instead of bombing out. */
4242 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4244 wait_file(chan
,ints
, "digits/9",lang
);
4245 wait_file(chan
,ints
, "digits/hundred",lang
);
4246 res
= ast_say_number(chan
, tm
.tm_year
, ints
, lang
, (char * ) NULL
);
4254 if (tm
.tm_hour
== 0)
4255 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4256 else if (tm
.tm_hour
> 12)
4257 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4259 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4260 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4262 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4267 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char * ) NULL
);
4269 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
4273 if (tm
.tm_min
== 0) {
4276 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char * ) NULL
);
4281 if (tm
.tm_hour
> 11)
4282 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4284 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4285 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4288 /* Shorthand for "Today", "Yesterday", or AdBY */
4289 /* XXX As emphasized elsewhere, this should the native way in your
4290 * language to say the date, with changes in what you say, depending
4291 * upon how recent the date is. XXX */
4295 time_t beg_today
, tt
;
4297 gettimeofday(&now
,NULL
);
4299 ast_localtime(&tt
,&tmnow
,timezone
);
4300 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4301 /* In any case, it saves not having to do ast_mktime() */
4302 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4303 if (beg_today
< time
) {
4305 res
= wait_file(chan
,ints
, "digits/today",lang
);
4306 } else if (beg_today
- 86400 < time
) {
4308 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4310 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4315 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4316 /* XXX As emphasized elsewhere, this should the native way in your
4317 * language to say the date, with changes in what you say, depending
4318 * upon how recent the date is. XXX */
4322 time_t beg_today
, tt
;
4324 gettimeofday(&now
,NULL
);
4326 ast_localtime(&tt
,&tmnow
,timezone
);
4327 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4328 /* In any case, it saves not having to do ast_mktime() */
4329 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4330 if (beg_today
< time
) {
4332 } else if ((beg_today
- 86400) < time
) {
4334 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4335 } else if (beg_today
- 86400 * 6 < time
) {
4336 /* Within the last week */
4337 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "A", timezone
);
4339 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "AdBY", timezone
);
4344 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HM", timezone
);
4348 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, (char * ) NULL
);
4350 res
= wait_file(chan
,ints
, "digits/second",lang
);
4354 res
= ast_say_date_with_format_fr(chan
, time
, ints
, lang
, "HMS", timezone
);
4358 /* Just ignore spaces and tabs */
4361 /* Unknown character */
4362 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4364 /* Jump out on DTMF */
4372 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
)
4375 int res
=0, offset
, sndoffset
;
4376 char sndfile
[256], nextmsg
[256];
4379 format
= "AdB 'digits/at' IMp";
4381 ast_localtime(&time
,&tm
,timezone
);
4383 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4384 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4385 switch (format
[offset
]) {
4386 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4388 /* Literal name of a sound file */
4390 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4391 sndfile
[sndoffset
] = format
[offset
];
4392 sndfile
[sndoffset
] = '\0';
4393 res
= wait_file(chan
,ints
,sndfile
,lang
);
4397 /* Sunday - Saturday */
4398 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4399 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4404 /* January - December */
4405 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4406 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4409 /* First - Twelfth */
4410 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4411 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4415 /* First day of the month is spelled as ordinal */
4416 if (tm
.tm_mday
== 1) {
4417 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mday
);
4418 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4421 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
4427 if (tm
.tm_year
> 99) {
4428 res
= wait_file(chan
,ints
, "digits/ore-2000",lang
);
4429 if (tm
.tm_year
> 100) {
4431 /* This works until the end of 2021 */
4432 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4433 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4437 if (tm
.tm_year
< 1) {
4438 /* I'm not going to handle 1900 and prior */
4439 /* We'll just be silent on the year, instead of bombing out. */
4441 res
= wait_file(chan
,ints
, "digits/ore-1900",lang
);
4442 if ((!res
) && (tm
.tm_year
!= 0)) {
4443 if (tm
.tm_year
<= 21) {
4445 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4446 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4448 /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
4450 ten
= tm
.tm_year
/ 10;
4451 one
= tm
.tm_year
% 10;
4452 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4453 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4456 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4457 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4468 if (tm
.tm_hour
== 0)
4469 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4470 else if (tm
.tm_hour
> 12)
4471 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4473 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4474 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4479 if (tm
.tm_hour
== 0) {
4480 res
= wait_file(chan
,ints
, "digits/ore-mezzanotte",lang
);
4481 } else if (tm
.tm_hour
== 1) {
4482 res
= wait_file(chan
,ints
, "digits/ore-una",lang
);
4484 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4489 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4494 if (tm
.tm_hour
> 11)
4495 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4497 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4498 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4501 /* Shorthand for "Today", "Yesterday", or ABdY */
4502 /* XXX As emphasized elsewhere, this should the native way in your
4503 * language to say the date, with changes in what you say, depending
4504 * upon how recent the date is. XXX */
4508 time_t beg_today
, tt
;
4510 gettimeofday(&now
,NULL
);
4512 ast_localtime(&tt
,&tmnow
,timezone
);
4513 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4514 /* In any case, it saves not having to do ast_mktime() */
4515 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4516 if (beg_today
< time
) {
4518 res
= wait_file(chan
,ints
, "digits/today",lang
);
4519 } else if (beg_today
- 86400 < time
) {
4521 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4523 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4528 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4532 time_t beg_today
, tt
;
4534 gettimeofday(&now
,NULL
);
4536 ast_localtime(&tt
,&tmnow
,timezone
);
4537 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4538 /* In any case, it saves not having to do ast_mktime() */
4539 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4540 if (beg_today
< time
) {
4542 } else if ((beg_today
- 86400) < time
) {
4544 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4545 } else if (beg_today
- 86400 * 6 < time
) {
4546 /* Within the last week */
4547 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "A", timezone
);
4549 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "AdB", timezone
);
4554 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HM", timezone
);
4558 if (tm
.tm_sec
== 0) {
4559 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4560 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4561 } else if (tm
.tm_sec
< 10) {
4562 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4564 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4565 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4567 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
4568 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
4569 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4572 ten
= (tm
.tm_sec
/ 10) * 10;
4573 one
= (tm
.tm_sec
% 10);
4574 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
4575 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4577 /* Fifty, not fifty-zero */
4579 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4580 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4586 res
= ast_say_date_with_format_it(chan
, time
, ints
, lang
, "HMS", timezone
);
4590 /* Just ignore spaces and tabs */
4593 /* Unknown character */
4594 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4596 /* Jump out on DTMF */
4605 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
)
4608 int res
=0, offset
, sndoffset
;
4609 char sndfile
[256], nextmsg
[256];
4612 format
= "ABdY 'digits/at' IMp";
4614 ast_localtime(&time
,&tm
,timezone
);
4616 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
4617 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4618 switch (format
[offset
]) {
4619 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4621 /* Literal name of a sound file */
4623 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4624 sndfile
[sndoffset
] = format
[offset
];
4625 sndfile
[sndoffset
] = '\0';
4626 res
= wait_file(chan
,ints
,sndfile
,lang
);
4630 /* Sunday - Saturday */
4631 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4632 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4637 /* January - December */
4638 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4639 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4642 /* First - Twelfth */
4643 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
4644 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4648 /* First - Thirtyfirst */
4649 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, NULL
);
4653 if (tm
.tm_year
> 99) {
4654 res
= wait_file(chan
,ints
, "digits/2",lang
);
4656 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
4658 if (tm
.tm_year
> 100) {
4660 /* This works until the end of 2020 */
4661 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
- 100);
4662 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4666 if (tm
.tm_year
< 1) {
4667 /* I'm not going to handle 1900 and prior */
4668 /* We'll just be silent on the year, instead of bombing out. */
4670 res
= wait_file(chan
,ints
, "digits/19",lang
);
4672 if (tm
.tm_year
<= 9) {
4674 res
= wait_file(chan
,ints
, "digits/oh",lang
);
4676 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4677 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4679 } else if (tm
.tm_year
<= 20) {
4681 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
4682 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4686 ten
= tm
.tm_year
/ 10;
4687 one
= tm
.tm_year
% 10;
4688 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
* 10);
4689 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4692 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
4693 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4704 if (tm
.tm_hour
== 0)
4705 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
4706 else if (tm
.tm_hour
> 12)
4707 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
4709 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
4710 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4715 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
4717 res
= wait_file(chan
,ints
, "digits/nl-uur",lang
);
4722 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
4727 if (tm
.tm_hour
> 11)
4728 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
4730 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
4731 res
= wait_file(chan
,ints
,nextmsg
,lang
);
4734 /* Shorthand for "Today", "Yesterday", or ABdY */
4735 /* XXX As emphasized elsewhere, this should the native way in your
4736 * language to say the date, with changes in what you say, depending
4737 * upon how recent the date is. XXX */
4741 time_t beg_today
, tt
;
4743 gettimeofday(&now
,NULL
);
4745 ast_localtime(&tt
,&tmnow
,timezone
);
4746 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4747 /* In any case, it saves not having to do ast_mktime() */
4748 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4749 if (beg_today
< time
) {
4751 res
= wait_file(chan
,ints
, "digits/today",lang
);
4752 } else if (beg_today
- 86400 < time
) {
4754 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4756 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4761 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
4765 time_t beg_today
, tt
;
4767 gettimeofday(&now
,NULL
);
4769 ast_localtime(&tt
,&tmnow
,timezone
);
4770 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4771 /* In any case, it saves not having to do ast_mktime() */
4772 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4773 if (beg_today
< time
) {
4775 } else if ((beg_today
- 86400) < time
) {
4777 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
4778 } else if (beg_today
- 86400 * 6 < time
) {
4779 /* Within the last week */
4780 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "A", timezone
);
4782 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "ABdY", timezone
);
4787 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HM", timezone
);
4791 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, (char *) NULL
);
4794 res
= ast_say_date_with_format_nl(chan
, time
, ints
, lang
, "HMS", timezone
);
4798 /* Just ignore spaces and tabs */
4801 /* Unknown character */
4802 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
4804 /* Jump out on DTMF */
4813 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
)
4816 int res
=0, offset
, sndoffset
;
4817 char sndfile
[256], nextmsg
[256];
4819 ast_localtime(&thetime
, &tm
, timezone
);
4821 for (offset
= 0 ; format
[offset
] != '\0' ; offset
++) {
4823 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
4824 switch (format
[offset
]) {
4825 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
4827 /* Literal name of a sound file */
4829 for (sndoffset
= 0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
4830 sndfile
[sndoffset
] = format
[offset
];
4831 sndfile
[sndoffset
] = '\0';
4832 res
= wait_file(chan
, ints
, sndfile
, lang
);
4836 /* Sunday - Saturday */
4837 snprintf(nextmsg
, sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
4838 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4843 /* January - December */
4844 snprintf(nextmsg
, sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
4845 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4848 /* Month enumerated */
4849 res
= ast_say_enumeration(chan
, (tm
.tm_mon
+ 1), ints
, lang
, NULL
);
4853 /* First - Thirtyfirst */
4854 remainder
= tm
.tm_mday
;
4855 if (tm
.tm_mday
> 30) {
4856 res
= wait_file(chan
, ints
, "digits/h-30", lang
);
4859 if (tm
.tm_mday
> 20 && tm
.tm_mday
< 30) {
4860 res
= wait_file(chan
, ints
, "digits/h-20", lang
);
4864 snprintf(nextmsg
, sizeof(nextmsg
), "digits/h-%d", remainder
);
4865 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4870 if (tm
.tm_year
> 100) {
4871 res
= wait_file(chan
, ints
, "digits/2", lang
);
4873 res
= wait_file(chan
, ints
, "digits/1000.2",lang
);
4874 if (tm
.tm_year
> 100) {
4876 res
= ast_say_enumeration(chan
, tm
.tm_year
- 100, ints
, lang
, NULL
);
4878 } else if (tm
.tm_year
== 100) {
4879 res
= wait_file(chan
, ints
, "digits/h-2000", lang
);
4881 if (tm
.tm_year
< 1) {
4882 /* I'm not going to handle 1900 and prior */
4883 /* We'll just be silent on the year, instead of bombing out. */
4886 res
= wait_file(chan
, ints
, "digits/1000", lang
);
4888 wait_file(chan
, ints
, "digits/900", lang
);
4889 res
= ast_say_enumeration(chan
, tm
.tm_year
, ints
, lang
, NULL
);
4894 wait_file(chan
, ints
, "digits/year", lang
);
4899 if (tm
.tm_hour
== 0)
4900 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-12");
4901 else if (tm
.tm_hour
> 12)
4902 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
- 12);
4904 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4906 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4911 if (tm
.tm_hour
!= 0) {
4912 snprintf(nextmsg
, sizeof(nextmsg
), "digits/t-%d", tm
.tm_hour
);
4913 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4915 res
= wait_file(chan
, ints
, "digits/t-24", lang
);
4920 if (tm
.tm_min
== 0) {
4921 if (format
[offset
] == 'M') {
4922 res
= wait_file(chan
, ints
, "digits/oclock", lang
);
4924 res
= wait_file(chan
, ints
, "digits/100", lang
);
4927 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4932 if (tm
.tm_hour
> 11)
4933 snprintf(nextmsg
, sizeof(nextmsg
), "digits/p-m");
4935 snprintf(nextmsg
, sizeof(nextmsg
), "digits/a-m");
4936 res
= wait_file(chan
, ints
, nextmsg
, lang
);
4939 /* Shorthand for "Today", "Yesterday", or AdBY */
4941 time_t tv_sec
= time(NULL
);
4945 ast_localtime(&tv_sec
,&tmnow
, timezone
);
4946 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4947 /* In any case, it saves not having to do ast_mktime() */
4948 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4949 if (beg_today
< thetime
) {
4951 res
= wait_file(chan
, ints
, "digits/today", lang
);
4952 } else if (beg_today
- 86400 < thetime
) {
4954 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4956 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4961 /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
4963 time_t tv_sec
= time(NULL
);
4967 ast_localtime(&tv_sec
, &tmnow
, timezone
);
4968 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
4969 /* In any case, it saves not having to do ast_mktime() */
4970 beg_today
= tv_sec
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
4971 if (beg_today
< thetime
) {
4973 } else if ((beg_today
- 86400) < thetime
) {
4975 res
= wait_file(chan
, ints
, "digits/yesterday", lang
);
4976 } else if (beg_today
- 86400 * 6 < thetime
) {
4977 /* Within the last week */
4978 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "A", timezone
);
4980 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "AdBY", timezone
);
4985 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HM", timezone
);
4989 res
= wait_file(chan
, ints
, "digits/and", lang
);
4991 if (tm
.tm_sec
== 1) {
4992 res
= wait_file(chan
, ints
, "digits/1z", lang
);
4994 res
= wait_file(chan
, ints
, "digits/second-a", lang
);
4996 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
4999 ten
= tm
.tm_sec
/ 10;
5000 one
= tm
.tm_sec
% 10;
5002 if (one
> 1 && one
< 5 && ten
!= 1)
5003 res
= wait_file(chan
,ints
, "digits/seconds",lang
);
5005 res
= wait_file(chan
,ints
, "digits/second",lang
);
5011 res
= ast_say_date_with_format(chan
, thetime
, ints
, lang
, "HMS", timezone
);
5015 /* Just ignore spaces and tabs */
5018 /* Unknown character */
5019 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5021 /* Jump out on DTMF */
5028 /* Portuguese syntax */
5029 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
)
5032 int res
=0, offset
, sndoffset
;
5033 char sndfile
[256], nextmsg
[256];
5036 format
= "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
5038 ast_localtime(&time
,&tm
,timezone
);
5040 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
5041 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
5042 switch (format
[offset
]) {
5043 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5045 /* Literal name of a sound file */
5047 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
5048 sndfile
[sndoffset
] = format
[offset
];
5049 sndfile
[sndoffset
] = '\0';
5050 snprintf(nextmsg
,sizeof(nextmsg
), "%s", sndfile
);
5051 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5055 /* Sunday - Saturday */
5056 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
5057 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5062 /* January - December */
5063 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
5064 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5067 /* First - Twelfth */
5068 if (!strcasecmp(lang
, "pt_BR")) {
5069 res
= ast_say_number(chan
, tm
.tm_mon
+1, ints
, lang
, (char *) NULL
);
5071 snprintf(nextmsg
,sizeof(nextmsg
), "digits/h-%d", tm
.tm_mon
+1);
5072 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5077 /* First - Thirtyfirst */
5078 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5082 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5087 if (!strcasecmp(lang
, "pt_BR")) {
5088 if (tm
.tm_hour
== 0) {
5089 if (format
[offset
] == 'I')
5090 res
= wait_file(chan
, ints
, "digits/pt-a", lang
);
5092 res
= wait_file(chan
, ints
, "digits/pt-meianoite", lang
);
5093 } else if (tm
.tm_hour
== 12) {
5094 if (format
[offset
] == 'I')
5095 res
= wait_file(chan
, ints
, "digits/pt-ao", lang
);
5097 res
= wait_file(chan
, ints
, "digits/pt-meiodia", lang
);
5099 if (format
[offset
] == 'I') {
5100 if ((tm
.tm_hour
% 12) != 1)
5101 res
= wait_file(chan
, ints
, "digits/pt-as", lang
);
5103 res
= wait_file(chan
, ints
, "digits/pt-a", lang
);
5106 res
= ast_say_number(chan
, (tm
.tm_hour
% 12), ints
, lang
, "f");
5109 if (tm
.tm_hour
== 0) {
5110 if (format
[offset
] == 'I')
5111 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
5113 res
= wait_file(chan
, ints
, "digits/pt-meianoite", lang
);
5115 else if (tm
.tm_hour
== 12) {
5116 if (format
[offset
] == 'I')
5117 res
= wait_file(chan
, ints
, "digits/pt-ao", lang
);
5119 res
= wait_file(chan
, ints
, "digits/pt-meiodia", lang
);
5122 if (format
[offset
] == 'I') {
5123 res
= wait_file(chan
, ints
, "digits/pt-ah", lang
);
5124 if ((tm
.tm_hour
% 12) != 1)
5126 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5129 res
= ast_say_number(chan
, (tm
.tm_hour
% 12), ints
, lang
, "f");
5136 if (!strcasecmp(lang
, "pt_BR")) {
5137 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5138 if ((!res
) && (format
[offset
] == 'H')) {
5139 if (tm
.tm_hour
> 1) {
5140 res
= wait_file(chan
,ints
,"digits/hours",lang
);
5142 res
= wait_file(chan
,ints
,"digits/hour",lang
);
5146 res
= ast_say_number(chan
, -tm
.tm_hour
, ints
, lang
, NULL
);
5148 if (tm
.tm_hour
!= 0) {
5149 int remainder
= tm
.tm_hour
;
5150 if (tm
.tm_hour
> 20) {
5151 res
= wait_file(chan
,ints
, "digits/20",lang
);
5155 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", remainder
);
5156 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5164 if (!strcasecmp(lang
, "pt_BR")) {
5165 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, NULL
);
5167 if (tm
.tm_min
> 1) {
5168 res
= wait_file(chan
,ints
,"digits/minutes",lang
);
5170 res
= wait_file(chan
,ints
,"digits/minute",lang
);
5174 if (tm
.tm_min
== 0) {
5175 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
5176 if (tm
.tm_hour
!= 1)
5178 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5180 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5187 if (!strcasecmp(lang
, "pt_BR")) {
5188 if ((tm
.tm_hour
!= 0) && (tm
.tm_hour
!= 12)) {
5189 res
= wait_file(chan
, ints
, "digits/pt-da", lang
);
5191 if ((tm
.tm_hour
>= 0) && (tm
.tm_hour
< 12))
5192 res
= wait_file(chan
, ints
, "digits/morning", lang
);
5193 else if ((tm
.tm_hour
>= 12) && (tm
.tm_hour
< 18))
5194 res
= wait_file(chan
, ints
, "digits/afternoon", lang
);
5195 else res
= wait_file(chan
, ints
, "digits/night", lang
);
5199 if (tm
.tm_hour
> 12)
5200 res
= wait_file(chan
, ints
, "digits/p-m", lang
);
5201 else if (tm
.tm_hour
&& tm
.tm_hour
< 12)
5202 res
= wait_file(chan
, ints
, "digits/a-m", lang
);
5206 /* Shorthand for "Today", "Yesterday", or ABdY */
5207 /* XXX As emphasized elsewhere, this should the native way in your
5208 * language to say the date, with changes in what you say, depending
5209 * upon how recent the date is. XXX */
5213 time_t beg_today
, tt
;
5215 gettimeofday(&now
,NULL
);
5217 ast_localtime(&tt
,&tmnow
,timezone
);
5218 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5219 /* In any case, it saves not having to do ast_mktime() */
5220 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5221 if (beg_today
< time
) {
5223 res
= wait_file(chan
,ints
, "digits/today",lang
);
5224 } else if (beg_today
- 86400 < time
) {
5226 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5228 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5233 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5234 /* XXX As emphasized elsewhere, this should the native way in your
5235 * language to say the date, with changes in what you say, depending
5236 * upon how recent the date is. XXX */
5240 time_t beg_today
, tt
;
5242 gettimeofday(&now
,NULL
);
5244 ast_localtime(&tt
,&tmnow
,timezone
);
5245 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5246 /* In any case, it saves not having to do ast_mktime() */
5247 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5248 if (beg_today
< time
) {
5250 } else if ((beg_today
- 86400) < time
) {
5252 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5253 } else if (beg_today
- 86400 * 6 < time
) {
5254 /* Within the last week */
5255 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "A", timezone
);
5257 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone
);
5262 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "H 'digits/pt-e' M", timezone
);
5266 if (!strcasecmp(lang
, "pt_BR")) {
5267 res
= ast_say_number(chan
, tm
.tm_sec
, ints
, lang
, NULL
);
5269 if (tm
.tm_sec
> 1) {
5270 res
= wait_file(chan
,ints
,"digits/seconds",lang
);
5272 res
= wait_file(chan
,ints
,"digits/second",lang
);
5274 } else if (tm
.tm_sec
< 10) {
5275 res
= wait_file(chan
,ints
, "digits/oh",lang
);
5277 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5278 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5280 } else if ((tm
.tm_sec
< 21) || (tm
.tm_sec
% 10 == 0)) {
5281 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5282 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5285 ten
= (tm
.tm_sec
/ 10) * 10;
5286 one
= (tm
.tm_sec
% 10);
5287 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", ten
);
5288 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5290 /* Fifty, not fifty-zero */
5292 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", one
);
5293 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5300 res
= ast_say_date_with_format_pt(chan
, time
, ints
, lang
, "HMS", timezone
);
5304 /* Just ignore spaces and tabs */
5307 /* Unknown character */
5308 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5310 /* Jump out on DTMF */
5318 /* Taiwanese / Chinese syntax */
5319 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
)
5322 int res
=0, offset
, sndoffset
;
5323 char sndfile
[256], nextmsg
[256];
5328 ast_localtime(&time
,&tm
,timezone
);
5330 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
5331 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
5332 switch (format
[offset
]) {
5333 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
5335 /* Literal name of a sound file */
5337 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
5338 sndfile
[sndoffset
] = format
[offset
];
5339 sndfile
[sndoffset
] = '\0';
5340 res
= wait_file(chan
,ints
,sndfile
,lang
);
5344 /* Sunday - Saturday */
5345 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
5346 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5352 /* January - December */
5353 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
5354 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5358 /* First - Thirtyfirst */
5359 if (!(tm
.tm_mday
% 10) || (tm
.tm_mday
< 10)) {
5360 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_mday
);
5361 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5363 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_mday
- (tm
.tm_mday
% 10));
5364 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5366 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_mday
% 10);
5367 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5370 if (!res
) res
= wait_file(chan
,ints
,"digits/day",lang
);
5374 if (tm
.tm_year
> 99) {
5375 res
= wait_file(chan
,ints
, "digits/2",lang
);
5377 res
= wait_file(chan
,ints
, "digits/thousand",lang
);
5379 if (tm
.tm_year
> 100) {
5381 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) / 10);
5382 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5384 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", (tm
.tm_year
- 100) % 10);
5385 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5390 res
= wait_file(chan
,ints
, "digits/year",lang
);
5393 if (tm
.tm_year
< 1) {
5394 /* I'm not going to handle 1900 and prior */
5395 /* We'll just be silent on the year, instead of bombing out. */
5397 res
= wait_file(chan
,ints
, "digits/1",lang
);
5399 res
= wait_file(chan
,ints
, "digits/9",lang
);
5402 if (tm
.tm_year
<= 9) {
5404 res
= wait_file(chan
,ints
, "digits/0",lang
);
5406 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
);
5407 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5411 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
/ 10);
5412 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5414 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_year
% 10);
5415 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5421 res
= wait_file(chan
,ints
, "digits/year",lang
);
5428 if (tm
.tm_hour
== 0)
5429 snprintf(nextmsg
,sizeof(nextmsg
), "digits/12");
5430 else if (tm
.tm_hour
> 12)
5431 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- 12);
5433 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5434 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5436 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5440 if (tm
.tm_hour
< 10) {
5441 res
= wait_file(chan
, ints
, "digits/0", lang
);
5445 if (!(tm
.tm_hour
% 10) || tm
.tm_hour
< 10) {
5446 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
);
5447 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5449 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
- (tm
.tm_hour
% 10));
5450 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5452 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_hour
% 10);
5453 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5457 res
= wait_file(chan
,ints
, "digits/oclock",lang
);
5462 if (!(tm
.tm_min
% 10) || tm
.tm_min
< 10) {
5463 if (tm
.tm_min
< 10) {
5464 res
= wait_file(chan
, ints
, "digits/0", lang
);
5466 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
);
5467 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5469 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
- (tm
.tm_min
% 10));
5470 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5472 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_min
% 10);
5473 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5477 res
= wait_file(chan
,ints
, "digits/minute",lang
);
5483 if (tm
.tm_hour
> 11)
5484 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
5486 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
5487 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5490 /* Shorthand for "Today", "Yesterday", or ABdY */
5491 /* XXX As emphasized elsewhere, this should the native way in your
5492 * language to say the date, with changes in what you say, depending
5493 * upon how recent the date is. XXX */
5497 time_t beg_today
, tt
;
5499 gettimeofday(&now
,NULL
);
5501 ast_localtime(&tt
,&tmnow
,timezone
);
5502 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5503 /* In any case, it saves not having to do ast_mktime() */
5504 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5505 if (beg_today
< time
) {
5507 res
= wait_file(chan
,ints
, "digits/today",lang
);
5508 } else if (beg_today
- 86400 < time
) {
5510 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5512 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5517 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
5518 /* XXX As emphasized elsewhere, this should the native way in your
5519 * language to say the date, with changes in what you say, depending
5520 * upon how recent the date is. XXX */
5524 time_t beg_today
, tt
;
5526 gettimeofday(&now
,NULL
);
5528 ast_localtime(&tt
,&tmnow
,timezone
);
5529 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
5530 /* In any case, it saves not having to do ast_mktime() */
5531 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
5532 if (beg_today
< time
) {
5534 } else if ((beg_today
- 86400) < time
) {
5536 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
5537 } else if (beg_today
- 86400 * 6 < time
) {
5538 /* Within the last week */
5539 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "A", timezone
);
5541 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "YBdA", timezone
);
5546 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "kM", timezone
);
5550 if (!(tm
.tm_sec
% 10) || tm
.tm_sec
< 10) {
5551 if (tm
.tm_sec
< 10) {
5552 res
= wait_file(chan
, ints
, "digits/0", lang
);
5554 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
);
5555 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5557 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
- (tm
.tm_sec
% 10));
5558 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5560 snprintf(nextmsg
,sizeof(nextmsg
), "digits/%d", tm
.tm_sec
% 10);
5561 res
= wait_file(chan
,ints
,nextmsg
,lang
);
5565 res
= wait_file(chan
,ints
, "digits/second",lang
);
5569 res
= ast_say_date_with_format_tw(chan
, time
, ints
, lang
, "HMS", timezone
);
5573 /* Just ignore spaces and tabs */
5576 /* Unknown character */
5577 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
5579 /* Jump out on DTMF */
5587 static int say_time(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5589 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5590 return(ast_say_time_en(chan
, t
, ints
, lang
));
5591 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5592 return(ast_say_time_de(chan
, t
, ints
, lang
));
5593 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5594 return(ast_say_time_fr(chan
, t
, ints
, lang
));
5595 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5596 return(ast_say_time_nl(chan
, t
, ints
, lang
));
5597 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5598 return(ast_say_time_pt(chan
, t
, ints
, lang
));
5599 } else if (!strcasecmp(lang
, "pt_BR") ) { /* Brazilian Portuguese syntax */
5600 return(ast_say_time_pt_BR(chan
, t
, ints
, lang
));
5601 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5602 return(ast_say_time_tw(chan
, t
, ints
, lang
));
5603 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5604 return(ast_say_time_gr(chan
, t
, ints
, lang
));
5605 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
5606 return(ast_say_time_ge(chan
, t
, ints
, lang
));
5607 } else if (!strcasecmp(lang
, "he")) { /* Hebrew syntax */
5608 return (ast_say_time_he(chan
, t
, ints
, lang
));
5611 /* Default to English */
5612 return(ast_say_time_en(chan
, t
, ints
, lang
));
5615 /* English syntax */
5616 int ast_say_time_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5622 ast_localtime(&t
, &tm
, NULL
);
5626 else if (hour
== 12)
5628 else if (hour
> 12) {
5633 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5635 if (tm
.tm_min
> 9) {
5637 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5638 } else if (tm
.tm_min
) {
5640 res
= ast_streamfile(chan
, "digits/oh", lang
);
5642 res
= ast_waitstream(chan
, ints
);
5644 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5647 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5649 res
= ast_waitstream(chan
, ints
);
5653 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5656 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5659 res
= ast_waitstream(chan
, ints
);
5664 int ast_say_time_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5669 ast_localtime(&t
, &tm
, NULL
);
5671 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "n");
5673 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5675 res
= ast_waitstream(chan
, ints
);
5678 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
5683 int ast_say_time_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5688 ast_localtime(&t
, &tm
, NULL
);
5690 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5692 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5695 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5701 int ast_say_time_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5706 ast_localtime(&t
, &tm
, NULL
);
5708 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char *) NULL
);
5710 res
= ast_streamfile(chan
, "digits/nl-uur", lang
);
5712 res
= ast_waitstream(chan
, ints
);
5715 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, NULL
);
5719 /* Portuguese syntax */
5720 int ast_say_time_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5726 ast_localtime(&t
, &tm
, NULL
);
5729 res
= ast_say_number(chan
, hour
, ints
, lang
, "f");
5732 res
= wait_file(chan
, ints
, "digits/pt-e", lang
);
5734 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5737 res
= wait_file(chan
, ints
, "digits/pt-hora", lang
);
5738 if (tm
.tm_hour
!= 1)
5740 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
5743 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5747 /* Brazilian Portuguese syntax */
5748 int ast_say_time_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5753 ast_localtime(&t
, &tm
, NULL
);
5755 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5758 res
= wait_file(chan
, ints
, "digits/hours", lang
);
5760 res
= wait_file(chan
, ints
, "digits/hour", lang
);
5762 if ((!res
) && (tm
.tm_min
)) {
5763 res
= wait_file(chan
, ints
, "digits/pt-e", lang
);
5765 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5768 res
= wait_file(chan
, ints
, "digits/minutes", lang
);
5770 res
= wait_file(chan
, ints
, "digits/minute", lang
);
5776 /* Taiwanese / Chinese syntax */
5777 int ast_say_time_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5783 ast_localtime(&t
, &tm
, NULL
);
5787 else if (hour
== 12)
5789 else if (hour
> 12) {
5795 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5798 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5801 res
= ast_waitstream(chan
, ints
);
5803 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5805 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5807 res
= ast_waitstream(chan
, ints
);
5809 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5811 res
= ast_streamfile(chan
, "digits/minute", lang
);
5813 res
= ast_waitstream(chan
, ints
);
5818 int ast_say_time_he(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5824 ast_localtime(&t
, &tm
, NULL
);
5830 res
= ast_say_number_full_he(chan
, hour
, ints
, lang
, "f", -1, -1);
5832 if (tm
.tm_min
> 9) {
5834 res
= ast_say_number_full_he(chan
, tm
.tm_min
, ints
, lang
, "f", -1, -1);
5835 } else if (tm
.tm_min
) {
5836 if (!res
) { /* say a leading zero if needed */
5837 res
= ast_say_number_full_he(chan
, 0, ints
, lang
, "f", -1, -1);
5840 res
= ast_waitstream(chan
, ints
);
5842 res
= ast_say_number_full_he(chan
, tm
.tm_min
, ints
, lang
, "f", -1, -1);
5845 res
= ast_waitstream(chan
, ints
);
5848 res
= ast_waitstream(chan
, ints
);
5851 static int say_datetime(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5853 if (!strcasecmp(lang
, "en") ) { /* English syntax */
5854 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5855 } else if (!strcasecmp(lang
, "de") ) { /* German syntax */
5856 return(ast_say_datetime_de(chan
, t
, ints
, lang
));
5857 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
5858 return(ast_say_datetime_fr(chan
, t
, ints
, lang
));
5859 } else if (!strcasecmp(lang
, "nl") ) { /* Dutch syntax */
5860 return(ast_say_datetime_nl(chan
, t
, ints
, lang
));
5861 } else if (!strcasecmp(lang
, "pt") ) { /* Portuguese syntax */
5862 return(ast_say_datetime_pt(chan
, t
, ints
, lang
));
5863 } else if (!strcasecmp(lang
, "pt_BR") ) { /* Brazilian Portuguese syntax */
5864 return(ast_say_datetime_pt_BR(chan
, t
, ints
, lang
));
5865 } else if (!strcasecmp(lang
, "tw") || !strcasecmp(lang
, "zh") ) { /* Taiwanese / Chinese syntax */
5866 return(ast_say_datetime_tw(chan
, t
, ints
, lang
));
5867 } else if (!strcasecmp(lang
, "gr") ) { /* Greek syntax */
5868 return(ast_say_datetime_gr(chan
, t
, ints
, lang
));
5869 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
5870 return(ast_say_datetime_ge(chan
, t
, ints
, lang
));
5871 } else if (!strcasecmp(lang
, "he")) { /* Hebrew syntax */
5872 return (ast_say_datetime_he(chan
, t
, ints
, lang
));
5875 /* Default to English */
5876 return(ast_say_datetime_en(chan
, t
, ints
, lang
));
5879 /* English syntax */
5880 int ast_say_datetime_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5887 ast_localtime(&t
, &tm
, NULL
);
5889 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5890 res
= ast_streamfile(chan
, fn
, lang
);
5892 res
= ast_waitstream(chan
, ints
);
5895 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5896 res
= ast_streamfile(chan
, fn
, lang
);
5898 res
= ast_waitstream(chan
, ints
);
5901 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5906 else if (hour
== 12)
5908 else if (hour
> 12) {
5913 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
5915 if (tm
.tm_min
> 9) {
5917 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5918 } else if (tm
.tm_min
) {
5920 res
= ast_streamfile(chan
, "digits/oh", lang
);
5922 res
= ast_waitstream(chan
, ints
);
5924 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5927 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5929 res
= ast_waitstream(chan
, ints
);
5933 res
= ast_streamfile(chan
, "digits/p-m", lang
);
5936 res
= ast_streamfile(chan
, "digits/a-m", lang
);
5939 res
= ast_waitstream(chan
, ints
);
5941 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
5946 int ast_say_datetime_de(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5951 ast_localtime(&t
, &tm
, NULL
);
5952 res
= ast_say_date(chan
, t
, ints
, lang
);
5954 ast_say_time(chan
, t
, ints
, lang
);
5960 int ast_say_datetime_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
5966 ast_localtime(&t
, &tm
, NULL
);
5969 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
5972 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
5973 res
= ast_streamfile(chan
, fn
, lang
);
5975 res
= ast_waitstream(chan
, ints
);
5978 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
5979 res
= ast_streamfile(chan
, fn
, lang
);
5981 res
= ast_waitstream(chan
, ints
);
5985 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, "f");
5987 res
= ast_streamfile(chan
, "digits/oclock", lang
);
5988 if (tm
.tm_min
> 0) {
5990 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
5993 res
= ast_waitstream(chan
, ints
);
5995 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
6000 int ast_say_datetime_nl(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6005 ast_localtime(&t
, &tm
, NULL
);
6006 res
= ast_say_date(chan
, t
, ints
, lang
);
6008 res
= ast_streamfile(chan
, "digits/nl-om", lang
);
6010 res
= ast_waitstream(chan
, ints
);
6013 ast_say_time(chan
, t
, ints
, lang
);
6017 /* Portuguese syntax */
6018 int ast_say_datetime_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6025 ast_localtime(&t
, &tm
, NULL
);
6027 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6028 res
= ast_streamfile(chan
, fn
, lang
);
6030 res
= ast_waitstream(chan
, ints
);
6033 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6034 res
= ast_streamfile(chan
, fn
, lang
);
6036 res
= ast_waitstream(chan
, ints
);
6039 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6044 else if (hour
== 12)
6046 else if (hour
> 12) {
6051 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
6053 if (tm
.tm_min
> 9) {
6055 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
6056 } else if (tm
.tm_min
) {
6058 res
= ast_streamfile(chan
, "digits/oh", lang
);
6060 res
= ast_waitstream(chan
, ints
);
6062 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
6065 res
= ast_streamfile(chan
, "digits/oclock", lang
);
6067 res
= ast_waitstream(chan
, ints
);
6071 res
= ast_streamfile(chan
, "digits/p-m", lang
);
6074 res
= ast_streamfile(chan
, "digits/a-m", lang
);
6077 res
= ast_waitstream(chan
, ints
);
6079 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
6083 /* Brazilian Portuguese syntax */
6084 int ast_say_datetime_pt_BR(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6089 ast_localtime(&t
, &tm
, NULL
);
6090 res
= ast_say_date(chan
, t
, ints
, lang
);
6092 res
= ast_say_time(chan
, t
, ints
, lang
);
6096 /* Taiwanese / Chinese syntax */
6097 int ast_say_datetime_tw(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6104 ast_localtime(&t
, &tm
, NULL
);
6106 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
6108 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6109 res
= ast_streamfile(chan
, fn
, lang
);
6111 res
= ast_waitstream(chan
, ints
);
6114 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6116 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6117 res
= ast_streamfile(chan
, fn
, lang
);
6119 res
= ast_waitstream(chan
, ints
);
6125 else if (hour
== 12)
6127 else if (hour
> 12) {
6133 res
= ast_streamfile(chan
, "digits/p-m", lang
);
6136 res
= ast_streamfile(chan
, "digits/a-m", lang
);
6139 res
= ast_waitstream(chan
, ints
);
6141 res
= ast_say_number(chan
, hour
, ints
, lang
, (char *) NULL
);
6143 res
= ast_streamfile(chan
, "digits/oclock", lang
);
6145 res
= ast_waitstream(chan
, ints
);
6147 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
6149 res
= ast_streamfile(chan
, "digits/minute", lang
);
6151 res
= ast_waitstream(chan
, ints
);
6156 int ast_say_datetime_he(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6163 ast_localtime(&t
, &tm
, NULL
);
6165 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6166 res
= ast_streamfile(chan
, fn
, lang
);
6168 res
= ast_waitstream(chan
, ints
);
6172 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6173 res
= ast_streamfile(chan
, fn
, lang
);
6175 res
= ast_waitstream(chan
, ints
);
6179 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, "f");
6188 res
= ast_say_number(chan
, hour
, ints
, lang
, "f");
6191 if (tm
.tm_min
> 9) {
6193 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
6195 } else if (tm
.tm_min
) {
6197 /* say a leading zero if needed */
6198 res
= ast_say_number(chan
, 0, ints
, lang
, "f");
6201 res
= ast_waitstream(chan
, ints
);
6204 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, "f");
6208 res
= ast_waitstream(chan
, ints
);
6212 res
= ast_waitstream(chan
, ints
);
6215 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, "f");
6219 static int say_datetime_from_now(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6221 if (!strcasecmp(lang
, "en") ) { /* English syntax */
6222 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
6223 } else if (!strcasecmp(lang
, "fr") ) { /* French syntax */
6224 return(ast_say_datetime_from_now_fr(chan
, t
, ints
, lang
));
6225 } else if (!strcasecmp(lang
, "pt") || !strcasecmp(lang
, "pt_BR")) { /* Portuguese syntax */
6226 return(ast_say_datetime_from_now_pt(chan
, t
, ints
, lang
));
6227 } else if (!strcasecmp(lang
, "ge") ) { /* Georgian syntax */
6228 return(ast_say_datetime_from_now_ge(chan
, t
, ints
, lang
));
6229 } else if (!strcasecmp(lang
, "he")) { /* Georgian syntax */
6230 return (ast_say_datetime_from_now_he(chan
, t
, ints
, lang
));
6233 /* Default to English */
6234 return(ast_say_datetime_from_now_en(chan
, t
, ints
, lang
));
6237 /* English syntax */
6238 int ast_say_datetime_from_now_en(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6249 ast_localtime(&t
, &tm
, NULL
);
6250 ast_localtime(&nowt
,&now
, NULL
);
6251 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6252 if ((daydiff
< 0) || (daydiff
> 6)) {
6253 /* Day of month and month */
6255 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6256 res
= ast_streamfile(chan
, fn
, lang
);
6258 res
= ast_waitstream(chan
, ints
);
6261 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6263 } else if (daydiff
) {
6264 /* Just what day of the week */
6266 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6267 res
= ast_streamfile(chan
, fn
, lang
);
6269 res
= ast_waitstream(chan
, ints
);
6271 } /* Otherwise, it was today */
6273 res
= ast_say_time(chan
, t
, ints
, lang
);
6278 int ast_say_datetime_from_now_fr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6289 ast_localtime(&t
, &tm
, NULL
);
6290 ast_localtime(&nowt
, &now
, NULL
);
6291 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6292 if ((daydiff
< 0) || (daydiff
> 6)) {
6293 /* Day of month and month */
6295 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6296 res
= ast_streamfile(chan
, fn
, lang
);
6298 res
= ast_waitstream(chan
, ints
);
6301 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6303 } else if (daydiff
) {
6304 /* Just what day of the week */
6306 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6307 res
= ast_streamfile(chan
, fn
, lang
);
6309 res
= ast_waitstream(chan
, ints
);
6311 } /* Otherwise, it was today */
6313 res
= ast_say_time(chan
, t
, ints
, lang
);
6317 /* Portuguese syntax */
6318 int ast_say_datetime_from_now_pt(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6329 ast_localtime(&t
, &tm
, NULL
);
6330 ast_localtime(&nowt
, &now
, NULL
);
6331 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6332 if ((daydiff
< 0) || (daydiff
> 6)) {
6333 /* Day of month and month */
6335 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
6337 res
= wait_file(chan
, ints
, "digits/pt-de", lang
);
6338 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6340 res
= wait_file(chan
, ints
, fn
, lang
);
6342 } else if (daydiff
) {
6343 /* Just what day of the week */
6344 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6346 res
= wait_file(chan
, ints
, fn
, lang
);
6347 } /* Otherwise, it was today */
6348 if (!strcasecmp(lang
, "pt_BR")) {
6349 if (tm
.tm_hour
> 1) {
6350 snprintf(fn
, sizeof(fn
), "digits/pt-as");
6352 snprintf(fn
, sizeof(fn
), "digits/pt-a");
6355 res
= wait_file(chan
, ints
, fn
, lang
);
6357 snprintf(fn
, sizeof(fn
), "digits/pt-ah");
6359 res
= wait_file(chan
, ints
, fn
, lang
);
6360 if (tm
.tm_hour
!= 1)
6362 res
= wait_file(chan
, ints
, "digits/pt-sss", lang
);
6364 res
= ast_say_time(chan
, t
, ints
, lang
);
6370 int ast_say_datetime_from_now_he(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6381 ast_localtime(&t
, &tm
, NULL
);
6382 ast_localtime(&nowt
, &now
, NULL
);
6383 daydiff
= now
.tm_yday
- tm
.tm_yday
;
6384 if ((daydiff
< 0) || (daydiff
> 6)) {
6385 /* Day of month and month */
6387 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6388 res
= ast_streamfile(chan
, fn
, lang
);
6390 res
= ast_waitstream(chan
, ints
);
6393 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, "f");
6395 } else if (daydiff
) {
6396 /* Just what day of the week */
6398 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6399 res
= ast_streamfile(chan
, fn
, lang
);
6401 res
= ast_waitstream(chan
, ints
);
6404 } /* Otherwise, it was today */
6406 res
= ast_say_time(chan
, t
, ints
, lang
);
6411 /*********************************** GREEK SUPPORT ***************************************/
6415 * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
6417 static int gr_say_number_female(int num
, struct ast_channel
*chan
, const char *ints
, const char *lang
){
6423 /* ast_log(LOG_DEBUG, "\n\n Saying number female %s %d \n\n",lang, num); */
6425 snprintf(fn
, sizeof(fn
), "digits/female-%d", num
);
6426 res
= wait_file(chan
, ints
, fn
, lang
);
6427 } else if (num
< 13) {
6428 res
= ast_say_number(chan
, num
, ints
, lang
, (char *) NULL
);
6429 } else if (num
<100 ) {
6430 tmp
= (num
/10) * 10;
6432 snprintf(fn
, sizeof(fn
), "digits/%d", tmp
);
6433 res
= ast_streamfile(chan
, fn
, lang
);
6435 res
= ast_waitstream(chan
, ints
);
6437 gr_say_number_female(left
, chan
, ints
, lang
);
6448 * A list of the files that you need to create
6449 -> digits/xilia = "xilia"
6450 -> digits/myrio = "ekatomyrio"
6451 -> digits/thousands = "xiliades"
6452 -> digits/millions = "ektatomyria"
6453 -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
6454 -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
6456 Here we must note that we use digits/tens/100 to utter "ekato"
6457 and digits/hundred-100 to utter "ekaton"
6458 -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
6459 "terakosia". Here again we use hundreds/1000 for "xilia"
6460 and digits/thousnds for "xiliades"
6463 static int ast_say_number_full_gr(struct ast_channel
*chan
, int num
, const char *ints
, const char *language
,int audiofd
, int ctrlfd
)
6471 snprintf(fn
, sizeof(fn
), "digits/0");
6472 res
= ast_streamfile(chan
, fn
, chan
->language
);
6474 return ast_waitstream(chan
, ints
);
6477 while (!res
&& num
) {
6480 snprintf(fn
, sizeof(fn
), "digits/%d", num
);
6482 } else if (num
<= 100) {
6483 /* 13 < num <= 100 */
6484 snprintf(fn
, sizeof(fn
), "digits/%d", (num
/10) * 10);
6485 num
-= ((num
/ 10) * 10);
6486 } else if (num
< 200) {
6487 /* 100 < num < 200 */
6488 snprintf(fn
, sizeof(fn
), "digits/hundred-100");
6489 num
-= ((num
/ 100) * 100);
6490 } else if (num
< 1000) {
6491 /* 200 < num < 1000 */
6492 snprintf(fn
, sizeof(fn
), "digits/hundred-%d", (num
/100)*100);
6493 num
-= ((num
/ 100) * 100);
6494 } else if (num
< 2000){
6495 snprintf(fn
, sizeof(fn
), "digits/xilia");
6496 num
-= ((num
/ 1000) * 1000);
6499 if (num
< 1000000) {
6500 res
= ast_say_number_full_gr(chan
, (num
/ 1000), ints
, chan
->language
, audiofd
, ctrlfd
);
6504 snprintf(fn
, sizeof(fn
), "digits/thousands");
6506 if (num
< 1000000000) { /* 1,000,000,000 */
6507 res
= ast_say_number_full_gr(chan
, (num
/ 1000000), ints
, chan
->language
,audiofd
, ctrlfd
);
6510 num
= num
% 1000000;
6511 snprintf(fn
, sizeof(fn
), "digits/millions");
6513 ast_log(LOG_DEBUG
, "Number '%d' is too big for me\n", num
);
6519 if (!ast_streamfile(chan
, fn
, language
)) {
6520 if ((audiofd
> -1) && (ctrlfd
> -1))
6521 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
6523 res
= ast_waitstream(chan
, ints
);
6525 ast_stopstream(chan
);
6533 * The format is weekday - day - month -year
6535 * A list of the files that you need to create
6536 * digits/day-[1..7] : "Deytera .. Paraskeyh"
6537 * digits/months/1..12 : "Ianouariou .. Dekembriou"
6538 Attention the months are in
6543 static int ast_say_date_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6551 ast_localtime(&t
,&tm
,NULL
);
6552 /* W E E K - D A Y */
6554 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6555 res
= ast_streamfile(chan
, fn
, lang
);
6557 res
= ast_waitstream(chan
, ints
);
6561 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6565 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6566 res
= ast_streamfile(chan
, fn
, lang
);
6568 res
= ast_waitstream(chan
, ints
);
6572 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
6578 /* A list of the files that you need to create
6579 * digits/female/1..4 : "Mia, dyo , treis, tesseris "
6580 * digits/kai : "KAI"
6582 * digits/p-m : "meta meshmbrias"
6583 * digits/a-m : "pro meshmbrias"
6586 static int ast_say_time_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6593 ast_localtime(&t
, &tm
, NULL
);
6598 else if (hour
== 12)
6600 else if (hour
> 12) {
6605 res
= gr_say_number_female(hour
, chan
, ints
, lang
);
6608 res
= ast_streamfile(chan
, "digits/kai", lang
);
6610 res
= ast_waitstream(chan
, ints
);
6612 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char *) NULL
);
6615 res
= ast_streamfile(chan
, "digits/hwra", lang
);
6617 res
= ast_waitstream(chan
, ints
);
6621 res
= ast_streamfile(chan
, "digits/p-m", lang
);
6624 res
= ast_streamfile(chan
, "digits/a-m", lang
);
6627 res
= ast_waitstream(chan
, ints
);
6633 static int ast_say_datetime_gr(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
6639 ast_localtime(&t
, &tm
, NULL
);
6642 /* W E E K - D A Y */
6644 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
6645 res
= ast_streamfile(chan
, fn
, lang
);
6647 res
= ast_waitstream(chan
, ints
);
6651 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6655 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
6656 res
= ast_streamfile(chan
, fn
, lang
);
6658 res
= ast_waitstream(chan
, ints
);
6661 res
= ast_say_time_gr(chan
, t
, ints
, lang
);
6665 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
)
6669 int res
=0, offset
, sndoffset
;
6670 char sndfile
[256], nextmsg
[256];
6673 format
= "AdBY 'digits/at' IMp";
6675 ast_localtime(&time
,&tm
,timezone
);
6677 for (offset
=0 ; format
[offset
] != '\0' ; offset
++) {
6678 ast_log(LOG_DEBUG
, "Parsing %c (offset %d) in %s\n", format
[offset
], offset
, format
);
6679 switch (format
[offset
]) {
6680 /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
6682 /* Literal name of a sound file */
6684 for (sndoffset
=0 ; (format
[++offset
] != '\'') && (sndoffset
< 256) ; sndoffset
++)
6685 sndfile
[sndoffset
] = format
[offset
];
6686 sndfile
[sndoffset
] = '\0';
6687 res
= wait_file(chan
,ints
,sndfile
,lang
);
6691 /* Sunday - Saturday */
6692 snprintf(nextmsg
,sizeof(nextmsg
), "digits/day-%d", tm
.tm_wday
);
6693 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6698 /* January - December */
6699 snprintf(nextmsg
,sizeof(nextmsg
), "digits/mon-%d", tm
.tm_mon
);
6700 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6704 /* first - thirtyfirst */
6705 gr_say_number_female(tm
.tm_mday
, chan
, ints
, lang
);
6710 ast_say_number_full_gr(chan
, 1900+tm
.tm_year
, ints
, chan
->language
, -1, -1);
6715 if (tm
.tm_hour
== 0)
6716 gr_say_number_female(12, chan
, ints
, lang
);
6717 else if (tm
.tm_hour
> 12)
6718 gr_say_number_female(tm
.tm_hour
- 12, chan
, ints
, lang
);
6720 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6725 gr_say_number_female(tm
.tm_hour
, chan
, ints
, lang
);
6731 res
= ast_streamfile(chan
, "digits/kai", lang
);
6733 res
= ast_waitstream(chan
, ints
);
6735 res
= ast_say_number_full_gr(chan
, tm
.tm_min
, ints
, lang
, -1, -1);
6738 res
= ast_streamfile(chan
, "digits/oclock", lang
);
6740 res
= ast_waitstream(chan
, ints
);
6746 if (tm
.tm_hour
> 11)
6747 snprintf(nextmsg
,sizeof(nextmsg
), "digits/p-m");
6749 snprintf(nextmsg
,sizeof(nextmsg
), "digits/a-m");
6750 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6753 /* Shorthand for "Today", "Yesterday", or ABdY */
6754 /* XXX As emphasized elsewhere, this should the native way in your
6755 * language to say the date, with changes in what you say, depending
6756 * upon how recent the date is. XXX */
6760 time_t beg_today
, tt
;
6762 gettimeofday(&now
,NULL
);
6764 ast_localtime(&tt
,&tmnow
,timezone
);
6765 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6766 /* In any case, it saves not having to do ast_mktime() */
6767 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6768 if (beg_today
< time
) {
6770 res
= wait_file(chan
,ints
, "digits/today",lang
);
6771 } else if (beg_today
- 86400 < time
) {
6773 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6775 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6780 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
6781 /* XXX As emphasized elsewhere, this should the native way in your
6782 * language to say the date, with changes in what you say, depending
6783 * upon how recent the date is. XXX */
6787 time_t beg_today
, tt
;
6789 gettimeofday(&now
,NULL
);
6791 ast_localtime(&tt
,&tmnow
,timezone
);
6792 /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
6793 /* In any case, it saves not having to do ast_mktime() */
6794 beg_today
= tt
- (tmnow
.tm_hour
* 3600) - (tmnow
.tm_min
* 60) - (tmnow
.tm_sec
);
6795 if (beg_today
< time
) {
6797 } else if ((beg_today
- 86400) < time
) {
6799 res
= wait_file(chan
,ints
, "digits/yesterday",lang
);
6800 } else if (beg_today
- 86400 * 6 < time
) {
6801 /* Within the last week */
6802 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "A", timezone
);
6804 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "AdBY", timezone
);
6809 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HM", timezone
);
6813 snprintf(nextmsg
,sizeof(nextmsg
), "digits/kai");
6814 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6816 res
= ast_say_number_full_gr(chan
, tm
.tm_sec
, ints
, lang
, -1, -1);
6818 snprintf(nextmsg
,sizeof(nextmsg
), "digits/seconds");
6819 res
= wait_file(chan
,ints
,nextmsg
,lang
);
6822 res
= ast_say_date_with_format_gr(chan
, time
, ints
, lang
, "HMS", timezone
);
6826 /* Just ignore spaces and tabs */
6829 /* Unknown character */
6830 ast_log(LOG_WARNING
, "Unknown character in datetime format %s: %c at pos %d\n", format
, format
[offset
], offset
);
6832 /* Jump out on DTMF */
6843 /*********************************** Georgian Support ***************************************/
6847 Convert a number into a semi-localized string. Only for Georgian.
6848 res must be of at least 256 bytes, preallocated.
6849 The output corresponds to Georgian spoken numbers, so
6850 it may be either converted to real words by applying a direct conversion
6851 table, or played just by substituting the entities with played files.
6853 Output may consist of the following tokens (separated by spaces):
6855 1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
6857 20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
6858 100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
6859 1000, 1000_. (atasi, atas).
6860 1000000, 1000000_. (milioni, milion).
6861 1000000000, 1000000000_. (miliardi, miliard).
6863 To be able to play the sounds, each of the above tokens needs
6864 a corresponding sound file. (e.g. 200_.gsm).
6866 static char* ast_translate_number_ge(int num
, char* res
, int res_len
)
6874 strncat(res
, "minus ", res_len
- strlen(res
) - 1);
6875 if ( num
> INT_MIN
) {
6883 /* directly read the numbers */
6884 if (num
<= 20 || num
== 40 || num
== 60 || num
== 80 || num
== 100) {
6885 snprintf(buf
, sizeof(buf
), "%d", num
);
6886 strncat(res
, buf
, res_len
- strlen(res
) - 1);
6891 if (num
< 40) { /* ocda... */
6892 strncat(res
, "20_ ", res_len
- strlen(res
) - 1);
6893 return ast_translate_number_ge(num
- 20, res
, res_len
);
6896 if (num
< 60) { /* ormocda... */
6897 strncat(res
, "40_ ", res_len
- strlen(res
) - 1);
6898 return ast_translate_number_ge(num
- 40, res
, res_len
);
6901 if (num
< 80) { /* samocda... */
6902 strncat(res
, "60_ ", res_len
- strlen(res
) - 1);
6903 return ast_translate_number_ge(num
- 60, res
, res_len
);
6906 if (num
< 100) { /* otxmocda... */
6907 strncat(res
, "80_ ", res_len
- strlen(res
) - 1);
6908 return ast_translate_number_ge(num
- 80, res
, res_len
);
6912 if (num
< 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
6913 remainder
= num
% 100;
6914 digit
= (num
- remainder
) / 100;
6916 if (remainder
== 0) {
6917 snprintf(buf
, sizeof(buf
), "%d", num
);
6918 strncat(res
, buf
, res_len
- strlen(res
) - 1);
6921 snprintf(buf
, sizeof(buf
), "%d_ ", digit
*100);
6922 strncat(res
, buf
, res_len
- strlen(res
) - 1);
6923 return ast_translate_number_ge(remainder
, res
, res_len
);
6929 strncat(res
, "1000", res_len
- strlen(res
) - 1);
6934 if (num
< 1000000) {
6935 remainder
= num
% 1000;
6936 digit
= (num
- remainder
) / 1000;
6938 if (remainder
== 0) {
6939 ast_translate_number_ge(digit
, res
, res_len
);
6940 strncat(res
, " 1000", res_len
- strlen(res
) - 1);
6945 strncat(res
, "1000_ ", res_len
- strlen(res
) - 1);
6946 return ast_translate_number_ge(remainder
, res
, res_len
);
6949 ast_translate_number_ge(digit
, res
, res_len
);
6950 strncat(res
, " 1000_ ", res_len
- strlen(res
) - 1);
6951 return ast_translate_number_ge(remainder
, res
, res_len
);
6956 if (num
== 1000000) {
6957 strncat(res
, "1 1000000", res_len
- strlen(res
) - 1);
6962 if (num
< 1000000000) {
6963 remainder
= num
% 1000000;
6964 digit
= (num
- remainder
) / 1000000;
6966 if (remainder
== 0) {
6967 ast_translate_number_ge(digit
, res
, res_len
);
6968 strncat(res
, " 1000000", res_len
- strlen(res
) - 1);
6972 ast_translate_number_ge(digit
, res
, res_len
);
6973 strncat(res
, " 1000000_ ", res_len
- strlen(res
) - 1);
6974 return ast_translate_number_ge(remainder
, res
, res_len
);
6979 if (num
== 1000000000) {
6980 strncat(res
, "1 1000000000", res_len
- strlen(res
) - 1);
6985 if (num
> 1000000000) {
6986 remainder
= num
% 1000000000;
6987 digit
= (num
- remainder
) / 1000000000;
6989 if (remainder
== 0) {
6990 ast_translate_number_ge(digit
, res
, res_len
);
6991 strncat(res
, " 1000000000", res_len
- strlen(res
) - 1);
6995 ast_translate_number_ge(digit
, res
, res_len
);
6996 strncat(res
, " 1000000000_ ", res_len
- strlen(res
) - 1);
6997 return ast_translate_number_ge(remainder
, res
, res_len
);
7007 /*! \brief ast_say_number_full_ge: Georgian syntax */
7008 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
)
7013 const char* remainder
= fn
;
7016 return ast_say_digits_full(chan
, 0, ints
, language
, audiofd
, ctrlfd
);
7019 ast_translate_number_ge(num
, fn
, 512);
7023 while (res
== 0 && (s
= strstr(remainder
, " "))) {
7024 size_t len
= s
- remainder
;
7025 char* new_string
= malloc(len
+ 1 + strlen("digits/"));
7027 sprintf(new_string
, "digits/");
7028 strncat(new_string
, remainder
, len
); /* we can't sprintf() it, it's not null-terminated. */
7029 /* new_string[len + strlen("digits/")] = '\0'; */
7031 if (!ast_streamfile(chan
, new_string
, language
)) {
7032 if ((audiofd
> -1) && (ctrlfd
> -1))
7033 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
7035 res
= ast_waitstream(chan
, ints
);
7037 ast_stopstream(chan
);
7041 remainder
= s
+ 1; /* position just after the found space char. */
7042 while (*remainder
== ' ') /* skip multiple spaces */
7047 /* the last chunk. */
7048 if (res
== 0 && *remainder
) {
7050 char* new_string
= malloc(strlen(remainder
) + 1 + strlen("digits/"));
7051 sprintf(new_string
, "digits/%s", remainder
);
7053 if (!ast_streamfile(chan
, new_string
, language
)) {
7054 if ((audiofd
> -1) && (ctrlfd
> -1))
7055 res
= ast_waitstream_full(chan
, ints
, audiofd
, ctrlfd
);
7057 res
= ast_waitstream(chan
, ints
);
7059 ast_stopstream(chan
);
7073 Georgian support for date/time requires the following files (*.gsm):
7075 mon-1, mon-2, ... (ianvari, tebervali, ...)
7076 day-1, day-2, ... (orshabati, samshabati, ...)
7084 /* Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi". */
7085 static int ast_say_date_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
7090 ast_localtime(&t
,&tm
,NULL
);
7093 res
= ast_say_number(chan
, tm
.tm_year
+ 1900, ints
, lang
, (char *) NULL
);
7096 snprintf(fn
, sizeof(fn
), "digits/tslis %d", tm
.tm_wday
);
7097 res
= ast_streamfile(chan
, fn
, lang
);
7099 res
= ast_waitstream(chan
, ints
);
7103 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char * ) NULL
);
7105 res = ast_waitstream(chan, ints);
7110 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
7111 res
= ast_streamfile(chan
, fn
, lang
);
7113 res
= ast_waitstream(chan
, ints
);
7123 /* Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
7124 static int ast_say_time_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
7129 ast_localtime(&t
, &tm
, NULL
);
7131 res
= ast_say_number(chan
, tm
.tm_hour
, ints
, lang
, (char*)NULL
);
7133 res
= ast_streamfile(chan
, "digits/saati_da", lang
);
7135 res
= ast_waitstream(chan
, ints
);
7140 res
= ast_say_number(chan
, tm
.tm_min
, ints
, lang
, (char*)NULL
);
7143 res
= ast_streamfile(chan
, "digits/tsuti", lang
);
7145 res
= ast_waitstream(chan
, ints
);
7154 /* Georgian syntax. Say date, then say time. */
7155 static int ast_say_datetime_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
7160 ast_localtime(&t
, &tm
, NULL
);
7161 res
= ast_say_date(chan
, t
, ints
, lang
);
7163 ast_say_time(chan
, t
, ints
, lang
);
7171 /* Georgian syntax */
7172 static int ast_say_datetime_from_now_ge(struct ast_channel
*chan
, time_t t
, const char *ints
, const char *lang
)
7183 ast_localtime(&t
, &tm
, NULL
);
7184 ast_localtime(&nowt
, &now
, NULL
);
7185 daydiff
= now
.tm_yday
- tm
.tm_yday
;
7186 if ((daydiff
< 0) || (daydiff
> 6)) {
7187 /* Day of month and month */
7189 res
= ast_say_number(chan
, tm
.tm_mday
, ints
, lang
, (char *) NULL
);
7191 snprintf(fn
, sizeof(fn
), "digits/mon-%d", tm
.tm_mon
);
7192 res
= ast_streamfile(chan
, fn
, lang
);
7194 res
= ast_waitstream(chan
, ints
);
7197 } else if (daydiff
) {
7198 /* Just what day of the week */
7200 snprintf(fn
, sizeof(fn
), "digits/day-%d", tm
.tm_wday
);
7201 res
= ast_streamfile(chan
, fn
, lang
);
7203 res
= ast_waitstream(chan
, ints
);
7205 } /* Otherwise, it was today */
7207 res
= ast_say_time(chan
, t
, ints
, lang
);
7215 * remap the 'say' functions to use those in this file
7217 static void __attribute__((constructor
)) __say_init(void)
7219 ast_say_number_full
= say_number_full
;
7220 ast_say_enumeration_full
= say_enumeration_full
;
7221 ast_say_digit_str_full
= say_digit_str_full
;
7222 ast_say_character_str_full
= say_character_str_full
;
7223 ast_say_phonetic_str_full
= say_phonetic_str_full
;
7224 ast_say_datetime
= say_datetime
;
7225 ast_say_time
= say_time
;
7226 ast_say_date
= say_date
;
7227 ast_say_datetime_from_now
= say_datetime_from_now
;
7228 ast_say_date_with_format
= say_date_with_format
;