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).
30 * \note 2007-03-20 : Support for Thai added by Dome C. <dome@tel.co.th>,
31 * IP Crossing Co., Ltd.
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include <netinet/in.h>
44 #include <iso/limits_iso.h>
47 #include "asterisk/file.h"
48 #include "asterisk/channel.h"
49 #include "asterisk/say.h"
50 #include "asterisk/lock.h"
51 #include "asterisk/localtime.h"
52 #include "asterisk/utils.h"
53 #include "asterisk/app.h"
55 /* Forward declaration */
56 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
59 static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
62 char fnbuf[10], asciibuf[20] = "letters/ascii";
67 while (str[num] && !res) {
77 fn = "letters/exclaimation-point";
83 fn = "letters/dollar";
92 fn = "letters/equals";
101 fn = "letters/space";
113 strcpy(fnbuf, "digits/X");
119 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
120 strcpy(fnbuf, "letters/X");
124 if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
125 (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
126 res = ast_streamfile(chan, fn, lang);
128 if ((audiofd > -1) && (ctrlfd > -1))
129 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
131 res = ast_waitstream(chan, ints);
133 ast_stopstream(chan);
141 static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
149 while (str[num] && !res) {
159 fn = "letters/exclaimation-point";
165 fn = "letters/dollar";
174 fn = "letters/equals";
180 fn = "letters/slash";
183 fn = "letters/space";
194 strcpy(fnbuf, "digits/X");
198 default: /* '9' falls here... */
200 if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
201 strcpy(fnbuf, "phonetic/X_p");
205 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
206 res = ast_streamfile(chan, fn, lang);
208 if ((audiofd > -1) && (ctrlfd > -1))
209 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
211 res = ast_waitstream(chan, ints);
213 ast_stopstream(chan);
221 static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
228 while (str[num] && !res) {
250 strcpy(fnbuf, "digits/X");
255 if (fn && ast_fileexists(fn, NULL, lang) > 0) {
256 res = ast_streamfile(chan, fn, lang);
258 if ((audiofd > -1) && (ctrlfd > -1))
259 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
261 res = ast_waitstream(chan, ints);
263 ast_stopstream(chan);
271 /* Forward declarations */
272 /*! \page Def_syntaxlang Asterisk Language Syntaxes supported
273 \note Not really language codes.
274 For these language codes, Asterisk will change the syntax when
275 saying numbers (and in some cases dates and voicemail messages
279 \arg \b en - English (US)
280 \arg \b en_GB - English (British)
281 \arg \b es - Spanish, Mexican
286 \arg \b no - Norwegian
288 \arg \b pt - Portuguese
289 \arg \b pt_BR - Portuguese (Brazil)
291 \arg \b zh - Taiwanese / Chinese
293 \arg \b ka - Georgian
294 \arg \b hu - Hungarian
297 For Some languages the numbers differ for gender and plural.
298 \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
299 \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
300 use the option argument 'p' for plural enumerations like in German
302 Date/Time functions currently have less languages supported than saynumber().
304 \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
306 See contrib/i18n.testsuite.conf for some examples of the different syntaxes
309 Portuguese sound files needed for Time/Date functions:
320 Spanish sound files needed for Time/Date functions:
325 Italian sound files needed for Time/Date functions:
331 /* Forward declarations of language specific variants of ast_say_number_full */
332 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
333 static int ast_say_number_full_cs(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
334 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);
335 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);
336 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
337 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);
338 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);
339 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);
340 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
341 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
342 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);
343 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);
344 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);
345 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);
346 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
347 static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
348 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);
349 static int ast_say_number_full_ka(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_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
351 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
352 static int ast_say_number_full_ur(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
354 /* Forward declarations of language specific variants of ast_say_enumeration_full */
355 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
356 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);
357 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);
358 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);
360 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
361 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
362 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
363 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
364 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
365 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
366 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
367 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
368 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
369 static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
370 static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
371 static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
373 static int ast_say_date_with_format_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
374 static int ast_say_date_with_format_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
375 static int ast_say_date_with_format_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
376 static int ast_say_date_with_format_es(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
377 static int ast_say_date_with_format_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
378 static int ast_say_date_with_format_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
379 static int ast_say_date_with_format_it(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
380 static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
381 static int ast_say_date_with_format_pl(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
382 static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
383 static int ast_say_date_with_format_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
384 static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
385 static int ast_say_date_with_format_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
387 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
388 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
389 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
390 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
391 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
392 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
393 static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
394 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
395 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
396 static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
397 static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
398 static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
400 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
401 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
402 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
403 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
404 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
405 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
406 static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
407 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
408 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
409 static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
410 static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
411 static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
413 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
414 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
415 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
416 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
417 static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
419 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
422 if ((res = ast_streamfile(chan, file, lang)))
423 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
425 res = ast_waitstream(chan, ints);
429 /*! \brief ast_say_number_full: call language-specific functions */
430 /* Called from AGI */
431 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
433 if (!strncasecmp(language, "en_GB", 5)) { /* British syntax */
434 return ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd);
435 } else if (!strncasecmp(language, "en", 2)) { /* English syntax */
436 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
437 } else if (!strncasecmp(language, "cs", 2)) { /* Czech syntax */
438 return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
439 } else if (!strncasecmp(language, "cz", 2)) { /* deprecated Czech syntax */
440 static int deprecation_warning = 0;
441 if (deprecation_warning++ % 10 == 0) {
442 ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
444 return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
445 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
446 return ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
447 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
448 return ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
449 } else if (!strncasecmp(language, "es", 2)) { /* Spanish syntax */
450 return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
451 } else if (!strncasecmp(language, "fr", 2)) { /* French syntax */
452 return ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd);
453 } else if (!strncasecmp(language, "ge", 2)) { /* deprecated Georgian syntax */
454 static int deprecation_warning = 0;
455 if (deprecation_warning++ % 10 == 0) {
456 ast_log(LOG_WARNING, "ge is not a standard language code. Please switch to using ka instead.\n");
458 return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
459 } else if (!strncasecmp(language, "gr", 2)) { /* Greek syntax */
460 return ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd);
461 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
462 return ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
463 } else if (!strncasecmp(language, "hu", 2)) { /* Hungarian syntax */
464 return ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd);
465 } else if (!strncasecmp(language, "it", 2)) { /* Italian syntax */
466 return ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd);
467 } else if (!strncasecmp(language, "ka", 2)) { /* Georgian syntax */
468 return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
469 } else if (!strncasecmp(language, "mx", 2)) { /* deprecated Mexican syntax */
470 static int deprecation_warning = 0;
471 if (deprecation_warning++ % 10 == 0) {
472 ast_log(LOG_WARNING, "mx is not a standard language code. Please switch to using es_MX instead.\n");
474 return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
475 } else if (!strncasecmp(language, "nl", 2)) { /* Dutch syntax */
476 return ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd);
477 } else if (!strncasecmp(language, "no", 2)) { /* Norwegian syntax */
478 return ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd);
479 } else if (!strncasecmp(language, "pl", 2)) { /* Polish syntax */
480 return ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd);
481 } else if (!strncasecmp(language, "pt", 2)) { /* Portuguese syntax */
482 return ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd);
483 } else if (!strncasecmp(language, "ru", 2)) { /* Russian syntax */
484 return ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd);
485 } else if (!strncasecmp(language, "se", 2)) { /* Swedish syntax */
486 return ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd);
487 } else if (!strncasecmp(language, "th", 2)) { /* Thai syntax */
488 return ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd);
489 } else if (!strncasecmp(language, "tw", 2)) { /* deprecated Taiwanese syntax */
490 static int deprecation_warning = 0;
491 if (deprecation_warning++ % 10 == 0) {
492 ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese. Please switch to using zh_TW instead.\n");
494 return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
495 } else if (!strncasecmp(language, "zh", 2)) { /* Taiwanese / Chinese syntax */
496 return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
497 } else if (!strncasecmp(language, "ur", 2)) { /* Urdu syntax */
498 return ast_say_number_full_ur(chan, num, ints, language, options, audiofd, ctrlfd);
501 /* Default to english */
502 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
505 /*! \brief ast_say_number_full_en: English syntax */
506 /* This is the default syntax, if no other syntax defined in this file is used */
507 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
513 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
515 while (!res && (num || playh)) {
517 ast_copy_string(fn, "digits/minus", sizeof(fn));
518 if ( num > INT_MIN ) {
524 ast_copy_string(fn, "digits/hundred", sizeof(fn));
526 } else if (num < 20) {
527 snprintf(fn, sizeof(fn), "digits/%d", num);
529 } else if (num < 100) {
530 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
534 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
538 if (num < 1000000) { /* 1,000,000 */
539 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
543 snprintf(fn, sizeof(fn), "digits/thousand");
545 if (num < 1000000000) { /* 1,000,000,000 */
546 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
550 ast_copy_string(fn, "digits/million", sizeof(fn));
552 ast_debug(1, "Number '%d' is too big for me\n", num);
559 if (!ast_streamfile(chan, fn, language)) {
560 if ((audiofd > -1) && (ctrlfd > -1))
561 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
563 res = ast_waitstream(chan, ints);
565 ast_stopstream(chan);
571 static int exp10_int(int power)
574 for (x=0;x<power;x++)
579 /*! \brief ast_say_number_full_cs: Czech syntax */
581 * 1m,2m - gender male
582 * 1w,2w - gender female
586 * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
588 * for each number 10^(3n + 3) exist 3 files represented as:
589 * 1 tousand = jeden tisic = 1_E3
590 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
591 * 5,6,... tousands = pet,sest,... tisic = 5_E3
597 * tousand, milion are gender male, so 1 and 2 is 1m 2m
598 * miliard is gender female, so 1 and 2 is 1w 2w
600 static int ast_say_number_full_cs(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
610 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
615 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
617 while (!res && (num || playh)) {
619 ast_copy_string(fn, "digits/minus", sizeof(fn));
620 if ( num > INT_MIN ) {
625 } else if (num < 3 ) {
626 snprintf(fn, sizeof(fn), "digits/%d%c", num, options[0]);
629 } else if (num < 20) {
630 snprintf(fn, sizeof(fn), "digits/%d", num);
633 } else if (num < 100) {
634 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
636 } else if (num < 1000) {
637 hundered = num / 100;
638 if ( hundered == 1 ) {
639 ast_copy_string(fn, "digits/1sto", sizeof(fn));
640 } else if ( hundered == 2 ) {
641 ast_copy_string(fn, "digits/2ste", sizeof(fn));
643 res = ast_say_number_full_cs(chan, hundered, ints, language, options, audiofd, ctrlfd);
646 if (hundered == 3 || hundered == 4) {
647 ast_copy_string(fn, "digits/sta", sizeof(fn));
648 } else if ( hundered > 4 ) {
649 ast_copy_string(fn, "digits/set", sizeof(fn));
652 num -= (hundered * 100);
653 } else { /* num > 1000 */
654 length = (int)log10(num)+1;
655 while ( (length % 3 ) != 1 ) {
658 left = num / (exp10_int(length-1));
661 case 9: options = "w"; /* 1,000,000,000 gender female */
663 default : options = "m"; /* others are male */
666 if ( left > 1 ) { /* we dont say "one thousand" but only thousand */
667 res = ast_say_number_full_cs(chan, left, ints, language, options, audiofd, ctrlfd);
671 if ( left >= 5 ) { /* >= 5 have the same declesion */
672 snprintf(fn, sizeof(fn), "digits/5_E%d", length - 1);
673 } else if ( left >= 2 && left <= 4 ) {
674 snprintf(fn, sizeof(fn), "digits/2-4_E%d", length - 1);
675 } else { /* left == 1 */
676 snprintf(fn, sizeof(fn), "digits/1_E%d", length - 1);
678 num -= left * (exp10_int(length-1));
681 if (!ast_streamfile(chan, fn, language)) {
682 if ((audiofd > -1) && (ctrlfd > -1)) {
683 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
685 res = ast_waitstream(chan, ints);
688 ast_stopstream(chan);
694 /*! \brief ast_say_number_full_da: Danish syntax */
696 In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
698 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)
703 int cn = 1; /* +1 = commune; -1 = neuter */
706 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
708 if (options && !strncasecmp(options, "n", 1)) cn = -1;
710 while (!res && (num || playh || playa )) {
711 /* The grammar for Danish numbers is the same as for English except
713 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
714 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
715 * "one-and twenty" and 68 is "eight-and sixty".
716 * - "million" is different in singular and plural form
717 * - numbers > 1000 with zero as the third digit from last have an
718 * "and" before the last two digits, i.e. 2034 is "two thousand and
719 * four-and thirty" and 1000012 is "one million and twelve".
722 ast_copy_string(fn, "digits/minus", sizeof(fn));
723 if ( num > INT_MIN ) {
729 ast_copy_string(fn, "digits/hundred", sizeof(fn));
732 ast_copy_string(fn, "digits/and", sizeof(fn));
734 } else if (num == 1 && cn == -1) {
735 ast_copy_string(fn, "digits/1N", sizeof(fn));
737 } else if (num < 20) {
738 snprintf(fn, sizeof(fn), "digits/%d", num);
740 } else if (num < 100) {
743 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
746 snprintf(fn, sizeof(fn), "digits/%d", num);
751 int hundreds = num / 100;
753 ast_copy_string(fn, "digits/1N", sizeof(fn));
755 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
758 num -= 100 * hundreds;
764 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
768 ast_copy_string(fn, "digits/thousand", sizeof(fn));
770 if (num < 1000000000) {
771 int millions = num / 1000000;
772 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
776 ast_copy_string(fn, "digits/million", sizeof(fn));
778 ast_copy_string(fn, "digits/millions", sizeof(fn));
781 ast_debug(1, "Number '%d' is too big for me\n", num);
785 if (num && num < 100)
790 if (!ast_streamfile(chan, fn, language)) {
791 if ((audiofd > -1) && (ctrlfd > -1))
792 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
794 res = ast_waitstream(chan, ints);
796 ast_stopstream(chan);
802 /*! \brief ast_say_number_full_de: German syntax */
804 In addition to English, the following sounds are required:
806 "1-and" through "9-and"
809 NB "1" is recorded as 'eins'
811 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)
814 int mf = 1; /* +1 = male and neuter; -1 = female */
818 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
820 if (options && (!strncasecmp(options, "f", 1)))
823 while (!res && num) {
824 /* The grammar for German numbers is the same as for English except
826 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
827 * "one-and twenty" and 68 is "eight-and sixty".
828 * - "one" varies according to gender
829 * - 100 is 'hundert', however all other instances are 'ein hundert'
830 * - 1000 is 'tausend', however all other instances are 'ein tausend'
831 * - 1000000 is always 'eine million'
832 * - "million" is different in singular and plural form
835 ast_copy_string(fn, "digits/minus", sizeof(fn));
836 if ( num > INT_MIN ) {
841 } else if (num < 100 && t) {
842 ast_copy_string(fn, "digits/and", sizeof(fn));
844 } else if (num == 1 && mf == -1) {
845 snprintf(fn, sizeof(fn), "digits/%dF", num);
847 } else if (num < 20) {
848 snprintf(fn, sizeof(fn), "digits/%d", num);
850 } else if (num < 100) {
853 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
856 snprintf(fn, sizeof(fn), "digits/%d", num);
859 } else if (num == 100 && t == 0) {
860 ast_copy_string(fn, "digits/hundred", sizeof(fn));
862 } else if (num < 1000) {
863 int hundreds = num / 100;
866 ast_copy_string(fn, "digits/1N", sizeof(fn));
868 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
870 ast_copy_string(fna, "digits/hundred", sizeof(fna));
872 } else if (num == 1000 && t == 0) {
873 ast_copy_string(fn, "digits/thousand", sizeof(fn));
875 } else if (num < 1000000) {
876 int thousands = num / 1000;
879 if (thousands == 1) {
880 ast_copy_string(fn, "digits/1N", sizeof(fn));
881 ast_copy_string(fna, "digits/thousand", sizeof(fna));
883 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
886 ast_copy_string(fn, "digits/thousand", sizeof(fn));
888 } else if (num < 1000000000) {
889 int millions = num / 1000000;
893 ast_copy_string(fn, "digits/1F", sizeof(fn));
894 ast_copy_string(fna, "digits/million", sizeof(fna));
896 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
899 ast_copy_string(fn, "digits/millions", sizeof(fn));
901 } else if (num <= INT_MAX) {
902 int billions = num / 1000000000;
903 num = num % 1000000000;
906 ast_copy_string(fn, "digits/1F", sizeof(fn));
907 ast_copy_string(fna, "digits/milliard", sizeof(fna));
909 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
913 ast_copy_string(fn, "digits/milliards", sizeof(fn));
916 ast_debug(1, "Number '%d' is too big for me\n", num);
920 if (!ast_streamfile(chan, fn, language)) {
921 if ((audiofd > -1) && (ctrlfd > -1))
922 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
924 res = ast_waitstream(chan, ints);
926 ast_stopstream(chan);
928 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
929 if ((audiofd > -1) && (ctrlfd > -1))
930 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
932 res = ast_waitstream(chan, ints);
934 ast_stopstream(chan);
942 /*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
944 In addition to American English, the following sounds are required: "and"
946 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
953 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
955 while (!res && (num || playh || playa )) {
957 ast_copy_string(fn, "digits/minus", sizeof(fn));
958 if ( num > INT_MIN ) {
964 ast_copy_string(fn, "digits/hundred", sizeof(fn));
967 ast_copy_string(fn, "digits/and", sizeof(fn));
969 } else if (num < 20) {
970 snprintf(fn, sizeof(fn), "digits/%d", num);
972 } else if (num < 100) {
973 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
975 } else if (num < 1000) {
976 int hundreds = num / 100;
977 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
980 num -= 100 * hundreds;
983 } else if (num < 1000000) {
984 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
987 ast_copy_string(fn, "digits/thousand", sizeof(fn));
989 if (num && num < 100)
991 } else if (num < 1000000000) {
992 int millions = num / 1000000;
993 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
996 ast_copy_string(fn, "digits/million", sizeof(fn));
998 if (num && num < 100)
1001 ast_debug(1, "Number '%d' is too big for me\n", num);
1006 if (!ast_streamfile(chan, fn, language)) {
1007 if ((audiofd > -1) && (ctrlfd > -1))
1008 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1010 res = ast_waitstream(chan, ints);
1012 ast_stopstream(chan);
1018 /*! \brief ast_say_number_full_es: Spanish syntax */
1020 Requires a few new audios:
1021 1F.gsm: feminine 'una'
1022 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
1024 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)
1028 int mf = 0; /* +1 = male; -1 = female */
1031 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1034 if (!strncasecmp(options, "f", 1))
1036 else if (!strncasecmp(options, "m", 1))
1040 while (!res && num) {
1042 ast_copy_string(fn, "digits/minus", sizeof(fn));
1043 if ( num > INT_MIN ) {
1049 ast_copy_string(fn, "digits/and", sizeof(fn));
1051 } else if (num == 1) {
1053 snprintf(fn, sizeof(fn), "digits/%dF", num);
1055 snprintf(fn, sizeof(fn), "digits/%dM", num);
1057 snprintf(fn, sizeof(fn), "digits/%d", num);
1059 } else if (num < 31) {
1060 snprintf(fn, sizeof(fn), "digits/%d", num);
1062 } else if (num < 100) {
1063 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1067 } else if (num == 100) {
1068 ast_copy_string(fn, "digits/100", sizeof(fn));
1070 } else if (num < 200) {
1071 ast_copy_string(fn, "digits/100-and", sizeof(fn));
1075 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1077 } else if (num < 2000) {
1079 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1081 if (num < 1000000) {
1082 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1086 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1088 if (num < 2147483640) {
1089 if ((num/1000000) == 1) {
1090 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1093 ast_copy_string(fn, "digits/million", sizeof(fn));
1095 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1098 ast_copy_string(fn, "digits/millions", sizeof(fn));
1102 ast_debug(1, "Number '%d' is too big for me\n", num);
1110 if (!ast_streamfile(chan, fn, language)) {
1111 if ((audiofd > -1) && (ctrlfd > -1))
1112 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1114 res = ast_waitstream(chan, ints);
1116 ast_stopstream(chan);
1124 /*! \brief ast_say_number_full_fr: French syntax */
1125 /* Extra sounds needed:
1128 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)
1133 int mf = 1; /* +1 = male; -1 = female */
1136 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1138 if (options && !strncasecmp(options, "f", 1))
1141 while (!res && (num || playh || playa)) {
1143 ast_copy_string(fn, "digits/minus", sizeof(fn));
1144 if ( num > INT_MIN ) {
1150 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1153 ast_copy_string(fn, "digits/et", sizeof(fn));
1155 } else if (num == 1) {
1157 snprintf(fn, sizeof(fn), "digits/%dF", num);
1159 snprintf(fn, sizeof(fn), "digits/%d", num);
1161 } else if (num < 21) {
1162 snprintf(fn, sizeof(fn), "digits/%d", num);
1164 } else if (num < 70) {
1165 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1166 if ((num % 10) == 1) playa++;
1168 } else if (num < 80) {
1169 ast_copy_string(fn, "digits/60", sizeof(fn));
1170 if ((num % 10) == 1) playa++;
1172 } else if (num < 100) {
1173 ast_copy_string(fn, "digits/80", sizeof(fn));
1175 } else if (num < 200) {
1176 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1178 } else if (num < 1000) {
1179 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1182 } else if (num < 2000) {
1183 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1185 } else if (num < 1000000) {
1186 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1189 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1191 } else if (num < 1000000000) {
1192 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1195 ast_copy_string(fn, "digits/million", sizeof(fn));
1196 num = num % 1000000;
1198 ast_debug(1, "Number '%d' is too big for me\n", num);
1202 if (!ast_streamfile(chan, fn, language)) {
1203 if ((audiofd > -1) && (ctrlfd > -1))
1204 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1206 res = ast_waitstream(chan, ints);
1208 ast_stopstream(chan);
1217 /* Check doc/lang/hebrew-digits.txt for information about the various
1218 * recordings required to make this translation work properly */
1219 #define SAY_NUM_BUF_SIZE 256
1220 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)
1223 int state = 0; /* no need to save anything */
1224 int mf = -1; /* +1 = Masculin; -1 = Feminin */
1227 char fn[SAY_NUM_BUF_SIZE] = "";
1229 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
1232 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1234 if (options && !strncasecmp(options, "m", 1)) {
1237 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
1239 /* Do we have work to do? */
1240 while (!res && (num || (state > 0))) {
1241 /* first type of work: play a second sound. In this loop
1242 * we can only play one sound file at a time. Thus playing
1243 * a second one requires repeating the loop just for the
1244 * second file. The variable 'state' remembers where we were.
1245 * state==0 is the normal mode and it means that we continue
1246 * to check if the number num has yet anything left.
1248 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);
1252 } else if (state == 2) {
1253 if ((num >= 11) && (num < 21)) {
1255 snprintf(fn, sizeof(fn), "digits/ve");
1257 snprintf(fn, sizeof(fn), "digits/uu");
1262 snprintf(fn, sizeof(fn), "digits/ve");
1265 snprintf(fn, sizeof(fn), "digits/uu");
1269 snprintf(fn, sizeof(fn), "digits/ve");
1271 snprintf(fn, sizeof(fn), "digits/uu");
1275 snprintf(fn, sizeof(fn), "digits/ve");
1278 snprintf(fn, sizeof(fn), "digits/ve");
1281 snprintf(fn, sizeof(fn), "digits/ve");
1284 snprintf(fn, sizeof(fn), "digits/ve");
1287 snprintf(fn, sizeof(fn), "digits/uu");
1290 snprintf(fn, sizeof(fn), "digits/ve");
1293 snprintf(fn, sizeof(fn), "digits/ve");
1298 } else if (state == 3) {
1299 snprintf(fn, sizeof(fn), "digits/1k");
1301 } else if (num < 0) {
1302 snprintf(fn, sizeof(fn), "digits/minus");
1304 } else if (num < 20) {
1306 snprintf(fn, sizeof(fn), "digits/%d", num);
1308 snprintf(fn, sizeof(fn), "digits/%dm", num);
1311 } else if ((num < 100) && (num >= 20)) {
1312 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1317 } else if ((num >= 100) && (num < 1000)) {
1319 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
1320 num = num - (tmpnum * 100);
1321 if ((num > 0) && (num < 11)) {
1324 } else if ((num >= 1000) && (num < 10000)) {
1325 tmpnum = num / 1000;
1326 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
1327 num = num - (tmpnum * 1000);
1328 if ((num > 0) && (num < 11)) {
1331 } else if (num < 20000) {
1332 snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
1335 } else if (num < 1000000) {
1336 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
1340 snprintf(fn, sizeof(fn), "digits/1k");
1342 if ((num > 0) && (num < 11)) {
1345 } else if (num < 2000000) {
1346 snprintf(fn, sizeof(fn), "digits/million");
1347 num = num % 1000000;
1348 if ((num > 0) && (num < 11)) {
1351 } else if (num < 3000000) {
1352 snprintf(fn, sizeof(fn), "digits/twomillion");
1353 num = num - 2000000;
1354 if ((num > 0) && (num < 11)) {
1357 } else if (num < 1000000000) {
1358 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
1362 snprintf(fn, sizeof(fn), "digits/million");
1363 num = num % 1000000;
1364 if ((num > 0) && (num < 11)) {
1368 ast_debug(1, "Number '%d' is too big for me\n", num);
1373 if (!ast_streamfile(chan, fn, language)) {
1374 if ((audiofd > -1) && (ctrlfd > -1)) {
1375 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1377 res = ast_waitstream(chan, ints);
1380 ast_stopstream(chan);
1386 /*! \brief ast_say_number_full_hu: Hungarian syntax */
1387 /* Extra sounds need:
1391 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1397 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1401 like english, except numbers up to 29 are from 2 words.
1402 10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
1405 while(!res && (num || playh)) {
1407 ast_copy_string(fn, "digits/minus", sizeof(fn));
1408 if ( num > INT_MIN ) {
1414 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1416 } else if (num < 11 || num == 20) {
1417 snprintf(fn, sizeof(fn), "digits/%d", num);
1419 } else if (num < 20) {
1420 ast_copy_string(fn, "digits/10en", sizeof(fn));
1422 } else if (num < 30) {
1423 ast_copy_string(fn, "digits/20on", sizeof(fn));
1425 } else if (num < 100) {
1426 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1430 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1434 if (num < 1000000) { /* 1,000,000 */
1435 res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
1439 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1441 if (num < 1000000000) { /* 1,000,000,000 */
1442 res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1446 ast_copy_string(fn, "digits/million", sizeof(fn));
1448 ast_debug(1, "Number '%d' is too big for me\n", num);
1455 if(!ast_streamfile(chan, fn, language)) {
1456 if ((audiofd > -1) && (ctrlfd > -1))
1457 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1459 res = ast_waitstream(chan, ints);
1461 ast_stopstream(chan);
1467 /*! \brief ast_say_number_full_it: Italian */
1468 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1476 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1481 Like english, numbers up to 20 are a single 'word', and others
1482 compound, but with exceptions.
1483 For example 21 is not twenty-one, but there is a single word in 'it'.
1484 Idem for 28 (ie when a the 2nd part of a compund number
1485 starts with a vowel)
1487 There are exceptions also for hundred, thousand and million.
1488 In english 100 = one hundred, 200 is two hundred.
1489 In italian 100 = cento , like to say hundred (without one),
1490 200 and more are like english.
1492 Same applies for thousand:
1493 1000 is one thousand in en, 2000 is two thousand.
1494 In it we have 1000 = mille , 2000 = 2 mila
1496 For million(s) we use the plural, if more than one
1497 Also, one million is abbreviated in it, like on-million,
1498 or 'un milione', not 'uno milione'.
1499 So the right file is provided.
1502 while (!res && (num || playh)) {
1504 ast_copy_string(fn, "digits/minus", sizeof(fn));
1505 if ( num > INT_MIN ) {
1511 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1513 } else if (num < 20) {
1514 snprintf(fn, sizeof(fn), "digits/%d", num);
1516 } else if (num == 21) {
1517 snprintf(fn, sizeof(fn), "digits/%d", num);
1519 } else if (num == 28) {
1520 snprintf(fn, sizeof(fn), "digits/%d", num);
1522 } else if (num == 31) {
1523 snprintf(fn, sizeof(fn), "digits/%d", num);
1525 } else if (num == 38) {
1526 snprintf(fn, sizeof(fn), "digits/%d", num);
1528 } else if (num == 41) {
1529 snprintf(fn, sizeof(fn), "digits/%d", num);
1531 } else if (num == 48) {
1532 snprintf(fn, sizeof(fn), "digits/%d", num);
1534 } else if (num == 51) {
1535 snprintf(fn, sizeof(fn), "digits/%d", num);
1537 } else if (num == 58) {
1538 snprintf(fn, sizeof(fn), "digits/%d", num);
1540 } else if (num == 61) {
1541 snprintf(fn, sizeof(fn), "digits/%d", num);
1543 } else if (num == 68) {
1544 snprintf(fn, sizeof(fn), "digits/%d", num);
1546 } else if (num == 71) {
1547 snprintf(fn, sizeof(fn), "digits/%d", num);
1549 } else if (num == 78) {
1550 snprintf(fn, sizeof(fn), "digits/%d", num);
1552 } else if (num == 81) {
1553 snprintf(fn, sizeof(fn), "digits/%d", num);
1555 } else if (num == 88) {
1556 snprintf(fn, sizeof(fn), "digits/%d", num);
1558 } else if (num == 91) {
1559 snprintf(fn, sizeof(fn), "digits/%d", num);
1561 } else if (num == 98) {
1562 snprintf(fn, sizeof(fn), "digits/%d", num);
1564 } else if (num < 100) {
1565 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1569 if ((num / 100) > 1) {
1570 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1573 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1577 if (num < 1000000) { /* 1,000,000 */
1579 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1584 if ((tempnum / 1000) < 2)
1585 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1586 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1587 ast_copy_string(fn, "digits/thousands", sizeof(fn));
1589 if (num < 1000000000) { /* 1,000,000,000 */
1590 if ((num / 1000000) > 1)
1591 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1596 if ((tempnum / 1000000) < 2)
1597 ast_copy_string(fn, "digits/million", sizeof(fn));
1599 ast_copy_string(fn, "digits/millions", sizeof(fn));
1601 ast_debug(1, "Number '%d' is too big for me\n", num);
1608 if (!ast_streamfile(chan, fn, language)) {
1609 if ((audiofd > -1) && (ctrlfd > -1))
1610 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1612 res = ast_waitstream(chan, ints);
1614 ast_stopstream(chan);
1620 /*! \brief ast_say_number_full_nl: dutch syntax */
1621 /* New files: digits/nl-en
1623 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1630 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1631 while (!res && (num || playh )) {
1633 ast_copy_string(fn, "digits/minus", sizeof(fn));
1634 if ( num > INT_MIN ) {
1640 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1642 } else if (num < 20) {
1643 snprintf(fn, sizeof(fn), "digits/%d", num);
1645 } else if (num < 100) {
1648 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1652 ast_copy_string(fn, "digits/nl-en", sizeof(fn));
1654 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1657 } else if (num < 200) {
1658 /* hundred, not one-hundred */
1659 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1661 } else if (num < 1000) {
1662 snprintf(fn, sizeof(fn), "digits/%d", num / 100);
1667 /* thousand, not one-thousand */
1669 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1670 } else if (num < 10000) { /* 1,100 to 9,9999 */
1671 res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
1675 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1677 if (num < 1000000) { /* 1,000,000 */
1678 res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
1682 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1684 if (num < 1000000000) { /* 1,000,000,000 */
1685 res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1689 ast_copy_string(fn, "digits/million", sizeof(fn));
1691 ast_debug(1, "Number '%d' is too big for me\n", num);
1699 if (!ast_streamfile(chan, fn, language)) {
1700 if ((audiofd > -1) && (ctrlfd > -1))
1701 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1703 res = ast_waitstream(chan, ints);
1705 ast_stopstream(chan);
1711 /*! \brief ast_say_number_full_no: Norwegian syntax */
1713 In addition to American English, the following sounds are required: "and", "1N"
1715 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)
1720 int cn = 1; /* +1 = commune; -1 = neuter */
1724 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1726 if (options && !strncasecmp(options, "n", 1)) cn = -1;
1728 while (!res && (num || playh || playa )) {
1729 /* The grammar for Norwegian numbers is the same as for English except
1730 * for the following:
1731 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1732 * "and" before the last two digits, i.e. 2034 is "two thousand and
1733 * thirty-four" and 1000012 is "one million and twelve".
1736 ast_copy_string(fn, "digits/minus", sizeof(fn));
1737 if ( num > INT_MIN ) {
1743 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1746 ast_copy_string(fn, "digits/and", sizeof(fn));
1748 } else if (num == 1 && cn == -1) {
1749 ast_copy_string(fn, "digits/1N", sizeof(fn));
1751 } else if (num < 20) {
1752 snprintf(fn, sizeof(fn), "digits/%d", num);
1754 } else if (num < 100) {
1755 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1757 } else if (num < 1000) {
1758 int hundreds = num / 100;
1760 ast_copy_string(fn, "digits/1N", sizeof(fn));
1762 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1765 num -= 100 * hundreds;
1768 } else if (num < 1000000) {
1769 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1772 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1774 if (num && num < 100)
1776 } else if (num < 1000000000) {
1777 int millions = num / 1000000;
1778 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1781 ast_copy_string(fn, "digits/million", sizeof(fn));
1783 if (num && num < 100)
1786 ast_debug(1, "Number '%d' is too big for me\n", num);
1791 if (!ast_streamfile(chan, fn, language)) {
1792 if ((audiofd > -1) && (ctrlfd > -1))
1793 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1795 res = ast_waitstream(chan, ints);
1797 ast_stopstream(chan);
1804 char *separator_dziesiatek;
1808 char *dziesiatki[10];
1813 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1819 return odm->rzedy[rzad - 1][0];
1820 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1821 return odm->rzedy[rzad - 1][1];
1823 return odm->rzedy[rzad - 1][2];
1826 static char* pl_append(char* buffer, char* str)
1828 strcpy(buffer, str);
1829 buffer += strlen(str);
1833 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1835 char file_name[255] = "digits/";
1836 strcat(file_name, fn);
1837 ast_debug(1, "Trying to play: %s\n", file_name);
1838 if (!ast_streamfile(chan, file_name, language)) {
1839 if ((audiofd > -1) && (ctrlfd > -1))
1840 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1842 ast_waitstream(chan, ints);
1844 ast_stopstream(chan);
1847 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1849 /* Initialise variables to allow compilation on Debian-stable, etc */
1859 if (i == 0 && rzad > 0) {
1863 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1867 m1000E6 = i % 1000000000;
1868 i1000E6 = i / 1000000000;
1870 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1872 m1000E3 = m1000E6 % 1000000;
1873 i1000E3 = m1000E6 / 1000000;
1875 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1877 m1000 = m1000E3 % 1000;
1878 i1000 = m1000E3 / 1000;
1880 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1886 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1888 if ( m100 > 0 && m100 <=9 ) {
1890 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1892 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1893 } else if (m100 % 10 == 0) {
1894 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1895 } else if (m100 <= 19 ) {
1896 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1897 } else if (m100 != 0) {
1898 if (odm->separator_dziesiatek[0]==' ') {
1899 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1900 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1904 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1905 b = pl_append(b, odm->separator_dziesiatek);
1906 b = pl_append(b, odm->cyfry2[m100 % 10]);
1907 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1912 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1916 /* ast_say_number_full_pl: Polish syntax */
1917 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)
1927 1000000000.2 miliardy
1928 1000000000.5 miliardow
1992 70m siedemdziesieciu
2004 90m dziewiedziesieciu
2006 and combinations of eg.: 20_1, 30m_3m, etc...
2010 char *zenski_cyfry[] = {"0", "1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
2012 char *zenski_cyfry2[] = {"0", "1", "2z", "3", "4", "5", "6", "7", "8", "9"};
2014 char *meski_cyfry[] = {"0", "1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
2016 char *meski_cyfry2[] = {"0", "1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
2018 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
2020 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
2022 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
2024 char *nijaki_cyfry[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2026 char *nijaki_cyfry2[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2028 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
2030 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
2032 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
2034 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
2036 /* Initialise variables to allow compilation on Debian-stable, etc */
2039 static odmiana *odmiana_nieosobowa = NULL;
2040 static odmiana *odmiana_meska = NULL;
2041 static odmiana *odmiana_zenska = NULL;
2043 if (odmiana_nieosobowa == NULL) {
2044 odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
2046 odmiana_nieosobowa->separator_dziesiatek = " ";
2048 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
2049 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
2050 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
2051 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
2052 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
2053 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
2056 if (odmiana_zenska == NULL) {
2057 odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
2059 odmiana_zenska->separator_dziesiatek = " ";
2061 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
2062 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
2063 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
2064 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
2065 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
2066 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
2069 if (odmiana_meska == NULL) {
2070 odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
2072 odmiana_meska->separator_dziesiatek = " ";
2074 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
2075 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
2076 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
2077 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
2078 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
2079 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
2083 if (strncasecmp(options, "f", 1) == 0)
2085 else if (strncasecmp(options, "m", 1) == 0)
2088 o = odmiana_nieosobowa;
2090 o = odmiana_nieosobowa;
2092 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
2096 /* ast_say_number_full_pt: Portuguese syntax */
2097 /* Extra sounds needed: */
2098 /* For feminin all sound files end with F */
2099 /* 100E for 100+ something */
2100 /* 1000000S for plural */
2101 /* pt-e for 'and' */
2102 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)
2106 int mf = 1; /* +1 = male; -1 = female */
2110 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2112 if (options && !strncasecmp(options, "f", 1))
2115 while (!res && num ) {
2117 ast_copy_string(fn, "digits/minus", sizeof(fn));
2118 if ( num > INT_MIN ) {
2123 } else if (num < 20) {
2124 if ((num == 1 || num == 2) && (mf < 0))
2125 snprintf(fn, sizeof(fn), "digits/%dF", num);
2127 snprintf(fn, sizeof(fn), "digits/%d", num);
2129 } else if (num < 100) {
2130 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2134 } else if (num < 1000) {
2136 ast_copy_string(fn, "digits/100", sizeof(fn));
2138 ast_copy_string(fn, "digits/100E", sizeof(fn));
2140 if (mf < 0 && num > 199)
2141 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2143 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2148 } else if (num < 1000000) {
2150 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2154 ast_copy_string(fn, "digits/1000", sizeof(fn));
2155 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2158 } else if (num < 1000000000) {
2159 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2163 ast_copy_string(fn, "digits/1000000", sizeof(fn));
2165 ast_copy_string(fn, "digits/1000000S", sizeof(fn));
2167 if ((num % 1000000) &&
2169 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2170 /* no hundreds and below */
2171 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2173 num = num % 1000000;
2175 /* number is too big */
2176 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2180 if (!ast_streamfile(chan, fn, language)) {
2181 if ((audiofd > -1) && (ctrlfd > -1))
2182 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2184 res = ast_waitstream(chan, ints);
2186 ast_stopstream(chan);
2188 if (!res && playh) {
2189 res = wait_file(chan, ints, "digits/pt-e", language);
2190 ast_stopstream(chan);
2197 /*! \brief ast_say_number_full_se: Swedish syntax */
2198 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)
2203 int cn = 1; /* +1 = commune; -1 = neuter */
2205 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2206 if (options && !strncasecmp(options, "n", 1)) cn = -1;
2208 while (!res && (num || playh)) {
2210 ast_copy_string(fn, "digits/minus", sizeof(fn));
2211 if ( num > INT_MIN ) {
2217 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2219 } else if (num < 20) {
2220 snprintf(fn, sizeof(fn), "digits/%d", num);
2222 } else if (num < 100) {
2223 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2225 } else if (num == 1 && cn == -1) { /* En eller ett? */
2226 ast_copy_string(fn, "digits/1N", sizeof(fn));
2230 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2234 if (num < 1000000) { /* 1,000,000 */
2235 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
2240 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2242 if (num < 1000000000) { /* 1,000,000,000 */
2243 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
2248 ast_copy_string(fn, "digits/million", sizeof(fn));
2250 ast_debug(1, "Number '%d' is too big for me\n", num);
2257 if (!ast_streamfile(chan, fn, language)) {
2258 if ((audiofd > -1) && (ctrlfd > -1))
2259 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2261 res = ast_waitstream(chan, ints);
2262 ast_stopstream(chan);
2269 /*! \brief ast_say_number_full_zh: Taiwanese / Chinese syntax */
2270 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2276 int last_length = 0;
2280 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2282 while (!res && (num || playh || playt || playz)) {
2284 ast_copy_string(fn, "digits/minus", sizeof(fn));
2285 if ( num > INT_MIN ) {
2291 snprintf(fn, sizeof(fn), "digits/0");
2295 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2298 snprintf(fn, sizeof(fn), "digits/thousand");
2300 } else if (num < 10) {
2301 snprintf(buf, 10, "%d", num);
2302 if (last_length - strlen(buf) > 1 && last_length != 0) {
2303 last_length = strlen(buf);
2307 snprintf(fn, sizeof(fn), "digits/%d", num);
2309 } else if (num < 100) {
2310 snprintf(buf, 10, "%d", num);
2311 if (last_length - strlen(buf) > 1 && last_length != 0) {
2312 last_length = strlen(buf);
2316 last_length = strlen(buf);
2317 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2321 snprintf(buf, 10, "%d", num);
2322 if (last_length - strlen(buf) > 1 && last_length != 0) {
2323 last_length = strlen(buf);
2327 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2329 snprintf(buf, 10, "%d", num);
2330 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2331 last_length = strlen(buf);
2332 num -= ((num / 100) * 100);
2333 } else if (num < 10000){
2334 snprintf(buf, 10, "%d", num);
2335 snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
2337 snprintf(buf, 10, "%d", num);
2338 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2339 last_length = strlen(buf);
2340 num -= ((num / 1000) * 1000);
2341 } else if (num < 100000000) { /* 100,000,000 */
2342 res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
2345 snprintf(buf, 10, "%d", num);
2346 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2347 num -= ((num / 10000) * 10000);
2348 last_length = strlen(buf);
2349 snprintf(fn, sizeof(fn), "digits/wan");
2351 if (num < 1000000000) { /* 1,000,000,000 */
2352 res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
2355 snprintf(buf, 10, "%d", num);
2356 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2357 last_length = strlen(buf);
2358 num -= ((num / 100000000) * 100000000);
2359 snprintf(fn, sizeof(fn), "digits/yi");
2361 ast_debug(1, "Number '%d' is too big for me\n", num);
2367 if (!ast_streamfile(chan, fn, language)) {
2368 if ((audiofd > -1) && (ctrlfd > -1))
2369 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2371 res = ast_waitstream(chan, ints);
2373 ast_stopstream(chan);
2380 * \brief Counting in Urdu, the national language of Pakistan
2383 static int ast_say_number_full_ur(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2390 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2393 while (!res && (num || playh)) {
2395 snprintf(fn, sizeof(fn), "digits/hundred");
2397 } else if (num < 100) {
2398 snprintf(fn, sizeof(fn), "digits/%d", num);
2400 } else if (num < 1000) {
2401 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2403 num -= ((num / 100) * 100);
2404 } else if (num < 100000) { /* 1,00,000 */
2405 if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
2409 snprintf(fn, sizeof(fn), "digits/thousand");
2410 } else if (num < 10000000) { /* 1,00,00,000 */
2411 if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
2415 snprintf(fn, sizeof(fn), "digits/lac");
2416 } else if (num < 1000000000) { /* 1,00,00,00,000 */
2417 if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
2420 num = num % 10000000;
2421 snprintf(fn, sizeof(fn), "digits/crore");
2423 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2428 if (!ast_streamfile(chan, fn, language)) {
2429 if ((audiofd > -1) && (ctrlfd > -1)) {
2430 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2432 res = ast_waitstream(chan, ints);
2435 ast_stopstream(chan);
2441 /*! \brief determine last digits for thousands/millions (ru) */
2442 static int get_lastdigits_ru(int num) {
2445 } else if (num < 100) {
2446 return get_lastdigits_ru(num % 10);
2447 } else if (num < 1000) {
2448 return get_lastdigits_ru(num % 100);
2450 return 0; /* number too big */
2454 /*! \brief ast_say_number_full_ru: Russian syntax */
2455 /*! \brief additional files:
2456 n00.gsm (one hundred, two hundred, ...)
2459 thousands-i.gsm (tisyachi)
2460 million-a.gsm (milliona)
2466 where 'n' from 1 to 9
2468 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)
2474 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2476 while (!res && (num)) {
2478 ast_copy_string(fn, "digits/minus", sizeof(fn));
2479 if ( num > INT_MIN ) {
2484 } else if (num < 20) {
2485 if (options && strlen(options) == 1 && num < 3) {
2486 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2488 snprintf(fn, sizeof(fn), "digits/%d", num);
2491 } else if (num < 100) {
2492 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2494 } else if (num < 1000){
2495 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2497 } else if (num < 1000000) { /* 1,000,000 */
2498 lastdigits = get_lastdigits_ru(num / 1000);
2500 if (lastdigits < 3) {
2501 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2503 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2507 if (lastdigits == 1) {
2508 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2509 } else if (lastdigits > 1 && lastdigits < 5) {
2510 ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
2512 ast_copy_string(fn, "digits/thousands", sizeof(fn));
2515 } else if (num < 1000000000) { /* 1,000,000,000 */
2516 lastdigits = get_lastdigits_ru(num / 1000000);
2518 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2521 if (lastdigits == 1) {
2522 ast_copy_string(fn, "digits/million", sizeof(fn));
2523 } else if (lastdigits > 1 && lastdigits < 5) {
2524 ast_copy_string(fn, "digits/million-a", sizeof(fn));
2526 ast_copy_string(fn, "digits/millions", sizeof(fn));
2530 ast_debug(1, "Number '%d' is too big for me\n", num);
2534 if (!ast_streamfile(chan, fn, language)) {
2535 if ((audiofd > -1) && (ctrlfd > -1))
2536 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2538 res = ast_waitstream(chan, ints);
2540 ast_stopstream(chan);
2546 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2552 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2554 while(!res && (num || playh)) {
2556 ast_copy_string(fn, "digits/lop", sizeof(fn));
2557 if ( num > INT_MIN ) {
2563 ast_copy_string(fn, "digits/roi", sizeof(fn));
2565 } else if (num < 100) {
2566 if ((num <= 20) || ((num % 10) == 1)) {
2567 snprintf(fn, sizeof(fn), "digits/%d", num);
2570 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2573 } else if (num < 1000) {
2574 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2577 } else if (num < 10000) { /* 10,000 */
2578 res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
2582 ast_copy_string(fn, "digits/pan", sizeof(fn));
2583 } else if (num < 100000) { /* 100,000 */
2584 res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
2588 ast_copy_string(fn, "digits/muan", sizeof(fn));
2589 } else if (num < 1000000) { /* 1,000,000 */
2590 res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
2594 ast_copy_string(fn, "digits/san", sizeof(fn));
2596 res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2600 ast_copy_string(fn, "digits/larn", sizeof(fn));
2603 if(!ast_streamfile(chan, fn, language)) {
2604 if ((audiofd > -1) && (ctrlfd > -1))
2605 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2607 res = ast_waitstream(chan, ints);
2609 ast_stopstream(chan);
2615 /*! \brief ast_say_enumeration_full: call language-specific functions */
2616 /* Called from AGI */
2617 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2619 if (!strncasecmp(language, "en", 2)) { /* English syntax */
2620 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
2621 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
2622 return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
2623 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
2624 return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
2625 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
2626 return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
2629 /* Default to english */
2630 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
2633 /*! \brief ast_say_enumeration_full_en: English syntax */
2634 /* This is the default syntax, if no other syntax defined in this file is used */
2635 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2640 while (!res && num) {
2642 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
2643 if ( num > INT_MIN ) {
2648 } else if (num < 20) {
2649 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2651 } else if (num < 100) {
2652 int tens = num / 10;
2655 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2657 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2659 } else if (num < 1000) {
2660 int hundreds = num / 100;
2662 if (hundreds > 1 || t == 1) {
2663 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2668 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2670 ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
2672 } else if (num < 1000000) {
2673 int thousands = num / 1000;
2675 if (thousands > 1 || t == 1) {
2676 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2681 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2683 ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
2686 } else if (num < 1000000000) {
2687 int millions = num / 1000000;
2688 num = num % 1000000;
2690 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2694 ast_copy_string(fn, "digits/million", sizeof(fn));
2696 ast_copy_string(fn, "digits/h-million", sizeof(fn));
2698 } else if (num < INT_MAX) {
2699 int billions = num / 1000000000;
2700 num = num % 1000000000;
2702 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2706 ast_copy_string(fn, "digits/billion", sizeof(fn));
2708 ast_copy_string(fn, "digits/h-billion", sizeof(fn));
2710 } else if (num == INT_MAX) {
2711 ast_copy_string(fn, "digits/h-last", sizeof(fn));
2714 ast_debug(1, "Number '%d' is too big for me\n", num);
2719 if (!ast_streamfile(chan, fn, language)) {
2720 if ((audiofd > -1) && (ctrlfd > -1)) {
2721 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2723 res = ast_waitstream(chan, ints);
2726 ast_stopstream(chan);
2732 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2733 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)
2735 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2737 char fn[256] = "", fna[256] = "";
2740 if (options && !strncasecmp(options, "f", 1)) {
2742 } else if (options && !strncasecmp(options, "n", 1)) {
2749 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2751 while (!res && num) {
2753 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
2754 if ( num > INT_MIN ) {
2759 } else if (num < 100 && t) {
2760 ast_copy_string(fn, "digits/and", sizeof(fn));
2762 } else if (num < 20) {
2763 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2765 } else if (num < 100) {
2766 int ones = num % 10;
2768 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2771 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2774 } else if (num == 100 && t == 0) {
2775 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2777 } else if (num < 1000) {
2778 int hundreds = num / 100;
2780 if (hundreds == 1) {
2781 ast_copy_string(fn, "digits/1N", sizeof(fn));
2783 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2786 ast_copy_string(fna, "digits/hundred", sizeof(fna));
2788 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2791 } else if (num < 1000000) {
2792 int thousands = num / 1000;
2794 if (thousands == 1) {
2796 ast_copy_string(fn, "digits/1N", sizeof(fn));
2797 ast_copy_string(fna, "digits/thousand", sizeof(fna));
2800 ast_copy_string(fn, "digits/1N", sizeof(fn));
2801 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2803 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2807 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2812 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2814 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2818 } else if (num < 1000000000) {
2819 int millions = num / 1000000;
2820 num = num % 1000000;
2821 if (millions == 1) {
2823 ast_copy_string(fn, "digits/1F", sizeof(fn));
2824 ast_copy_string(fna, "digits/million", sizeof(fna));
2826 ast_copy_string(fn, "digits/1N", sizeof(fn));
2827 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2830 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2835 ast_copy_string(fn, "digits/millions", sizeof(fn));
2837 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2841 } else if (num < INT_MAX) {
2842 int billions = num / 1000000000;
2843 num = num % 1000000000;
2844 if (billions == 1) {
2846 ast_copy_string(fn, "digits/1F", sizeof(fn));
2847 ast_copy_string(fna, "digits/milliard", sizeof(fna));
2849 ast_copy_string(fn, "digits/1N", sizeof(fn));
2850 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2853 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2857 ast_copy_string(fn, "digits/milliards", sizeof(fna));
2859 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2863 } else if (num == INT_MAX) {
2864 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
2867 ast_debug(1, "Number '%d' is too big for me\n", num);
2872 if (!ast_streamfile(chan, fn, language)) {
2873 if ((audiofd > -1) && (ctrlfd > -1))
2874 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2876 res = ast_waitstream(chan, ints);
2878 ast_stopstream(chan);
2880 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
2881 if ((audiofd > -1) && (ctrlfd > -1)) {
2882 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2884 res = ast_waitstream(chan, ints);
2887 ast_stopstream(chan);
2895 /*! \brief ast_say_enumeration_full_de: German syntax */
2896 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)
2898 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2900 char fn[256] = "", fna[256] = "";
2903 if (options && !strncasecmp(options, "f", 1)) {
2905 } else if (options && !strncasecmp(options, "n", 1)) {
2912 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2914 while (!res && num) {
2916 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
2917 if ( num > INT_MIN ) {
2922 } else if (num < 100 && t) {
2923 ast_copy_string(fn, "digits/and", sizeof(fn));
2925 } else if (num < 20) {
2926 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2928 } else if (num < 100) {
2929 int ones = num % 10;
2931 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2934 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2937 } else if (num == 100 && t == 0) {
2938 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2940 } else if (num < 1000) {
2941 int hundreds = num / 100;
2943 if (hundreds == 1) {
2944 ast_copy_string(fn, "digits/1N", sizeof(fn));
2946 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2949 ast_copy_string(fna, "digits/hundred", sizeof(fna));
2951 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2954 } else if (num < 1000000) {
2955 int thousands = num / 1000;
2957 if (thousands == 1) {
2959 ast_copy_string(fn, "digits/1N", sizeof(fn));
2960 ast_copy_string(fna, "digits/thousand", sizeof(fna));
2963 ast_copy_string(fn, "digits/1N", sizeof(fn));
2964 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2966 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2970 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2975 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2977 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2981 } else if (num < 1000000000) {
2982 int millions = num / 1000000;
2983 num = num % 1000000;
2984 if (millions == 1) {
2986 ast_copy_string(fn, "digits/1F", sizeof(fn));
2987 ast_copy_string(fna, "digits/million", sizeof(fna));
2989 ast_copy_string(fn, "digits/1N", sizeof(fn));
2990 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2993 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2998 ast_copy_string(fn, "digits/millions", sizeof(fn));
3000 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
3004 } else if (num < INT_MAX) {
3005 int billions = num / 1000000000;
3006 num = num % 1000000000;
3007 if (billions == 1) {
3009 ast_copy_string(fn, "digits/1F", sizeof(fn));
3010 ast_copy_string(fna, "digits/milliard", sizeof(fna));
3012 ast_copy_string(fn, "digits/1N", sizeof(fn));
3013 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
3016 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
3020 ast_copy_string(fn, "digits/milliards", sizeof(fna));
3022 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
3026 } else if (num == INT_MAX) {
3027 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3030 ast_debug(1, "Number '%d' is too big for me\n", num);
3035 if (!ast_streamfile(chan, fn, language)) {
3036 if ((audiofd > -1) && (ctrlfd > -1))
3037 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3039 res = ast_waitstream(chan, ints);
3041 ast_stopstream(chan);
3043 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3044 if ((audiofd > -1) && (ctrlfd > -1)) {
3045 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3047 res = ast_waitstream(chan, ints);
3050 ast_stopstream(chan);
3058 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)
3062 int mf = -1; /* +1 = Masculin; -1 = Feminin */
3063 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
3065 if (options && !strncasecmp(options, "m", 1)) {
3069 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, options=\"%s\", mf=%d\n", num, options, mf);
3071 while (!res && num) {
3073 snprintf(fn, sizeof(fn), "digits/minus"); /* kind of senseless for enumerations, but our best effort for error checking */
3074 if (num > INT_MIN) {