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);
353 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
355 /* Forward declarations of language specific variants of ast_say_enumeration_full */
356 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
357 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);
358 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);
359 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 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
362 /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
363 static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
364 static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
365 static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
366 static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
367 static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
368 static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
369 static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
370 static int ast_say_date_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
371 static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
372 static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
373 static int ast_say_date_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
375 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);
376 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);
377 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);
378 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);
379 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);
380 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);
381 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);
382 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);
383 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);
384 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);
385 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);
386 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);
387 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);
388 static int ast_say_date_with_format_vi(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *tzone);
390 static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
391 static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
392 static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
393 static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
394 static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
395 static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
396 static int ast_say_time_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
397 static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
398 static int ast_say_time_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
399 static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
400 static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
401 static int ast_say_time_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
403 static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
404 static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
405 static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
406 static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
407 static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
408 static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
409 static int ast_say_datetime_zh(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
410 static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
411 static int ast_say_datetime_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
412 static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
413 static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
414 static int ast_say_datetime_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
416 static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
417 static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
418 static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
419 static int ast_say_datetime_from_now_ka(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
420 static int ast_say_datetime_from_now_he(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
422 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
425 if ((res = ast_streamfile(chan, file, lang)))
426 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
428 res = ast_waitstream(chan, ints);
432 /*! \brief ast_say_number_full: call language-specific functions
433 \note Called from AGI */
434 static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
436 if (!strncasecmp(language, "en_GB", 5)) { /* British syntax */
437 return ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd);
438 } else if (!strncasecmp(language, "en", 2)) { /* English syntax */
439 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
440 } else if (!strncasecmp(language, "cs", 2)) { /* Czech syntax */
441 return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
442 } else if (!strncasecmp(language, "cz", 2)) { /* deprecated Czech syntax */
443 static int deprecation_warning = 0;
444 if (deprecation_warning++ % 10 == 0) {
445 ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
447 return ast_say_number_full_cs(chan, num, ints, language, options, audiofd, ctrlfd);
448 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
449 return ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
450 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
451 return ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
452 } else if (!strncasecmp(language, "es", 2)) { /* Spanish syntax */
453 return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
454 } else if (!strncasecmp(language, "fr", 2)) { /* French syntax */
455 return ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd);
456 } else if (!strncasecmp(language, "ge", 2)) { /* deprecated Georgian syntax */
457 static int deprecation_warning = 0;
458 if (deprecation_warning++ % 10 == 0) {
459 ast_log(LOG_WARNING, "ge is not a standard language code. Please switch to using ka instead.\n");
461 return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
462 } else if (!strncasecmp(language, "gr", 2)) { /* Greek syntax */
463 return ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd);
464 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
465 return ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
466 } else if (!strncasecmp(language, "hu", 2)) { /* Hungarian syntax */
467 return ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd);
468 } else if (!strncasecmp(language, "it", 2)) { /* Italian syntax */
469 return ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd);
470 } else if (!strncasecmp(language, "ka", 2)) { /* Georgian syntax */
471 return ast_say_number_full_ka(chan, num, ints, language, options, audiofd, ctrlfd);
472 } else if (!strncasecmp(language, "mx", 2)) { /* deprecated Mexican syntax */
473 static int deprecation_warning = 0;
474 if (deprecation_warning++ % 10 == 0) {
475 ast_log(LOG_WARNING, "mx is not a standard language code. Please switch to using es_MX instead.\n");
477 return ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd);
478 } else if (!strncasecmp(language, "nl", 2)) { /* Dutch syntax */
479 return ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd);
480 } else if (!strncasecmp(language, "no", 2)) { /* Norwegian syntax */
481 return ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd);
482 } else if (!strncasecmp(language, "pl", 2)) { /* Polish syntax */
483 return ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd);
484 } else if (!strncasecmp(language, "pt", 2)) { /* Portuguese syntax */
485 return ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd);
486 } else if (!strncasecmp(language, "ru", 2)) { /* Russian syntax */
487 return ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd);
488 } else if (!strncasecmp(language, "se", 2)) { /* Swedish syntax */
489 return ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd);
490 } else if (!strncasecmp(language, "th", 2)) { /* Thai syntax */
491 return ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd);
492 } else if (!strncasecmp(language, "tw", 2)) { /* deprecated Taiwanese syntax */
493 static int deprecation_warning = 0;
494 if (deprecation_warning++ % 10 == 0) {
495 ast_log(LOG_WARNING, "tw is a standard language code for Twi, not Taiwanese. Please switch to using zh_TW instead.\n");
497 return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
498 } else if (!strncasecmp(language, "zh", 2)) { /* Taiwanese / Chinese syntax */
499 return ast_say_number_full_zh(chan, num, ints, language, audiofd, ctrlfd);
500 } else if (!strncasecmp(language, "ur", 2)) { /* Urdu syntax */
501 return ast_say_number_full_ur(chan, num, ints, language, options, audiofd, ctrlfd);
502 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
503 return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
506 /* Default to english */
507 return ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd);
510 /*! \brief ast_say_number_full_en: English syntax
511 \note This is the default syntax, if no other syntax defined in this file is used */
512 static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
518 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
520 while (!res && (num || playh)) {
522 ast_copy_string(fn, "digits/minus", sizeof(fn));
523 if ( num > INT_MIN ) {
529 ast_copy_string(fn, "digits/hundred", sizeof(fn));
531 } else if (num < 20) {
532 snprintf(fn, sizeof(fn), "digits/%d", num);
534 } else if (num < 100) {
535 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
539 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
543 if (num < 1000000) { /* 1,000,000 */
544 res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
548 snprintf(fn, sizeof(fn), "digits/thousand");
550 if (num < 1000000000) { /* 1,000,000,000 */
551 res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
555 ast_copy_string(fn, "digits/million", sizeof(fn));
557 ast_debug(1, "Number '%d' is too big for me\n", num);
564 if (!ast_streamfile(chan, fn, language)) {
565 if ((audiofd > -1) && (ctrlfd > -1))
566 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
568 res = ast_waitstream(chan, ints);
570 ast_stopstream(chan);
576 static int exp10_int(int power)
579 for (x=0;x<power;x++)
584 /*! \brief ast_say_number_full_cs: Czech syntax
587 * - 1m,2m - gender male
588 * - 1w,2w - gender female
592 * - hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
594 * for each number 10^(3n + 3) exist 3 files represented as:
595 * 1 tousand = jeden tisic = 1_E3
596 * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
597 * 5,6,... tousands = pet,sest,... tisic = 5_E3
603 * tousand, milion are gender male, so 1 and 2 is 1m 2m
604 * miliard is gender female, so 1 and 2 is 1w 2w
606 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)
616 /* options - w = woman, m = man, n = neutral. Defaultl is woman */
621 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
623 while (!res && (num || playh)) {
625 ast_copy_string(fn, "digits/minus", sizeof(fn));
626 if ( num > INT_MIN ) {
631 } else if (num < 3 ) {
632 snprintf(fn, sizeof(fn), "digits/%d%c", num, options[0]);
635 } else if (num < 20) {
636 snprintf(fn, sizeof(fn), "digits/%d", num);
639 } else if (num < 100) {
640 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
642 } else if (num < 1000) {
643 hundered = num / 100;
644 if ( hundered == 1 ) {
645 ast_copy_string(fn, "digits/1sto", sizeof(fn));
646 } else if ( hundered == 2 ) {
647 ast_copy_string(fn, "digits/2ste", sizeof(fn));
649 res = ast_say_number_full_cs(chan, hundered, ints, language, options, audiofd, ctrlfd);
652 if (hundered == 3 || hundered == 4) {
653 ast_copy_string(fn, "digits/sta", sizeof(fn));
654 } else if ( hundered > 4 ) {
655 ast_copy_string(fn, "digits/set", sizeof(fn));
658 num -= (hundered * 100);
659 } else { /* num > 1000 */
660 length = (int)log10(num)+1;
661 while ( (length % 3 ) != 1 ) {
664 left = num / (exp10_int(length-1));
667 case 9: options = "w"; /* 1,000,000,000 gender female */
669 default : options = "m"; /* others are male */
672 if ( left > 1 ) { /* we don't say "one thousand" but only thousand */
673 res = ast_say_number_full_cs(chan, left, ints, language, options, audiofd, ctrlfd);
677 if ( left >= 5 ) { /* >= 5 have the same declesion */
678 snprintf(fn, sizeof(fn), "digits/5_E%d", length - 1);
679 } else if ( left >= 2 && left <= 4 ) {
680 snprintf(fn, sizeof(fn), "digits/2-4_E%d", length - 1);
681 } else { /* left == 1 */
682 snprintf(fn, sizeof(fn), "digits/1_E%d", length - 1);
684 num -= left * (exp10_int(length-1));
687 if (!ast_streamfile(chan, fn, language)) {
688 if ((audiofd > -1) && (ctrlfd > -1)) {
689 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
691 res = ast_waitstream(chan, ints);
694 ast_stopstream(chan);
700 /*! \brief ast_say_number_full_da: Danish syntax
702 - In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
704 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)
709 int cn = 1; /* +1 = commune; -1 = neuter */
712 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
714 if (options && !strncasecmp(options, "n", 1)) cn = -1;
716 while (!res && (num || playh || playa )) {
717 /* The grammar for Danish numbers is the same as for English except
719 * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
720 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
721 * "one-and twenty" and 68 is "eight-and sixty".
722 * - "million" is different in singular and plural form
723 * - numbers > 1000 with zero as the third digit from last have an
724 * "and" before the last two digits, i.e. 2034 is "two thousand and
725 * four-and thirty" and 1000012 is "one million and twelve".
728 ast_copy_string(fn, "digits/minus", sizeof(fn));
729 if ( num > INT_MIN ) {
735 ast_copy_string(fn, "digits/hundred", sizeof(fn));
738 ast_copy_string(fn, "digits/and", sizeof(fn));
740 } else if (num == 1 && cn == -1) {
741 ast_copy_string(fn, "digits/1N", sizeof(fn));
743 } else if (num < 20) {
744 snprintf(fn, sizeof(fn), "digits/%d", num);
746 } else if (num < 100) {
749 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
752 snprintf(fn, sizeof(fn), "digits/%d", num);
757 int hundreds = num / 100;
759 ast_copy_string(fn, "digits/1N", sizeof(fn));
761 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
764 num -= 100 * hundreds;
770 res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
774 ast_copy_string(fn, "digits/thousand", sizeof(fn));
776 if (num < 1000000000) {
777 int millions = num / 1000000;
778 res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
782 ast_copy_string(fn, "digits/million", sizeof(fn));
784 ast_copy_string(fn, "digits/millions", sizeof(fn));
787 ast_debug(1, "Number '%d' is too big for me\n", num);
791 if (num && num < 100)
796 if (!ast_streamfile(chan, fn, language)) {
797 if ((audiofd > -1) && (ctrlfd > -1))
798 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
800 res = ast_waitstream(chan, ints);
802 ast_stopstream(chan);
808 /*! \brief ast_say_number_full_de: German syntax
811 In addition to English, the following sounds are required:
813 - "1-and" through "9-and"
816 - NB "1" is recorded as 'eins'
818 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)
821 int mf = 1; /* +1 = male and neuter; -1 = female */
825 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
827 if (options && (!strncasecmp(options, "f", 1)))
830 while (!res && num) {
831 /* The grammar for German numbers is the same as for English except
833 * - numbers 20 through 99 are said in reverse order, i.e. 21 is
834 * "one-and twenty" and 68 is "eight-and sixty".
835 * - "one" varies according to gender
836 * - 100 is 'hundert', however all other instances are 'ein hundert'
837 * - 1000 is 'tausend', however all other instances are 'ein tausend'
838 * - 1000000 is always 'eine million'
839 * - "million" is different in singular and plural form
842 ast_copy_string(fn, "digits/minus", sizeof(fn));
843 if ( num > INT_MIN ) {
848 } else if (num < 100 && t) {
849 ast_copy_string(fn, "digits/and", sizeof(fn));
851 } else if (num == 1 && mf == -1) {
852 snprintf(fn, sizeof(fn), "digits/%dF", num);
854 } else if (num < 20) {
855 snprintf(fn, sizeof(fn), "digits/%d", num);
857 } else if (num < 100) {
860 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
863 snprintf(fn, sizeof(fn), "digits/%d", num);
866 } else if (num == 100 && t == 0) {
867 ast_copy_string(fn, "digits/hundred", sizeof(fn));
869 } else if (num < 1000) {
870 int hundreds = num / 100;
873 ast_copy_string(fn, "digits/1N", sizeof(fn));
875 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
877 ast_copy_string(fna, "digits/hundred", sizeof(fna));
879 } else if (num == 1000 && t == 0) {
880 ast_copy_string(fn, "digits/thousand", sizeof(fn));
882 } else if (num < 1000000) {
883 int thousands = num / 1000;
886 if (thousands == 1) {
887 ast_copy_string(fn, "digits/1N", sizeof(fn));
888 ast_copy_string(fna, "digits/thousand", sizeof(fna));
890 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
893 ast_copy_string(fn, "digits/thousand", sizeof(fn));
895 } else if (num < 1000000000) {
896 int millions = num / 1000000;
900 ast_copy_string(fn, "digits/1F", sizeof(fn));
901 ast_copy_string(fna, "digits/million", sizeof(fna));
903 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
906 ast_copy_string(fn, "digits/millions", sizeof(fn));
908 } else if (num <= INT_MAX) {
909 int billions = num / 1000000000;
910 num = num % 1000000000;
913 ast_copy_string(fn, "digits/1F", sizeof(fn));
914 ast_copy_string(fna, "digits/milliard", sizeof(fna));
916 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
920 ast_copy_string(fn, "digits/milliards", sizeof(fn));
923 ast_debug(1, "Number '%d' is too big for me\n", num);
927 if (!ast_streamfile(chan, fn, language)) {
928 if ((audiofd > -1) && (ctrlfd > -1))
929 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
931 res = ast_waitstream(chan, ints);
933 ast_stopstream(chan);
935 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
936 if ((audiofd > -1) && (ctrlfd > -1))
937 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
939 res = ast_waitstream(chan, ints);
941 ast_stopstream(chan);
949 /*! \brief ast_say_number_full_en_GB: British syntax
951 - In addition to American English, the following sounds are required: "and"
953 static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
960 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
962 while (!res && (num || playh || playa )) {
964 ast_copy_string(fn, "digits/minus", sizeof(fn));
965 if ( num > INT_MIN ) {
971 ast_copy_string(fn, "digits/hundred", sizeof(fn));
974 ast_copy_string(fn, "digits/and", sizeof(fn));
976 } else if (num < 20) {
977 snprintf(fn, sizeof(fn), "digits/%d", num);
979 } else if (num < 100) {
980 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
982 } else if (num < 1000) {
983 int hundreds = num / 100;
984 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
987 num -= 100 * hundreds;
990 } else if (num < 1000000) {
991 res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
994 ast_copy_string(fn, "digits/thousand", sizeof(fn));
996 if (num && num < 100)
998 } else if (num < 1000000000) {
999 int millions = num / 1000000;
1000 res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
1003 ast_copy_string(fn, "digits/million", sizeof(fn));
1005 if (num && num < 100)
1008 ast_debug(1, "Number '%d' is too big for me\n", num);
1013 if (!ast_streamfile(chan, fn, language)) {
1014 if ((audiofd > -1) && (ctrlfd > -1))
1015 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1017 res = ast_waitstream(chan, ints);
1019 ast_stopstream(chan);
1025 /*! \brief ast_say_number_full_es: Spanish syntax
1028 Requires a few new audios:
1029 1F.gsm: feminine 'una'
1030 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
1032 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)
1036 int mf = 0; /* +1 = male; -1 = female */
1039 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1042 if (!strncasecmp(options, "f", 1))
1044 else if (!strncasecmp(options, "m", 1))
1048 while (!res && num) {
1050 ast_copy_string(fn, "digits/minus", sizeof(fn));
1051 if ( num > INT_MIN ) {
1057 ast_copy_string(fn, "digits/and", sizeof(fn));
1059 } else if (num == 1) {
1061 snprintf(fn, sizeof(fn), "digits/%dF", num);
1063 snprintf(fn, sizeof(fn), "digits/%dM", num);
1065 snprintf(fn, sizeof(fn), "digits/%d", num);
1067 } else if (num < 31) {
1068 snprintf(fn, sizeof(fn), "digits/%d", num);
1070 } else if (num < 100) {
1071 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1075 } else if (num == 100) {
1076 ast_copy_string(fn, "digits/100", sizeof(fn));
1078 } else if (num < 200) {
1079 ast_copy_string(fn, "digits/100-and", sizeof(fn));
1083 snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
1085 } else if (num < 2000) {
1087 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1089 if (num < 1000000) {
1090 res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1094 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1096 if (num < 2147483640) {
1097 if ((num/1000000) == 1) {
1098 res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
1101 ast_copy_string(fn, "digits/million", sizeof(fn));
1103 res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1106 ast_copy_string(fn, "digits/millions", sizeof(fn));
1110 ast_debug(1, "Number '%d' is too big for me\n", num);
1118 if (!ast_streamfile(chan, fn, language)) {
1119 if ((audiofd > -1) && (ctrlfd > -1))
1120 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1122 res = ast_waitstream(chan, ints);
1124 ast_stopstream(chan);
1132 /*! \brief ast_say_number_full_fr: French syntax
1133 Extra sounds needed:
1136 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)
1141 int mf = 1; /* +1 = male; -1 = female */
1144 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1146 if (options && !strncasecmp(options, "f", 1))
1149 while (!res && (num || playh || playa)) {
1151 ast_copy_string(fn, "digits/minus", sizeof(fn));
1152 if ( num > INT_MIN ) {
1158 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1161 ast_copy_string(fn, "digits/et", sizeof(fn));
1163 } else if (num == 1) {
1165 snprintf(fn, sizeof(fn), "digits/%dF", num);
1167 snprintf(fn, sizeof(fn), "digits/%d", num);
1169 } else if (num < 21) {
1170 snprintf(fn, sizeof(fn), "digits/%d", num);
1172 } else if (num < 70) {
1173 snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
1174 if ((num % 10) == 1) playa++;
1176 } else if (num < 80) {
1177 ast_copy_string(fn, "digits/60", sizeof(fn));
1178 if ((num % 10) == 1) playa++;
1180 } else if (num < 100) {
1181 ast_copy_string(fn, "digits/80", sizeof(fn));
1183 } else if (num < 200) {
1184 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1186 } else if (num < 1000) {
1187 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1190 } else if (num < 2000) {
1191 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1193 } else if (num < 1000000) {
1194 res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
1197 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1199 } else if (num < 1000000000) {
1200 res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
1203 ast_copy_string(fn, "digits/million", sizeof(fn));
1204 num = num % 1000000;
1206 ast_debug(1, "Number '%d' is too big for me\n", num);
1210 if (!ast_streamfile(chan, fn, language)) {
1211 if ((audiofd > -1) && (ctrlfd > -1))
1212 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1214 res = ast_waitstream(chan, ints);
1216 ast_stopstream(chan);
1225 * Check doc/lang/hebrew-digits.txt for information about the various
1226 * recordings required to make this translation work properly */
1227 #define SAY_NUM_BUF_SIZE 256
1228 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)
1231 int state = 0; /* no need to save anything */
1232 int mf = -1; /* +1 = Masculin; -1 = Feminin */
1235 char fn[SAY_NUM_BUF_SIZE] = "";
1237 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: started. num: %d, options=\"%s\"\n", num, options);
1240 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1242 if (options && !strncasecmp(options, "m", 1)) {
1245 ast_verbose(VERBOSE_PREFIX_3 "ast_say_digits_full: num: %d, state=%d, options=\"%s\", mf=%d\n", num, state, options, mf);
1247 /* Do we have work to do? */
1248 while (!res && (num || (state > 0))) {
1249 /* first type of work: play a second sound. In this loop
1250 * we can only play one sound file at a time. Thus playing
1251 * a second one requires repeating the loop just for the
1252 * second file. The variable 'state' remembers where we were.
1253 * state==0 is the normal mode and it means that we continue
1254 * to check if the number num has yet anything left.
1256 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);
1260 } else if (state == 2) {
1261 if ((num >= 11) && (num < 21)) {
1263 snprintf(fn, sizeof(fn), "digits/ve");
1265 snprintf(fn, sizeof(fn), "digits/uu");
1270 snprintf(fn, sizeof(fn), "digits/ve");
1273 snprintf(fn, sizeof(fn), "digits/uu");
1277 snprintf(fn, sizeof(fn), "digits/ve");
1279 snprintf(fn, sizeof(fn), "digits/uu");
1283 snprintf(fn, sizeof(fn), "digits/ve");
1286 snprintf(fn, sizeof(fn), "digits/ve");
1289 snprintf(fn, sizeof(fn), "digits/ve");
1292 snprintf(fn, sizeof(fn), "digits/ve");
1295 snprintf(fn, sizeof(fn), "digits/uu");
1298 snprintf(fn, sizeof(fn), "digits/ve");
1301 snprintf(fn, sizeof(fn), "digits/ve");
1306 } else if (state == 3) {
1307 snprintf(fn, sizeof(fn), "digits/1k");
1309 } else if (num < 0) {
1310 snprintf(fn, sizeof(fn), "digits/minus");
1312 } else if (num < 20) {
1314 snprintf(fn, sizeof(fn), "digits/%d", num);
1316 snprintf(fn, sizeof(fn), "digits/%dm", num);
1319 } else if ((num < 100) && (num >= 20)) {
1320 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
1325 } else if ((num >= 100) && (num < 1000)) {
1327 snprintf(fn, sizeof(fn), "digits/%d00", tmpnum);
1328 num = num - (tmpnum * 100);
1329 if ((num > 0) && (num < 11)) {
1332 } else if ((num >= 1000) && (num < 10000)) {
1333 tmpnum = num / 1000;
1334 snprintf(fn, sizeof(fn), "digits/%dk", tmpnum);
1335 num = num - (tmpnum * 1000);
1336 if ((num > 0) && (num < 11)) {
1339 } else if (num < 20000) {
1340 snprintf(fn, sizeof(fn), "digits/%dm", (num / 1000));
1343 } else if (num < 1000000) {
1344 res = ast_say_number_full_he(chan, num / 1000, ints, language, "m", audiofd, ctrlfd);
1348 snprintf(fn, sizeof(fn), "digits/1k");
1350 if ((num > 0) && (num < 11)) {
1353 } else if (num < 2000000) {
1354 snprintf(fn, sizeof(fn), "digits/million");
1355 num = num % 1000000;
1356 if ((num > 0) && (num < 11)) {
1359 } else if (num < 3000000) {
1360 snprintf(fn, sizeof(fn), "digits/twomillion");
1361 num = num - 2000000;
1362 if ((num > 0) && (num < 11)) {
1365 } else if (num < 1000000000) {
1366 res = ast_say_number_full_he(chan, num / 1000000, ints, language, "m", audiofd, ctrlfd);
1370 snprintf(fn, sizeof(fn), "digits/million");
1371 num = num % 1000000;
1372 if ((num > 0) && (num < 11)) {
1376 ast_debug(1, "Number '%d' is too big for me\n", num);
1381 if (!ast_streamfile(chan, fn, language)) {
1382 if ((audiofd > -1) && (ctrlfd > -1)) {
1383 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1385 res = ast_waitstream(chan, ints);
1388 ast_stopstream(chan);
1394 /*! \brief ast_say_number_full_hu: Hungarian syntax
1396 Extra sounds needed:
1400 static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1406 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1410 like english, except numbers up to 29 are from 2 words.
1411 10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
1414 while(!res && (num || playh)) {
1416 ast_copy_string(fn, "digits/minus", sizeof(fn));
1417 if ( num > INT_MIN ) {
1423 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1425 } else if (num < 11 || num == 20) {
1426 snprintf(fn, sizeof(fn), "digits/%d", num);
1428 } else if (num < 20) {
1429 ast_copy_string(fn, "digits/10en", sizeof(fn));
1431 } else if (num < 30) {
1432 ast_copy_string(fn, "digits/20on", sizeof(fn));
1434 } else if (num < 100) {
1435 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1439 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1443 if (num < 1000000) { /* 1,000,000 */
1444 res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
1448 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1450 if (num < 1000000000) { /* 1,000,000,000 */
1451 res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1455 ast_copy_string(fn, "digits/million", sizeof(fn));
1457 ast_debug(1, "Number '%d' is too big for me\n", num);
1464 if(!ast_streamfile(chan, fn, language)) {
1465 if ((audiofd > -1) && (ctrlfd > -1))
1466 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1468 res = ast_waitstream(chan, ints);
1470 ast_stopstream(chan);
1476 /*! \brief ast_say_number_full_it: Italian */
1477 static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1485 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1490 Like english, numbers up to 20 are a single 'word', and others
1491 compound, but with exceptions.
1492 For example 21 is not twenty-one, but there is a single word in 'it'.
1493 Idem for 28 (ie when a the 2nd part of a compund number
1494 starts with a vowel)
1496 There are exceptions also for hundred, thousand and million.
1497 In english 100 = one hundred, 200 is two hundred.
1498 In italian 100 = cento , like to say hundred (without one),
1499 200 and more are like english.
1501 Same applies for thousand:
1502 1000 is one thousand in en, 2000 is two thousand.
1503 In it we have 1000 = mille , 2000 = 2 mila
1505 For million(s) we use the plural, if more than one
1506 Also, one million is abbreviated in it, like on-million,
1507 or 'un milione', not 'uno milione'.
1508 So the right file is provided.
1511 while (!res && (num || playh)) {
1513 ast_copy_string(fn, "digits/minus", sizeof(fn));
1514 if ( num > INT_MIN ) {
1520 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1522 } else if (num < 20) {
1523 snprintf(fn, sizeof(fn), "digits/%d", num);
1525 } else if (num == 21) {
1526 snprintf(fn, sizeof(fn), "digits/%d", num);
1528 } else if (num == 28) {
1529 snprintf(fn, sizeof(fn), "digits/%d", num);
1531 } else if (num == 31) {
1532 snprintf(fn, sizeof(fn), "digits/%d", num);
1534 } else if (num == 38) {
1535 snprintf(fn, sizeof(fn), "digits/%d", num);
1537 } else if (num == 41) {
1538 snprintf(fn, sizeof(fn), "digits/%d", num);
1540 } else if (num == 48) {
1541 snprintf(fn, sizeof(fn), "digits/%d", num);
1543 } else if (num == 51) {
1544 snprintf(fn, sizeof(fn), "digits/%d", num);
1546 } else if (num == 58) {
1547 snprintf(fn, sizeof(fn), "digits/%d", num);
1549 } else if (num == 61) {
1550 snprintf(fn, sizeof(fn), "digits/%d", num);
1552 } else if (num == 68) {
1553 snprintf(fn, sizeof(fn), "digits/%d", num);
1555 } else if (num == 71) {
1556 snprintf(fn, sizeof(fn), "digits/%d", num);
1558 } else if (num == 78) {
1559 snprintf(fn, sizeof(fn), "digits/%d", num);
1561 } else if (num == 81) {
1562 snprintf(fn, sizeof(fn), "digits/%d", num);
1564 } else if (num == 88) {
1565 snprintf(fn, sizeof(fn), "digits/%d", num);
1567 } else if (num == 91) {
1568 snprintf(fn, sizeof(fn), "digits/%d", num);
1570 } else if (num == 98) {
1571 snprintf(fn, sizeof(fn), "digits/%d", num);
1573 } else if (num < 100) {
1574 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1578 if ((num / 100) > 1) {
1579 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
1582 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1586 if (num < 1000000) { /* 1,000,000 */
1588 res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
1593 if ((tempnum / 1000) < 2)
1594 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1595 else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
1596 ast_copy_string(fn, "digits/thousands", sizeof(fn));
1598 if (num < 1000000000) { /* 1,000,000,000 */
1599 if ((num / 1000000) > 1)
1600 res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1605 if ((tempnum / 1000000) < 2)
1606 ast_copy_string(fn, "digits/million", sizeof(fn));
1608 ast_copy_string(fn, "digits/millions", sizeof(fn));
1610 ast_debug(1, "Number '%d' is too big for me\n", num);
1617 if (!ast_streamfile(chan, fn, language)) {
1618 if ((audiofd > -1) && (ctrlfd > -1))
1619 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1621 res = ast_waitstream(chan, ints);
1623 ast_stopstream(chan);
1629 /*! \brief ast_say_number_full_nl: dutch syntax
1630 * New files: digits/nl-en
1632 static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
1639 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1640 while (!res && (num || playh )) {
1642 ast_copy_string(fn, "digits/minus", sizeof(fn));
1643 if ( num > INT_MIN ) {
1649 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1651 } else if (num < 20) {
1652 snprintf(fn, sizeof(fn), "digits/%d", num);
1654 } else if (num < 100) {
1657 res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
1661 ast_copy_string(fn, "digits/nl-en", sizeof(fn));
1663 snprintf(fn, sizeof(fn), "digits/%d", num - units);
1666 } else if (num < 200) {
1667 /* hundred, not one-hundred */
1668 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1670 } else if (num < 1000) {
1671 snprintf(fn, sizeof(fn), "digits/%d", num / 100);
1676 /* thousand, not one-thousand */
1678 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1679 } else if (num < 10000) { /* 1,100 to 9,9999 */
1680 res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
1684 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1686 if (num < 1000000) { /* 1,000,000 */
1687 res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
1691 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1693 if (num < 1000000000) { /* 1,000,000,000 */
1694 res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
1698 ast_copy_string(fn, "digits/million", sizeof(fn));
1700 ast_debug(1, "Number '%d' is too big for me\n", num);
1708 if (!ast_streamfile(chan, fn, language)) {
1709 if ((audiofd > -1) && (ctrlfd > -1))
1710 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1712 res = ast_waitstream(chan, ints);
1714 ast_stopstream(chan);
1720 /*! \brief ast_say_number_full_no: Norwegian syntax
1722 * In addition to American English, the following sounds are required: "and", "1N"
1724 * The grammar for Norwegian numbers is the same as for English except
1725 * for the following:
1726 * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
1727 * "and" before the last two digits, i.e. 2034 is "two thousand and
1728 * thirty-four" and 1000012 is "one million and twelve".
1730 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)
1735 int cn = 1; /* +1 = commune; -1 = neuter */
1739 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
1741 if (options && !strncasecmp(options, "n", 1)) cn = -1;
1743 while (!res && (num || playh || playa )) {
1745 ast_copy_string(fn, "digits/minus", sizeof(fn));
1746 if ( num > INT_MIN ) {
1752 ast_copy_string(fn, "digits/hundred", sizeof(fn));
1755 ast_copy_string(fn, "digits/and", sizeof(fn));
1757 } else if (num == 1 && cn == -1) {
1758 ast_copy_string(fn, "digits/1N", sizeof(fn));
1760 } else if (num < 20) {
1761 snprintf(fn, sizeof(fn), "digits/%d", num);
1763 } else if (num < 100) {
1764 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
1766 } else if (num < 1000) {
1767 int hundreds = num / 100;
1769 ast_copy_string(fn, "digits/1N", sizeof(fn));
1771 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
1774 num -= 100 * hundreds;
1777 } else if (num < 1000000) {
1778 res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
1781 ast_copy_string(fn, "digits/thousand", sizeof(fn));
1783 if (num && num < 100)
1785 } else if (num < 1000000000) {
1786 int millions = num / 1000000;
1787 res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
1790 ast_copy_string(fn, "digits/million", sizeof(fn));
1792 if (num && num < 100)
1795 ast_debug(1, "Number '%d' is too big for me\n", num);
1800 if (!ast_streamfile(chan, fn, language)) {
1801 if ((audiofd > -1) && (ctrlfd > -1))
1802 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1804 res = ast_waitstream(chan, ints);
1806 ast_stopstream(chan);
1813 char *separator_dziesiatek;
1817 char *dziesiatki[10];
1822 static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
1828 return odm->rzedy[rzad - 1][0];
1829 if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
1830 return odm->rzedy[rzad - 1][1];
1832 return odm->rzedy[rzad - 1][2];
1835 static char* pl_append(char* buffer, char* str)
1837 strcpy(buffer, str);
1838 buffer += strlen(str);
1842 static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
1844 char file_name[255] = "digits/";
1845 strcat(file_name, fn);
1846 ast_debug(1, "Trying to play: %s\n", file_name);
1847 if (!ast_streamfile(chan, file_name, language)) {
1848 if ((audiofd > -1) && (ctrlfd > -1))
1849 ast_waitstream_full(chan, ints, audiofd, ctrlfd);
1851 ast_waitstream(chan, ints);
1853 ast_stopstream(chan);
1856 static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
1858 /* Initialise variables to allow compilation on Debian-stable, etc */
1868 if (i == 0 && rzad > 0) {
1872 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
1876 m1000E6 = i % 1000000000;
1877 i1000E6 = i / 1000000000;
1879 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
1881 m1000E3 = m1000E6 % 1000000;
1882 i1000E3 = m1000E6 / 1000000;
1884 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
1886 m1000 = m1000E3 % 1000;
1887 i1000 = m1000E3 / 1000;
1889 powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
1895 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
1897 if ( m100 > 0 && m100 <=9 ) {
1899 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
1901 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
1902 } else if (m100 % 10 == 0) {
1903 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1904 } else if (m100 <= 19 ) {
1905 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
1906 } else if (m100 != 0) {
1907 if (odm->separator_dziesiatek[0]==' ') {
1908 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
1909 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
1913 b = pl_append(b, odm->dziesiatki[m100 / 10]);
1914 b = pl_append(b, odm->separator_dziesiatek);
1915 b = pl_append(b, odm->cyfry2[m100 % 10]);
1916 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
1921 pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
1925 /* ast_say_number_full_pl: Polish syntax
1935 1000000000.2 miliardy
1936 1000000000.5 miliardow
2000 70m siedemdziesieciu
2012 90m dziewiedziesieciu
2014 and combinations of eg.: 20_1, 30m_3m, etc...
2017 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)
2019 char *zenski_cyfry[] = {"0", "1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
2021 char *zenski_cyfry2[] = {"0", "1", "2z", "3", "4", "5", "6", "7", "8", "9"};
2023 char *meski_cyfry[] = {"0", "1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
2025 char *meski_cyfry2[] = {"0", "1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
2027 char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
2029 char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
2031 char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
2033 char *nijaki_cyfry[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2035 char *nijaki_cyfry2[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2037 char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
2039 char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
2041 char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
2043 char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
2045 /* Initialise variables to allow compilation on Debian-stable, etc */
2048 static odmiana *odmiana_nieosobowa = NULL;
2049 static odmiana *odmiana_meska = NULL;
2050 static odmiana *odmiana_zenska = NULL;
2052 if (odmiana_nieosobowa == NULL) {
2053 odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
2055 odmiana_nieosobowa->separator_dziesiatek = " ";
2057 memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
2058 memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
2059 memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
2060 memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
2061 memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
2062 memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
2065 if (odmiana_zenska == NULL) {
2066 odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
2068 odmiana_zenska->separator_dziesiatek = " ";
2070 memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
2071 memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
2072 memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
2073 memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
2074 memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
2075 memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
2078 if (odmiana_meska == NULL) {
2079 odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
2081 odmiana_meska->separator_dziesiatek = " ";
2083 memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
2084 memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
2085 memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
2086 memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
2087 memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
2088 memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
2092 if (strncasecmp(options, "f", 1) == 0)
2094 else if (strncasecmp(options, "m", 1) == 0)
2097 o = odmiana_nieosobowa;
2099 o = odmiana_nieosobowa;
2101 powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
2105 /* ast_say_number_full_pt: Portuguese syntax
2107 * Extra sounds needed:
2108 * For feminin all sound files ends with F
2109 * 100E for 100+ something
2110 * 1000000S for plural
2113 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)
2117 int mf = 1; /* +1 = male; -1 = female */
2121 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2123 if (options && !strncasecmp(options, "f", 1))
2126 while (!res && num ) {
2128 ast_copy_string(fn, "digits/minus", sizeof(fn));
2129 if ( num > INT_MIN ) {
2134 } else if (num < 20) {
2135 if ((num == 1 || num == 2) && (mf < 0))
2136 snprintf(fn, sizeof(fn), "digits/%dF", num);
2138 snprintf(fn, sizeof(fn), "digits/%d", num);
2140 } else if (num < 100) {
2141 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2145 } else if (num < 1000) {
2147 ast_copy_string(fn, "digits/100", sizeof(fn));
2149 ast_copy_string(fn, "digits/100E", sizeof(fn));
2151 if (mf < 0 && num > 199)
2152 snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
2154 snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
2159 } else if (num < 1000000) {
2161 res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
2165 ast_copy_string(fn, "digits/1000", sizeof(fn));
2166 if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
2169 } else if (num < 1000000000) {
2170 res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
2174 ast_copy_string(fn, "digits/1000000", sizeof(fn));
2176 ast_copy_string(fn, "digits/1000000S", sizeof(fn));
2178 if ((num % 1000000) &&
2180 ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
2181 /* no hundreds and below */
2182 (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
2184 num = num % 1000000;
2186 /* number is too big */
2187 ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
2191 if (!ast_streamfile(chan, fn, language)) {
2192 if ((audiofd > -1) && (ctrlfd > -1))
2193 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2195 res = ast_waitstream(chan, ints);
2197 ast_stopstream(chan);
2199 if (!res && playh) {
2200 res = wait_file(chan, ints, "digits/pt-e", language);
2201 ast_stopstream(chan);
2208 /*! \brief ast_say_number_full_se: Swedish syntax
2213 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)
2218 int cn = 1; /* +1 = commune; -1 = neuter */
2220 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2221 if (options && !strncasecmp(options, "n", 1)) cn = -1;
2223 while (!res && (num || playh)) {
2225 ast_copy_string(fn, "digits/minus", sizeof(fn));
2226 if ( num > INT_MIN ) {
2232 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2234 } else if (num < 20) {
2235 snprintf(fn, sizeof(fn), "digits/%d", num);
2237 } else if (num < 100) {
2238 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2240 } else if (num == 1 && cn == -1) { /* En eller ett? */
2241 ast_copy_string(fn, "digits/1N", sizeof(fn));
2245 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2249 if (num < 1000000) { /* 1,000,000 */
2250 res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
2255 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2257 if (num < 1000000000) { /* 1,000,000,000 */
2258 res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
2263 ast_copy_string(fn, "digits/million", sizeof(fn));
2265 ast_debug(1, "Number '%d' is too big for me\n", num);
2272 if (!ast_streamfile(chan, fn, language)) {
2273 if ((audiofd > -1) && (ctrlfd > -1))
2274 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2276 res = ast_waitstream(chan, ints);
2277 ast_stopstream(chan);
2284 /*! \brief ast_say_number_full_zh: Taiwanese / Chinese syntax */
2285 static int ast_say_number_full_zh(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2291 int last_length = 0;
2295 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2297 while (!res && (num || playh || playt || playz)) {
2299 ast_copy_string(fn, "digits/minus", sizeof(fn));
2300 if ( num > INT_MIN ) {
2306 snprintf(fn, sizeof(fn), "digits/0");
2310 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2313 snprintf(fn, sizeof(fn), "digits/thousand");
2315 } else if (num < 10) {
2316 snprintf(buf, 10, "%d", num);
2317 if (last_length - strlen(buf) > 1 && last_length != 0) {
2318 last_length = strlen(buf);
2322 snprintf(fn, sizeof(fn), "digits/%d", num);
2324 } else if (num < 100) {
2325 snprintf(buf, 10, "%d", num);
2326 if (last_length - strlen(buf) > 1 && last_length != 0) {
2327 last_length = strlen(buf);
2331 last_length = strlen(buf);
2332 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2336 snprintf(buf, 10, "%d", num);
2337 if (last_length - strlen(buf) > 1 && last_length != 0) {
2338 last_length = strlen(buf);
2342 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2344 snprintf(buf, 10, "%d", num);
2345 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2346 last_length = strlen(buf);
2347 num -= ((num / 100) * 100);
2348 } else if (num < 10000){
2349 snprintf(buf, 10, "%d", num);
2350 snprintf(fn, sizeof(fn), "digits/%d", (num / 1000));
2352 snprintf(buf, 10, "%d", num);
2353 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2354 last_length = strlen(buf);
2355 num -= ((num / 1000) * 1000);
2356 } else if (num < 100000000) { /* 100,000,000 */
2357 res = ast_say_number_full_zh(chan, num / 10000, ints, language, audiofd, ctrlfd);
2360 snprintf(buf, 10, "%d", num);
2361 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2362 num -= ((num / 10000) * 10000);
2363 last_length = strlen(buf);
2364 snprintf(fn, sizeof(fn), "digits/wan");
2366 if (num < 1000000000) { /* 1,000,000,000 */
2367 res = ast_say_number_full_zh(chan, num / 100000000, ints, language, audiofd, ctrlfd);
2370 snprintf(buf, 10, "%d", num);
2371 ast_log(LOG_DEBUG, "Number '%d' %d %d\n", num, (int)strlen(buf), last_length);
2372 last_length = strlen(buf);
2373 num -= ((num / 100000000) * 100000000);
2374 snprintf(fn, sizeof(fn), "digits/yi");
2376 ast_debug(1, "Number '%d' is too big for me\n", num);
2382 if (!ast_streamfile(chan, fn, language)) {
2383 if ((audiofd > -1) && (ctrlfd > -1))
2384 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2386 res = ast_waitstream(chan, ints);
2388 ast_stopstream(chan);
2395 * \brief Counting in Urdu, the national language of Pakistan
2398 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)
2405 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2408 while (!res && (num || playh)) {
2410 snprintf(fn, sizeof(fn), "digits/hundred");
2412 } else if (num < 100) {
2413 snprintf(fn, sizeof(fn), "digits/%d", num);
2415 } else if (num < 1000) {
2416 snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
2418 num -= ((num / 100) * 100);
2419 } else if (num < 100000) { /* 1,00,000 */
2420 if ((res = ast_say_number_full_ur(chan, num / 1000, ints, language, options, audiofd, ctrlfd))) {
2424 snprintf(fn, sizeof(fn), "digits/thousand");
2425 } else if (num < 10000000) { /* 1,00,00,000 */
2426 if ((res = ast_say_number_full_ur(chan, num / 100000, ints, language, options, audiofd, ctrlfd))) {
2430 snprintf(fn, sizeof(fn), "digits/lac");
2431 } else if (num < 1000000000) { /* 1,00,00,00,000 */
2432 if ((res = ast_say_number_full_ur(chan, num / 10000000, ints, language, options, audiofd, ctrlfd))) {
2435 num = num % 10000000;
2436 snprintf(fn, sizeof(fn), "digits/crore");
2438 ast_log(LOG_DEBUG, "Number '%d' is too big for me\n", num);
2443 if (!ast_streamfile(chan, fn, language)) {
2444 if ((audiofd > -1) && (ctrlfd > -1)) {
2445 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2447 res = ast_waitstream(chan, ints);
2450 ast_stopstream(chan);
2456 /*! \brief determine last digits for thousands/millions (ru) */
2457 static int get_lastdigits_ru(int num) {
2460 } else if (num < 100) {
2461 return get_lastdigits_ru(num % 10);
2462 } else if (num < 1000) {
2463 return get_lastdigits_ru(num % 100);
2465 return 0; /* number too big */
2469 /*! \brief ast_say_number_full_ru: Russian syntax
2472 n00.gsm (one hundred, two hundred, ...)
2475 thousands-i.gsm (tisyachi)
2476 million-a.gsm (milliona)
2482 where 'n' from 1 to 9
2484 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)
2490 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2492 while (!res && (num)) {
2494 ast_copy_string(fn, "digits/minus", sizeof(fn));
2495 if ( num > INT_MIN ) {
2500 } else if (num < 20) {
2501 if (options && strlen(options) == 1 && num < 3) {
2502 snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
2504 snprintf(fn, sizeof(fn), "digits/%d", num);
2507 } else if (num < 100) {
2508 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
2510 } else if (num < 1000){
2511 snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
2513 } else if (num < 1000000) { /* 1,000,000 */
2514 lastdigits = get_lastdigits_ru(num / 1000);
2516 if (lastdigits < 3) {
2517 res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
2519 res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
2523 if (lastdigits == 1) {
2524 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2525 } else if (lastdigits > 1 && lastdigits < 5) {
2526 ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
2528 ast_copy_string(fn, "digits/thousands", sizeof(fn));
2531 } else if (num < 1000000000) { /* 1,000,000,000 */
2532 lastdigits = get_lastdigits_ru(num / 1000000);
2534 res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
2537 if (lastdigits == 1) {
2538 ast_copy_string(fn, "digits/million", sizeof(fn));
2539 } else if (lastdigits > 1 && lastdigits < 5) {
2540 ast_copy_string(fn, "digits/million-a", sizeof(fn));
2542 ast_copy_string(fn, "digits/millions", sizeof(fn));
2546 ast_debug(1, "Number '%d' is too big for me\n", num);
2550 if (!ast_streamfile(chan, fn, language)) {
2551 if ((audiofd > -1) && (ctrlfd > -1))
2552 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2554 res = ast_waitstream(chan, ints);
2556 ast_stopstream(chan);
2562 /*! \brief Thai syntax */
2563 static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2569 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2571 while(!res && (num || playh)) {
2573 ast_copy_string(fn, "digits/lop", sizeof(fn));
2574 if ( num > INT_MIN ) {
2580 ast_copy_string(fn, "digits/roi", sizeof(fn));
2582 } else if (num < 100) {
2583 if ((num <= 20) || ((num % 10) == 1)) {
2584 snprintf(fn, sizeof(fn), "digits/%d", num);
2587 snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
2590 } else if (num < 1000) {
2591 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2594 } else if (num < 10000) { /* 10,000 */
2595 res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
2599 ast_copy_string(fn, "digits/pan", sizeof(fn));
2600 } else if (num < 100000) { /* 100,000 */
2601 res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
2605 ast_copy_string(fn, "digits/muan", sizeof(fn));
2606 } else if (num < 1000000) { /* 1,000,000 */
2607 res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
2611 ast_copy_string(fn, "digits/san", sizeof(fn));
2613 res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2617 ast_copy_string(fn, "digits/larn", sizeof(fn));
2620 if(!ast_streamfile(chan, fn, language)) {
2621 if ((audiofd > -1) && (ctrlfd > -1))
2622 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2624 res = ast_waitstream(chan, ints);
2626 ast_stopstream(chan);
2632 /*! \brief ast_say_number_full_vi: Vietnamese syntax */
2633 static int ast_say_number_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2643 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2644 while (!res && (num || playh)) {
2646 ast_copy_string(fn, "digits/minus", sizeof(fn));
2647 if ( num > INT_MIN ) {
2653 snprintf(fn, sizeof(fn), "digits/%da", num);
2657 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2660 ast_copy_string(fn, "digits/odd", sizeof(fn));
2662 } else if (playoh) {
2663 ast_copy_string(fn, "digits/0-hundred", sizeof(fn));
2665 } else if (playohz) {
2666 ast_copy_string(fn, "digits/0-hundred-odd", sizeof(fn));
2668 } else if (num < 20) {
2669 snprintf(fn, sizeof(fn), "digits/%d", num);
2671 } else if (num < 100) {
2672 snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
2674 if ((num == 5) || (num == 4) || (num == 1)) playl++;
2677 snprintf(fn, sizeof(fn), "digits/%d", (num/100));
2679 if (num && (num < 10)) {
2686 if (num < 1000000) { /* 1,000,000 */
2687 res = ast_say_number_full_vi(chan, num / 1000, ints, language, audiofd, ctrlfd);
2691 snprintf(fn, sizeof(fn), "digits/thousand");
2692 if (num && (num < 10)) {
2694 } else if (num && (num < 100)){
2702 if (num < 1000000000) { /* 1,000,000,000 */
2703 res = ast_say_number_full_vi(chan, num / 1000000, ints, language, audiofd, ctrlfd);
2707 ast_copy_string(fn, "digits/million", sizeof(fn));
2715 if (!ast_streamfile(chan, fn, language)) {
2716 if ((audiofd > -1) && (ctrlfd > -1))
2717 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2719 res = ast_waitstream(chan, ints);
2721 ast_stopstream(chan);
2727 /*! \brief ast_say_enumeration_full: call language-specific functions
2728 * \note Called from AGI */
2729 static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
2731 if (!strncasecmp(language, "en", 2)) { /* English syntax */
2732 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
2733 } else if (!strncasecmp(language, "da", 2)) { /* Danish syntax */
2734 return ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd);
2735 } else if (!strncasecmp(language, "de", 2)) { /* German syntax */
2736 return ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd);
2737 } else if (!strncasecmp(language, "he", 2)) { /* Hebrew syntax */
2738 return ast_say_enumeration_full_he(chan, num, ints, language, options, audiofd, ctrlfd);
2739 } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */
2740 return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd);
2743 /* Default to english */
2744 return ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd);
2747 /*! \brief ast_say_enumeration_full_en: English syntax
2748 \note This is the default syntax, if no other syntax defined in this file is used */
2749 static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2754 while (!res && num) {
2756 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
2757 if ( num > INT_MIN ) {
2762 } else if (num < 20) {
2763 snprintf(fn, sizeof(fn), "digits/h-%d", num);
2765 } else if (num < 100) {
2766 int tens = num / 10;
2769 snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
2771 snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
2773 } else if (num < 1000) {
2774 int hundreds = num / 100;
2776 if (hundreds > 1 || t == 1) {
2777 res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
2782 ast_copy_string(fn, "digits/hundred", sizeof(fn));
2784 ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
2786 } else if (num < 1000000) {
2787 int thousands = num / 1000;
2789 if (thousands > 1 || t == 1) {
2790 res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
2795 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2797 ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
2800 } else if (num < 1000000000) {
2801 int millions = num / 1000000;
2802 num = num % 1000000;
2804 res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
2808 ast_copy_string(fn, "digits/million", sizeof(fn));
2810 ast_copy_string(fn, "digits/h-million", sizeof(fn));
2812 } else if (num < INT_MAX) {
2813 int billions = num / 1000000000;
2814 num = num % 1000000000;
2816 res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
2820 ast_copy_string(fn, "digits/billion", sizeof(fn));
2822 ast_copy_string(fn, "digits/h-billion", sizeof(fn));
2824 } else if (num == INT_MAX) {
2825 ast_copy_string(fn, "digits/h-last", sizeof(fn));
2828 ast_debug(1, "Number '%d' is too big for me\n", num);
2833 if (!ast_streamfile(chan, fn, language)) {
2834 if ((audiofd > -1) && (ctrlfd > -1)) {
2835 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2837 res = ast_waitstream(chan, ints);
2840 ast_stopstream(chan);
2846 static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
2850 ast_copy_string(fn, "digits/h", sizeof(fn));
2852 if (!ast_streamfile(chan, fn, language)) {
2853 if ((audiofd > -1) && (ctrlfd > -1)) {
2854 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
2856 res = ast_waitstream(chan, ints);
2859 ast_stopstream(chan);
2862 return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd);
2865 /*! \brief ast_say_enumeration_full_da: Danish syntax */
2866 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)
2868 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
2870 char fn[256] = "", fna[256] = "";
2873 if (options && !strncasecmp(options, "f", 1)) {
2875 } else if (options && !strncasecmp(options, "n", 1)) {
2882 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
2884 while (!res && num) {
2886 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
2887 if ( num > INT_MIN ) {
2892 } else if (num < 100 && t) {
2893 ast_copy_string(fn, "digits/and", sizeof(fn));
2895 } else if (num < 20) {
2896 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2898 } else if (num < 100) {
2899 int ones = num % 10;
2901 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
2904 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
2907 } else if (num == 100 && t == 0) {
2908 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
2910 } else if (num < 1000) {
2911 int hundreds = num / 100;
2913 if (hundreds == 1) {
2914 ast_copy_string(fn, "digits/1N", sizeof(fn));
2916 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
2919 ast_copy_string(fna, "digits/hundred", sizeof(fna));
2921 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
2924 } else if (num < 1000000) {
2925 int thousands = num / 1000;
2927 if (thousands == 1) {
2929 ast_copy_string(fn, "digits/1N", sizeof(fn));
2930 ast_copy_string(fna, "digits/thousand", sizeof(fna));
2933 ast_copy_string(fn, "digits/1N", sizeof(fn));
2934 snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
2936 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2940 res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
2945 ast_copy_string(fn, "digits/thousand", sizeof(fn));
2947 snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
2951 } else if (num < 1000000000) {
2952 int millions = num / 1000000;
2953 num = num % 1000000;
2954 if (millions == 1) {
2956 ast_copy_string(fn, "digits/1F", sizeof(fn));
2957 ast_copy_string(fna, "digits/million", sizeof(fna));
2959 ast_copy_string(fn, "digits/1N", sizeof(fn));
2960 snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
2963 res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
2968 ast_copy_string(fn, "digits/millions", sizeof(fn));
2970 snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
2974 } else if (num < INT_MAX) {
2975 int billions = num / 1000000000;
2976 num = num % 1000000000;
2977 if (billions == 1) {
2979 ast_copy_string(fn, "digits/1F", sizeof(fn));
2980 ast_copy_string(fna, "digits/milliard", sizeof(fna));
2982 ast_copy_string(fn, "digits/1N", sizeof(fn));
2983 snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
2986 res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
2990 ast_copy_string(fn, "digits/milliards", sizeof(fna));
2992 snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
2996 } else if (num == INT_MAX) {
2997 snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
3000 ast_debug(1, "Number '%d' is too big for me\n", num);
3005 if (!ast_streamfile(chan, fn, language)) {
3006 if ((audiofd > -1) && (ctrlfd > -1))
3007 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3009 res = ast_waitstream(chan, ints);
3011 ast_stopstream(chan);
3013 if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
3014 if ((audiofd > -1) && (ctrlfd > -1)) {
3015 res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
3017 res = ast_waitstream(chan, ints);
3020 ast_stopstream(chan);
3028 /*! \brief ast_say_enumeration_full_de: German syntax */
3029 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)
3031 /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
3033 char fn[256] = "", fna[256] = "";
3036 if (options && !strncasecmp(options, "f", 1)) {
3038 } else if (options && !strncasecmp(options, "n", 1)) {
3045 return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
3047 while (!res && num) {
3049 ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
3050 if ( num > INT_MIN ) {
3055 } else if (num < 100 && t) {
3056 ast_copy_string(fn, "digits/and", sizeof(fn));
3058 } else if (num < 20) {
3059 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3061 } else if (num < 100) {
3062 int ones = num % 10;
3064 snprintf(fn, sizeof(fn), "digits/%d-and", ones);
3067 snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
3070 } else if (num == 100 && t == 0) {
3071 snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
3073 } else if (num < 1000) {
3074 int hundreds = num / 100;
3076 if (hundreds == 1) {
3077 ast_copy_string(fn, "digits/1N", sizeof(fn));
3079 snprintf(fn, sizeof(fn), "digits/%d", hundreds);
3082 ast_copy_string(fna, "digits/hundred", sizeof(fna));
3084 snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
3087 } else if (num < 1000000) {
3088 int thousands = num / 1000;
3090 if (thousands == 1) {